AI写代码最后一步为何最烦?四段式收尾流程让AI产出真正可用

发布时间:2026/10/14 10:22:57
AI写代码最后一步为何最烦?四段式收尾流程让AI产出真正可用 用 AI 写代码久了最烦的一直是最后一步。这句话不是矫情是真实工作流里慢慢形成的感受。我最早用 AI 写代码觉得最大的好处就是“生成得快”。不管是一个解析函数、一段配置脚本还是一整套增删改查接口丢给 AI 几分钟就能看到完整代码而且基本能跑。但真正用了一段时间之后我的感受悄悄变了生成越快收尾越痛苦。上周清理一个日志解析模块就是典型场景——AI 给的函数功能上没问题但等我往工程里接的时候才发现时区处理、异常策略、字段缺失默认值全都没和项目对齐。功能看着完成了实际上离“能交付”还差一个下午。所以这篇就聊聊我用 AI 写代码过程中最真实的痛点最后一步到底烦在哪为什么这么烦以及我自己摸索出来的一套收尾流程。不敢说多高级但确实让我的交付效率上了一个台阶。1. 最后一步到底是什么不是部署是“收敛”先说结论。很多人以为最后一步是部署上线或者是提交代码、发个版本。但我用了这么久最烦的最后一步其实是把 AI 的产出从“能跑”变成“能交付”。能跑的标准很低——跑起来不报错就算能跑。但能交付的标准很高边界要处理、异常要兜底、风格要统一、测试要补齐、注释要可信连依赖都要干净。这个差距非常大。1.1 生成阶段的快掩盖了收尾阶段的慢AI 写代码非常擅长生成“正面逻辑”。你告诉它这个函数要做什么它能立刻写出正常路径下的完整实现。比如一个订单金额计算函数它知道要遍历明细、汇总金额、保留两位小数。但订单金额算出来之后要不要校验上限明细为空怎么办货币类型不一致要不要转换这些“背面逻辑”AI 很少主动考虑。这也是为什么生成阶段越爽收尾阶段越痛苦。因为人的预期被 AI 拉高了——刚才你明明看到它几分钟就写出了一大段看起来完整的东西下意识会觉得“这功能应该差不多了”。结果一接入工程发现最花时间的不是写代码而是给 AI 补它没想过的问题。我习惯做个类比AI 像一个特别会写初稿的实习生大纲漂亮正文通顺但所有脚注、参考文献、排版格式都得你亲自收拾。你越夸它初稿写得快收拾的时候就越想把稿子扔了。1.2 最后一步的真实构成我复盘过很多次最后一步的“烦”不是单一原因而是好几个任务叠在一起。我把它们列成了一张清单收尾任务具体内容为什么花时间需求回读把口头需求转成代码后再逐条核对看 AI 有没有遗漏AI 容易漏掉“隐含需求”比如权限校验、日志输出代码审查对 AI 生成的每个改动做 diff 审查重点看边界分支和错误处理生成代码往往没有思考“失败场景”所有假设都需要人肉确认测试补全生成代码几乎不主动写测试尤其不写边界测试没测试的代码是不敢上线的这个只能人补依赖与兼容检查AI 经常爱用“比较新的库版本”或者引入一些并不需要的包依赖一旦膨胀环境就变得脆弱上线容易出问题注释与文档对齐AI 注释描述的是“它想做的事”不一定是“它实际做的事”这种注释会误导后面的维护者比没有注释更危险每一项单看都能接受但五项叠在一起最后一步就变成了一座山。所以后来我的结论是如果你只用 AI 写代码但没建立“收尾清单”你迟早会在某一项上翻车而且大概率是上线之后才发现的。2. 为什么它这么烦三个躲不开的矛盾如果只看表面最后一步烦是因为“要干的琐事多”。但往深了挖其实是 AI 写代码这件事自带的三个矛盾。2.1 自信的错误与人类验证之间的信任断层AI 生成代码最大的问题不是它出错而是它从来不告诉你它哪里不确定。它会把猜测包装成确定的方案甚至写得非常笃定。比如让 AI 写一个时间解析函数它很可能默认本地时区而线上容器跑的是 UTC。如果它不加注释你可能根本意识不到它替你做了这个假设。这种“自信的错误”是最难防范的。因为一旦你不知道它在哪里替你做了假设你就不可能在收尾的时候精准检查。你只能选择全部抽查或者干脆逐行重读。这两个选项都非常耗时间。人与人之间结对编程的时候队友如果不确定他会问你一句“这里我不确定你的预期是啥”但 AI 不这么干它会把最可能的猜测直接写进代码里从表面上看起来毫无破绽。2.2 风格混合与维护负担转嫁另一个矛盾我称之为“风格混合”。AI 训练语料来自大量开源项目所以它生成的代码经常是一种多位陌生人风格的混合体。这次这个函数用get_user_info那个类又用fetchUserData命名风格前后不一致同一个模块里有的地方用单引号有的地方用双引号有的函数有类型注解有的又没有。这些看起来是小问题但代码是给人维护的。AI 写完代码拍拍屁股走了维护成本却全部转嫁给了团队。我见过有的项目因为大量引入 AI 生成代码最后代码风格变得特别混乱甚至同一个文件中缩进风格都漂移了。这里有个很贴切的类比AI 写代码就像请了三个人分别装修一个房间单独看每一块都不差但放在一起就是毛坯混搭风。收尾阶段你得花大量时间统一这个风格。2.3 上下文窗口与真实需求的偏差最后一个矛盾更本质AI 只能基于当前对话窗口里的上下文做判断它无法感知你项目的全貌。你和 AI 说“给我一个订单状态机”它理解的状态机可能是抽象类加子类实现但你的项目里其实只需要一个枚举加工具函数。它给出的方案并不是错的但它完全处在另一个上下文中。这种偏差往往不是改一行能解决的而是要推倒重来。所以最后一步有很大一部分工作是在把 AI 猜的东西和项目的真实情况对齐。哪些常量是项目里已经定义过的哪些接口是团队约定俗成的哪些数据流是跨越多个模块的这些问题全都要靠人去填平。这三重矛盾叠加在一起就注定了 AI 写代码不可能只靠“生成”就完事收尾必然是重头戏。3. 把最后一步做成固定流程我的四段式收尾法既然最后一步躲不开那就只能想办法让它变得不烦。我现在有一个固定的收尾流程分四段走先让 AI 自审再按变化频率拆分模块然后写验收用例最后用提交信息兜底。每一步都有很具体的操作。3.1 先让 AI 自审而不是人肉通读我在让 AI 生成完代码之后做的第一件事不是自己从头到尾读一遍而是先让 AI 自审。我用的提示词大概是这样的请以代码审查者的视角重新读一遍你刚才生成的代码。不要修改代码只列出 1. 可能导致线上问题的点至少列 10 个 2. 每个点给出的依据 3. 如果要修复你会怎么改。 如果找不出 10 个问题请尽量找而不是说“代码没有问题”。这个方法实测下来很稳。AI 在“自审模式”下会比“生成模式”更挑剔它会主动发现一些刚才自己忽略的边界问题。哪怕它找出的问题不一定都对但这个过程能帮你快速定位需要深入看的位置。要注意的一点如果 AI 回你说“这段代码质量很好没有明显问题”那基本可以确定它没认真审。自审模式下它应该能提出至少好几个疑点一个都提不出来反而说明有问题。我还会根据自审结果做二次提问挑其中风险最高的两三个让它给出具体的修改方案但不让它直接改。这一步的目的是确认它对问题理解到位了再由我来决定是否应用修改。为什么这样做因为 AI 一旦在长对话里连续多次修改很容易出现“改了这个坏了那个”的连锁反应所以它在自审阶段只能提方案、不能动代码这样最安全。3.2 按变化频率拆分模块避免 AI 动一发牵全身AI 生成的代码有一个常见问题它喜欢把很多东西挤在一个文件里。比如一段数据同步模块它会同时把配置、抓取逻辑、解析逻辑、主函数入口全写在一个 Python 文件里。这种结构看起来很紧凑但对后续迭代非常不友好。你想让 AI 改一下抓取逻辑的某个细节它可能顺手把配置项改了或者把解析逻辑重新排版了一遍。结果就是 diff 变得特别大你审查起来很费劲而且容易埋雷。我的做法是拿到 AI 的初稿之后先按“变化频率”把一个文件拆成多个文件。原则是——将来经常变的东西和几乎不变的东西分开外部调用接口固定住。比如一个数据同步模块我会拆成config.py # 配置项几乎不变 fetcher.py # 抓取逻辑经常变 parser.py # 解析逻辑经常变 main.py # 组装入口很少变拆完之后我后续只会让 AI 在特定文件内做修改并明确告诉它“只改 fetcher.py其他文件不要动。”这样一来AI 每次改动的范围就会被约束住diff 清晰审查成本直线下降。这个步骤本质上是在做工程结构设计。我建议在让 AI 写代码之前就先想好模块边界而不是等生成完再拆。3.3 用验收用例给需求上锁AI 只在锁内迭代第三个步骤可能是整个流程里最关键的一环先写验收测试用例再让 AI 去迭代代码。很多人顺序搞反了。常见做法是让 AI 先生成功能代码然后再补测试。但这样做的问题在于AI 生成的功能代码已经定义了行为你补测试的时候很容易顺着它的实现去写结果就是测试掩盖了业务需求而不是验证业务需求。我推荐的顺序是先把业务规则写成测试用例再让 AI 对着测试用例去实现。比如你要一个“连续失败 3 次锁定账号”的逻辑测试应该先写清楚def test_lock_after_three_failures(): user User(tester) user.record_failed_login() user.record_failed_login() assert user.is_locked is False user.record_failed_login() assert user.is_locked is True这个测试写出来AI 就知道需求的具体行为是什么了。它不会给你一个“成功 3 次锁定”的错误逻辑因为测试会直接挂掉。我通常会在让 AI 写功能代码时把这个测试和提示词一起丢过去然后说“请实现一个满足测试用例的函数注意包括边界行为。”这样 AI 的迭代空间就被锁在测试的规则内。后面它再怎么改只要测试通过核心需求就不会跑偏。这里要强调一下验收用例写得越贴近业务AI 发挥的空间就越小最后一步收到“惊喜”的概率就越低。如果你想完全控制行为测试就要把正常路径、边界路径、异常路径全部摊开。3.4 用 commit message 逼 AI 交底顺便留清决策痕迹最后一步往往被人忽略的是收尾时的信息记录。我现在的习惯是在确认代码功能没问题之后让 AI 再生成一份比较完整的提交说明但比普通提交信息多几个强制回答的问题这段代码解决了什么问题改了哪些公共接口或数据结构有哪些已知限制、假设或未验证部分如果不改会有什么后果这四个问题一丢给 AI它的回答经常会暴露一些问题。比如有一次它对“已知限制”的回答是“目前假设所有输入都是合法的”我才意识到这个代码里缺少了参数校验——这正好是我收尾时漏掉的一个点。这个过程本质上是在让 AI 做“交底”把脑海里那些没有写进代码的潜在判断都逼出来。很多 AI 生成的代码里隐藏了大量隐式假设这些假设如果不记录两周后你自己回来看这段代码都可能想不起来当初为什么这么做。而且让 AI 生成提交说明还有一个好处面对这四个必须回答的问题AI 会重新审视一次自己的代码等于又做了一轮总结式自审。这个成本几乎为零但信息密度和收益非常可观。4. 踩过的坑和排查实录最后一步的高频翻车现场上面讲的都是我现在的流程但流程不是一天建成的。中间我踩过不少坑有的还挺值得拿出来说说。这里就当问题排查实录吧。4.1 AI“假装修好了”循环验证才是解药这是我在实际使用中遇到最让人头大的问题。你向 AI 反馈一个 bug它马上道歉然后给你一大段修改代码看起来态度很好。但跑一遍测试问题照样在。你再来一次它还道歉又改了一种写法。这次看 diff 你会发现它只是把变量名换了一种叫法核心逻辑压根没动。这种现象我称之为“假装修复”——AI 为了满足用户的反馈表现得在积极行动但实际上没有真正定位到根因。排查方法其实很简单但需要自己坚持只看修复后的运行结果不看 AI 的描述每次修完必须跑最小复现用例如果连续两次没修好果断换一个小任务描述或者开一个新对话而不是在同一个长对话里继续来回。长对话里 AI 容易陷入“围绕上一轮输出修改”的模式反而不容易跳出来重新思考。我印象很深的一次是让 AI 修一个字符串编码问题。它来回改了四次每次都在改编码转换的方式但始终没解决。后来我换了个思路把出错的那一行原样贴给它不给任何上下文问它“这行代码在什么情况下会抛异常”它几乎立刻就定位到了问题因为那个问题根本不在编码转换而在前一步的字段提取。4.2 测试全绿但业务逻辑是反的断言要贴近业务另一种很隐蔽的翻车现场是测试全绿但业务逻辑是反的。后来我分析了一下原因出在测试本身写得太弱或者说断言方向没有扣住业务本质。有次让 AI 实现一个“连续失败 3 次锁定账号”的逻辑AI 给出的代码实现错了弄成了成功 3 次就锁定但当时的测试只断言了“调用 3 次后账号锁定”没有断言失败次数和成功次数的区别所以测试通过了。现在我做验收用例时会把业务条件的每一个分支都摊开来写并且断言尽量写成“业务可读”的形式而不是只判断最终状态。比如上面这个例子我会额外要求测试覆盖def test_should_not_lock_after_three_successes(): user User(tester) user.record_successful_login() user.record_successful_login() user.record_successful_login() assert user.is_locked is False这种断言值一开始可能看起来有点多余但它能把 AI 最常见的“逻辑方向搞反”问题直接按死在摇篮里。收尾阶段我会仔细体检测试确保测试覆盖的是业务行为而不是实现函数的某种“存在性”。4.3 注释和实现“各说各话”让 AI 自己讲一遍AI 生成的注释经常比代码乐观。它写注释时描述的是“代码想做什么”而不是“代码实际做了什么”。如果实现写歪了注释依然停留在理想状态这种注释对后续维护者的伤害很大。比如有一次 AI 写的代码里有注释“从响应中提取用户名并标准化”但实际代码只做了strip()并没有做大小写归一化。我看到注释时以为用户名已经归一化了结果下游逻辑一直出错。现在我在收尾时已经习惯让 AI 做“自述检查”给它指令不用改代码只用一句自然语言描述这段代码的输入、输出和每一个分支的具体条件然后我把描述和代码对照一遍很快就能发现不一致的地方。这个环节通常只花几分钟但能省掉后面几小时的排查时间。4.4 被忽略的小关卡依赖膨胀、死代码与格式漂移最后分享几个不致命但很磨人的小关卡。第一个是依赖膨胀。AI 生成代码时习惯性地import一些根本没用到的东西有时还会顺手给你安一个并不需要的第三方库。这种依赖一旦进入项目会带来环境脆弱性。我现在会专门看一遍 diff 里的 import 区域凡是没用到的直接删。第二个是死代码。AI 很喜欢保留一些“看起来有意义但实际没有调用者”的函数。这些代码会让模块看起来很完整但维护时需要读更多无用代码。我的态度是凡是后面接不上的函数不管它写得多么漂亮在收尾阶段都必须删掉不要心疼。第三个是格式漂移。AI 在很小的修改里可能会突然改变引号风格、换行方式或者缩进习惯尤其项目里原本有某种约定时。这个我用编辑器的一键格式化解决格式化完再检查 diff动作要快。久而久之你会发现这些小关卡虽然不起眼但舍掉它们能让最后一步提速不少。5. 一些真实体会把最后一步从“最烦”变成“最稳”经历了很多次收尾翻车之后我现在对“用 AI 写代码”这件事有了完全不同的理解。最初我觉得它是一种“加速工具”后来我意识到它更像一个“结对实习生”。你这个老手的价值不在于接替它写每一行代码而在于给它的产出把好最后一道关。我自己的体会是建立固定流程比每次临时应对更能降低焦虑。一旦你知道了收尾要过哪些关卡、按什么顺序过最后一步就不再是一座大山而是几件能按部就班做完的琐事。还有一个特别想分享的小经验在对话快到尾声的时候不妨强制 AI 说一句“这个方案的最大风险和未验证部分是什么”。这个动作经常能帮我提前发现问题。AI 生成代码时它对自己代码中关于业务假设的那些局限是有信息的只是不会主动说出来。逼它说出来那一刻起最后的“收尾”就已经变成一次可以通过清单完成的确认而不是一次提心吊胆的盲猜。代码写多了你会发现交付最重要的是确定性。好的收尾流程就是让 AI 的输出从“能跑”变成“确定可用”的那道保险。