ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI编码时代:从泥球制造到工程化协作的思维转变

AI编码时代:从泥球制造到工程化协作的思维转变 1. 项目概述从“泥球”现象到工程学解构最近在开发者社区里一个词被反复提起——“泥球制造机”。这不是什么新的硬件设备而是不少资深工程师对当前某些AI编码助手的戏谑称呼。这个比喻源自软件工程中的“泥球架构”Big Ball of Mud指的是一种杂乱无章、结构混乱、难以维护的代码结构。而“制造机”则精准地描述了当开发者过度依赖或不当使用AI助手时代码质量可能失控、快速腐化的过程。这个概念的流行与开发者Matt Pocock的一系列分享密不可分。Matt并非在单纯地吐槽AI工具不好用而是以一个拥有丰富一线经验的工程师视角系统性地解构了当我们把AI助手纳入工作流时究竟需要什么样的“技能集”Skill Set来驾驭它而不是被它带偏。这本质上是一个工程学问题我们如何将一种强大的、但可能产生“熵增”的自动化工具整合进一个追求清晰、可维护、可演进的软件工程体系中简单来说这个“项目”探讨的核心是在AI编码时代工程师的核心竞争力正在从“写代码”转向“定义问题、评审代码和设计结构”。AI助手可以成为一个强大的“副驾驶”但它无法替代驾驶员对目的地、路线和驾驶规范的理解。如果你发现自己项目里的代码越来越像一坨纠缠不清的意大利面函数职责模糊、依赖混乱、测试困难那么很可能你的AI助手正在扮演“泥球制造机”的角色而你需要的是Matt Pocock所强调的那套工程学技能集来重新掌控局面。2. “泥球制造机”现象、成因与典型症状要解决问题首先得准确诊断。AI助手是如何一步步沦为“泥球制造机”的这背后不是工具的错而是使用模式与工程素养缺失共同作用的结果。2.1 “泥球”代码的三大典型症状当AI生成的代码开始显现“泥球”特质时通常会有以下几个鲜明特征高耦合与低内聚的“功能块”AI倾向于根据一个具体的、局部的提示生成一个“完成功能”的代码块。例如你提示“写一个函数处理用户表单提交验证邮箱并保存到数据库”。AI可能会给你一个长达80行的函数里面混杂了输入清洗、正则验证、数据库连接、SQL拼接、错误处理等所有逻辑。从功能上看它“能用”但从结构上看它违反了单一职责原则各种关注点纠缠在一起成为一个典型的“泥球”核心。未来当你需要修改验证逻辑或更换数据库驱动时会发现牵一发而动全身。幻觉驱动的“依赖蔓延”AI在生成代码时可能会引入一些它“认为”需要但实际并未明确声明或项目中并不存在的依赖、工具函数或特定API的用法。我曾见过一个案例AI生成的一段Node.js代码使用了一个叫awesome-validation的第三方库来进行数据校验但这个库根本不存在是AI“幻想”出来的。更常见的是它使用了某个库较新版本才有的API而你的package.json里锁定的却是旧版本。这种“依赖幻觉”会导致项目在他人环境或构建流程中突然失败且错误信息往往令人费解。缺乏上下文感知的“模式复制”AI学习了海量的开源代码其中不乏各种设计模式和最佳实践。但问题在于它可能在不合时宜的地方机械地套用这些模式。例如在一个简单的内部工具脚本中AI可能会生成一个完整的、带有抽象工厂和复杂依赖注入的类层次结构严重过度设计。反之在一个大型核心业务模块中它又可能生成一堆全局函数和松散的过程式代码导致架构失控。它缺乏对项目当前阶段、具体领域和团队约定的“上下文”理解。2.2 成为“制造机”的四大诱因理解症状后我们再看诱因这能帮助我们从源头上规避。模糊的、结果导向的提示Prompt这是最主要的根源。当你对AI说“给我一个登录功能”你得到的就是一个“泥球”的完美种子。这个提示只描述了“是什么”登录完全没有定义“怎么做”架构、边界、接口和“遵循什么”规范、模式、约束。AI只能从它的训练数据中抓取一个最常见的、能实现登录的代码片段扔给你其质量完全取决于训练数据的运气。放弃所有权与评审责任将AI生成的代码不加审阅地直接提交等同于将代码质量的责任完全外包给一个统计学模型。AI没有“责任”概念它不会为后续的维护成本、性能瓶颈或安全漏洞负责。工程师一旦产生了“这是AI写的所以有问题也不是我的问题”的心态“泥球”的滋生就失去了最后的防火墙。对生成代码的“黑盒”信任尤其是对于不熟悉的语言或框架开发者可能因为自身知识的局限无法深入理解AI生成的代码。代码“看起来”能跑就认为它是正确的、最优的。这种信任掩盖了可能存在的算法低效、边界条件缺失、资源未释放等深层次问题。迭代过程中的“补丁叠加”当初始的AI生成代码存在缺陷时开发者倾向于用新的AI提示去“打补丁”。比如发现缺少错误处理就提示“为上面的函数添加错误处理”。AI可能会在外层包裹一个try-catch但异常处理逻辑可能与原有业务逻辑风格不统一或者捕获了过于宽泛的异常。经过多次这样的“迭代”原始代码块会像滚雪球一样膨胀和扭曲内部结构愈发混乱。注意识别“泥球”不能只看代码行数。一个精心设计的、符合领域模型的200行模块可能比一个50行但职责混乱的“泥球”函数要清晰和可维护得多。关键评判标准是变更成本当需求变化时修改这些代码需要动多少地方是否容易引入回归缺陷3. Matt Pocock技能集解构从“程序员”到“工程师”的思维转变Matt Pocock的观点之所以引起共鸣是因为他清晰地指出了对抗“泥球制造机”所需的不是更聪明的AI而是更成熟的工程思维。这套技能集可以解构为以下几个核心维度。3.1 精准的问题定义与需求拆解能力这是驾驭AI的起点也是最重要的技能。你不能问一个模糊的问题却期望得到一个精确的答案。从“要什么”到“怎么要”不要问“实现一个购物车”而要拆解为数据结构“购物车”是一个包含userId、items商品ID和数量列表、createdAt、updatedAt的对象。items中的每个商品需要关联库存和价格这些信息是否实时从商品服务获取还是下单时快照。核心操作需要addItem(itemId, quantity)、removeItem(itemId)、updateQuantity(itemId, quantity)、clearCart()、getCartSummary()等方法。边界与约束购物车数据存储在哪里用户会话、Redis、数据库是否需要考虑并发修改乐观锁。商品数量是否有上限提供上下文约束在提示中明确技术栈、项目规范、已有的工具函数或类。例如“在我的React项目中使用TypeScript状态管理已采用Zustand。请生成一个useCart的hook它调用我们现有的apiClient.post(‘/cart’)接口。遵循我们团队的命名规范函数用驼峰式类型用大驼峰式。”设定验收条件甚至可以提前定义测试用例的思路。“生成的函数应该能处理添加已存在的商品数量合并、添加数量为0的商品应移除、尝试添加不存在的商品ID应抛出特定错误。”实操心得我习惯在向AI提问前先用注释或Markdown在本地文件里写下我对这个模块的“设计草稿”包括输入输出、关键状态、错误类型。这个过程本身就是在澄清思路。然后将这个草稿作为提示的一部分交给AI让它充当“实现者”而非“设计者”。3.2 架构与设计模式的识别与应用能力AI可以生成实现了某个模式的代码但它无法替你决定“这里该用什么模式”。这项技能要求你识别代码异味能一眼看出AI生成的代码中是否存在过长的函数、过大的类、重复的代码块、不恰当的全局变量等“异味”。这是阻止“泥球”形成的第一道感官防线。选择恰当的模式根据具体场景知道何时引入分层如Repository模式隔离数据访问、何时使用策略模式替换复杂的条件判断、何时用工厂方法简化对象创建。例如对于支付处理AI可能会生成一长串if (gateway ‘paypal’) { … } else if (gateway ‘stripe’) { … }。一个有经验的工程师会提示AI“请使用策略模式重构以下支付逻辑定义一个PaymentStrategy接口并为PayPal和Stripe提供具体实现。支付上下文类PaymentContext应支持运行时切换策略。”设计契约与接口优先让AI生成接口Interface或抽象类Abstract Class而不是具体实现。先定义好模块之间的“契约”API合同再分别实现。这能强制你思考模块的职责和边界从源头降低耦合。3.3 严格的代码评审与重构技能将AI视为一个初级开发者它提交的每一行代码都需要经过你的严格评审Code Review。评审清单化针对AI代码建立专门的评审清单功能正确性逻辑是否符合需求边界条件空值、极值、错误输入是否处理性能影响有无不必要的循环嵌套数据库查询是否N1算法复杂度是否合理安全性有无SQL注入、XSS、路径遍历风险敏感信息是否硬编码可维护性命名是否清晰函数是否足够小、职责单一注释是否解释了“为什么”而非“是什么”一致性是否符合项目编码规范缩进、分号、引号是否使用了项目约定的工具库和模式重构而非重写对于问题代码不要直接丢弃让AI重写。而是将其作为“原材料”运用重构手法提取函数、重命名变量、拆分类、引入参数对象等进行改进。这个过程极具学习价值能加深你对代码结构和AI思维局限的理解。利用AI进行评审一个高阶技巧是将AI生成的代码和你的需求描述一起提交给另一个AI会话或另一个以代码评审见长的模型询问“请从代码质量、安全性和潜在bug的角度评审这段代码。”你可能会得到一些你忽略的视角。3.4 测试驱动与验证思维让AI在“约束”下工作能极大提高产出代码的质量。测试驱动开发TDD思维是强大的约束。提示即测试在让AI生成实现代码前先让它生成单元测试。例如“为一个名为validateEmail的函数编写Jest单元测试要求覆盖有效邮箱、无效格式、空值、超长字符串等情况。” 然后再基于这些测试用例去生成函数实现“现在请实现能通过上述所有测试的validateEmail函数。”生成测试用例即使不严格遵循TDD也可以在AI生成代码后立即提示“为上面生成的calculateDiscount函数编写一组全面的单元测试包括正常场景、边界场景如折扣率为0、为100和异常场景如输入为负数。”验证非功能需求对于涉及性能、并发的代码可以要求AI给出分析或简单的基准测试代码。“请分析上面生成的排序函数的时间复杂度并提供一个用console.time测量其处理1万个随机整数性能的示例。”常见问题AI生成的测试有时会出现“实现验证”而非“行为验证”的问题即测试过于依赖当前的具体实现一旦重构就容易失败。评审时需注意测试是否在验证正确的抽象层级输入输出而非内部中间状态。4. 实操将AI助手整合进高效工程工作流理论需要落地。下面我以一个具体的场景——“为一个电商后端添加优惠券核销功能”——来演示如何运用上述技能集与AI协作避免制造“泥球”。4.1 第一阶段需求分析与设计草稿人类主导首先完全抛开AI进行独立思考和分析。明确业务规则优惠券有类型百分比折扣如8折、固定金额减免如减50元。优惠券有使用门槛最低消费金额、适用商品品类、有效期。优惠券状态未使用、已使用、已过期、已作废。核销时需检查状态是否有效、是否在有效期、订单是否满足门槛、计算折后金额。核销后更新优惠券状态为已使用并记录关联订单号。设计草稿写在代码注释或文档里// 领域模型设计 interface Coupon { id: string; code: string; type: ‘percentage’ | ‘fixed’; value: number; // percentage: 80 表示8折 fixed: 5000 表示50元以分为单位 minAmount?: number; // 最低消费分 applicableCategoryIds?: string[]; startTime: Date; endTime: Date; status: ‘active’ | ‘used’ | ‘expired’ | ‘invalid’; userId: string; } interface Order { id: string; userId: string; totalAmount: number; // 订单总金额分 items: Array{productId: string, categoryId: string, price: number}; couponCode?: string; finalAmount: number; } // 核心服务接口 interface ICouponService { validateAndApply(couponCode: string, order: Order): Promise{ isValid: boolean; discountedAmount: number; message?: string }; markAsUsed(couponId: string, orderId: string): Promisevoid; } // 关键验证逻辑点 // 1. 查询优惠券按code和userId // 2. 状态校验 (active) // 3. 有效期校验 (now between startTime and endTime) // 4. 门槛校验 (order.totalAmount minAmount; order.items 包含 applicableCategory) // 5. 金额计算 (if type‘percentage’: final total * (value/100); if ‘fixed’: final total - value; final不能0) // 6. 更新状态事务内完成4.2 第二阶段分步骤、有约束的AI协作现在带着清晰的设计我们分步向AI例如Claude/ChatGPT发起请求。提示1生成领域模型和TypeScript类型“基于以下业务描述为TypeScript项目生成完整的领域模型接口和枚举。描述[此处粘贴上面的设计草稿中的业务规则部分]。请使用interface和type并添加详细的JSDoc注释。”提示2生成核心验证服务骨架“现在基于上面生成的Coupon和Order接口创建一个CouponService类实现ICouponService接口。请先只写方法签名和详细的JSDoc注释描述每个方法的职责、参数、返回值和可能抛出的错误。暂时不写具体实现。假设我们使用Prisma作为ORM依赖注入到服务中。”提示3实现具体的验证逻辑“接下来请实现CouponService中validateAndApply方法的具体逻辑。请严格遵循以下步骤和规则使用注入的prisma客户端根据couponCode和order.userId查询优惠券。如果未找到返回{ isValid: false, message: ‘优惠券不存在’ }。检查coupon.status是否为‘active’否则返回无效。检查当前时间是否在coupon.startTime和coupon.endTime之间。如果coupon.minAmount存在检查order.totalAmount coupon.minAmount。如果coupon.applicableCategoryIds存在且不为空检查订单中是否至少有一件商品的categoryId包含在该数组中。计算折后金额。注意coupon.value百分比类型是整数80代表80%固定金额单位是分。最终金额不能低于0。如果所有验证通过返回{ isValid: true, discountedAmount: [计算后的金额] }。 请使用清晰的变量名并为每一步的失败返回明确的message。”提示4生成单元测试“为上面实现的validateAndApply方法编写Jest单元测试。要求使用jest.mock模拟Prisma客户端。覆盖所有验证分支优惠券不存在、状态非active、过期、未开始、金额不足、品类不符、计算正确百分比和固定金额。包括一个‘happy path’完整通过的测试用例。测试中要验证返回的对象结构和消息。”4.3 第三阶段人工评审、重构与集成拿到AI生成的代码后进入核心的“工程师”工作环节逐行评审对照你的设计草稿和业务规则检查AI的实现是否有偏差。特别注意数据一致性金额计算时是否所有单位都是“分”时间比较是否考虑了时区错误处理返回的错误信息是否对用户友好是否泄露了内部细节如SQL错误性能查询优惠券时userId和code是否有复合索引AI可能不会考虑这点你需要补充。代码风格是否符合项目的Prettier/ESLint配置关键重构你可能会发现AI生成的验证逻辑全部堆在一个大的validateAndApply方法里。这时运用你的设计能力进行重构// 重构后将不同的验证职责拆分为私有方法 private validateStatus(coupon: Coupon): ValidationResult { ... } private validateValidityPeriod(coupon: Coupon): ValidationResult { ... } private validateOrderThreshold(coupon: Coupon, order: Order): ValidationResult { ... } private calculateDiscount(coupon: Coupon, order: Order): number { ... } // 主方法变得清晰像是一个协调者 async validateAndApply(couponCode: string, order: Order): PromiseApplyResult { const coupon await this.getCoupon(couponCode, order.userId); if (!coupon) return { isValid: false, message: ‘Invalid coupon’ }; const validations [ this.validateStatus(coupon), this.validateValidityPeriod(coupon), this.validateOrderThreshold(coupon, order), ]; const failedValidation validations.find(v !v.isValid); if (failedValidation) return { isValid: false, message: failedValidation.message }; const discountedAmount this.calculateDiscount(coupon, order); return { isValid: true, discountedAmount }; }然后你可以让AI帮你完成这些拆分出来的私有方法的实现提示非常明确。运行与调试运行AI生成的单元测试看是否能通过。通常第一次不会完全通过你需要根据测试失败信息分析是AI的实现有误还是测试用例的预期不对。这是一个绝佳的调试和学习过程。集成与文档将评审、重构后的代码集成到项目中并确保更新相关的API文档或Swagger定义。你可以让AI根据最终代码生成API接口文档的片段。通过这个流程AI扮演了“高级代码生成员”和“初级测试编写员”的角色而你将核心精力放在了架构设计、边界定义、逻辑拆解和最终质量把关这些高价值活动上。最终产出的代码其设计意图清晰、结构良好、测试覆盖充分与“泥球”截然不同。5. 进阶技巧与避坑指南在与AI协作的日常中积累一些特定技巧能极大提升效率和质量。5.1 提示工程从“聊天”到“编程”提供范例Few-Shot Learning这是最强大的技巧之一。不要只描述规则直接给AI一个或几个你期望的输入输出示例。低效提示“写一个函数解析查询字符串。”高效提示“写一个函数parseQueryString输入是一个URL查询字符串如‘nameJohnage30cityNewYork’输出是一个对象。例如输入‘foo1barhello’应该返回{ foo: ‘1’, bar: ‘hello’ }。注意值需要做URL解码号应被解码为空格。请用TypeScript实现。”指定思维链Chain-of-Thought对于复杂逻辑要求AI“逐步思考”。“请逐步推理并实现给定一个整数数组nums和一个目标值target找出数组中所有和为目标值且不重复的三元组。请你先列出解题思路例如排序、双指针然后再给出代码实现并加上时间复杂度和空间复杂度分析。”设定角色和约束“你是一个经验丰富的TypeScript后端工程师严格遵守Clean Code原则。请用面向对象的方式设计一个简单的任务队列系统包含Task、Queue和Worker类。要求队列支持优先级Worker是异步的。”5.2 工具链集成让AI在上下文中工作利用IDE插件Cursor、GitHub Copilot Chat等工具能直接读取你项目中的其他文件提供上下文感知的代码补全和建议。在提问时可以引用项目中的现有类“参考src/services/UserService.ts的风格”。结合代码分析工具将AI生成的代码立即用ESLint、Prettier、SonarQube等工具跑一遍。这些工具能快速发现风格不一致、潜在bug和安全漏洞弥补AI在静态分析上的不足。版本控制策略可以考虑为AI生成的大块代码创建一个单独的分支如feat/ai-coupon-service在这个分支上进行充分的评审、重构和测试确认无误后再合并到主开发分支。在提交信息中可以简要说明AI的贡献和你的修改如“feat: add coupon service (AI-assisted, refactored for clarity)”。5.3 常见“坑”与应对策略问题场景表现根本原因应对策略“幻觉”库或API代码使用了不存在的包或错误版本的API。AI训练数据滞后或混杂了非官方信息。1. 要求AI注明它使用的库和版本。2. 生成代码后立即检查package.json或官方文档确认。3. 提示时指定版本“使用Express.js4.18.2的语法”。过度设计简单任务用了复杂的设计模式引入不必要的抽象。AI从优秀但复杂的开源项目中学到了模式但缺乏对简单性的判断。明确说明项目阶段和复杂度“这是一个一次性脚本请用最简单直接的方式实现无需考虑扩展性。”安全漏洞代码包含硬编码的密钥、未经验证的用户输入直接拼接SQL等。训练数据中包含大量不安全的示例代码。在提示中强调安全“请确保没有安全漏洞用户输入必须经过验证和转义密钥必须从环境变量读取。”性能陷阱在循环内进行数据库查询N1问题使用低效算法。AI以实现功能为首要目标缺乏性能优化意识。在需求中增加性能约束“请确保函数的时间复杂度在O(n log n)以下并避免N1查询问题。”文化/规范不匹配命名风格、目录结构、测试框架与团队规范不符。AI学习的是公共代码库的平均风格。提供你项目的具体规范文件或示例。“请遵循我们项目的Airbnb JavaScript代码规范使用Jest进行测试测试文件放在__tests__目录下。”最后的个人体会AI编码助手不是“泥球”的根源对工程实践的懈怠才是。它像一面镜子放大了使用者自身在软件设计、代码评审和系统思维上的强弱。将AI融入工作流不是一个关于“是否”的选择而是一个关于“如何”的工程问题。它要求我们从代码的“打字员”升级为系统的“建筑师”和质量的“守门员”。这个过程起初可能会更慢因为你需要花更多时间在设计和评审上但长期来看它带来的代码清晰度、可维护性和开发信心的提升是单纯追求编码速度无法比拟的。真正的效率来自于写更少的、但更好的代码。
返回列表