用 AI 写代码的时间越长,越发现一个反直觉的事实:真正卡住效率的,往往不是 AI 生成代码那几分钟,而是生成完之后那串没人愿意干的收尾活。这个“最后一步”听起来像一次点击,实际是一连串的验证、修正和兜底:依赖能不能装、测试能不能过、风格符不符合规范、提交信息要不要重写、部署上去会不会崩。用 AI 写代码久了,最烦的一直是最后一步,因为前面的速度越快,后面这些琐碎就被放大得越刺眼。这篇文章我从实际踩过的场景出发,把“最后一步”拆开讲清楚,再给出一套我现在一直在用的收尾流程。不管你是刚用 AI 辅助写代码,还是已经被最后一步折磨了一阵子,应该都能找到对症的办法。
1. 先说说“最后一步”到底是什么:一堆没人愿意干的收尾活
在没有 AI 写代码之前,我把“写完代码”理解成敲完最后一个函数。现在我的理解完全变了:AI 可以几秒钟交出可读性不错的代码,但离“可以用”还差得很远。它缺的不是想象力,而是项目真实环境里的兼容性。
所谓最后一步,全部集中在这几个地方:代码能不能在当前项目里跑起来,测试能不能给足够的信心,提交之后别人能不能看明白,部署上去之后线上是不是还活着。随便哪里出问题,都要回来改一轮。AI 没有能力自己完成这些,因为很多信息它根本不知道,比如你们团队约定用什么提交格式、线上环境又设了哪些变量。
1.1 看似一步,其实是五步
我习惯把“最后一步”拆成五类,每一类都有各自的翻车方式:
| 收尾环节 | 核心问题 | AI 能帮到什么 | 为什么还是烦 |
|---|---|---|---|
| 依赖与构建 | 缺包、版本冲突、启动报错 | 生成安装命令、补依赖声明 | 它不知道当前项目实际的锁文件和运行环境 |
| 质量检查 | 格式不统一、静态检查报错 | 按提示做格式化、修 lint | 风格规范通常分散在项目历史里,不贴给它就看不见 |
| 测试验证 | 单测跑不过或等于没测 | 生成测试用例、修复断言 | 它写出来的测试容易自嗨,和真实行为对不上 |
| 提交交付 | diff 太乱、提交信息含糊 | 起草变更说明 | 最终要人为确认改了什么、为什么改 |
| 部署上线 | 环境变量缺失、配置不对 | 生成部署脚本 | 本地环境与线上永远是两个世界 |
这样列出来再看,所谓“一步”,其实是五个串行的小关卡。任何一个关卡卡住,后面的都走不动。人之所以觉得烦,不是因为难,而是因为每一步都很琐碎,打破心流。
1.2 为什么 AI 生成代码越快,最后一步越刺眼
这两年我明显感觉,AI 写代码把“生产端”速度提上去了,却没有把“质量端”速度提上去。以前自己写代码,写的时候就在想边界条件,所以最后收尾的工作量是内嵌在整个过程中的。现在用 AI 写,生成太快,我们把质量压力全部留到了最后。
第二个原因是收尾工作难以并行。依赖检查和测试也许能开几个窗口同时跑,但业务逻辑的最终确认、提交信息的梳理、部署后的观察,都得一个人串行完成。AI 生成代码再快,也不会帮你减少这些等待。
第三个原因是信任问题。面对同事写的代码,你默认对方理解了业务,很多细节扫一眼就过了。面对 AI 写的代码,我总想多看两遍,总怕它在某个偏僻分支里给出一个让人迷惑的结果。这种被迫的警惕心,让最后一步的人工检查工作量不降反增。“生成两分钟,收尾半小时”,就是这么来的。
2. 最让人上火的几种“最后一步”现场(附拆解)
空谈概念没意思,直接把我最近一年踩过的真实场景摆出来。它们都属于“最后一步”的范畴,但也各有各的脾气。
2.1 依赖地狱:代码是新的,环境是旧的
某次我让 AI 写一个内部后台的数据导出功能。功能本身不复杂,无非是查询、组装、写文件。AI 很顺畅地给了完整实现,可一跑就报错,说找不到某个日期处理函数。我检查版本管理文件才发现,项目里锁定的依赖版本比 AI 默认使用的新特性要旧,它的代码按最新版本语义写的,自然跑不起来。
为什么会出现这个坑?因为 AI 的训练数据覆盖了大量版本,生成时倾向于采用比较新的 API。除非你明确告诉它当前项目的依赖约束,否则它不会主动去迁就旧环境。我的解决办法是:在提示词里直接放一段锁定文件的内容,或者写明核心依赖的版本号。如果 AI 仍然用了不兼容的写法,我会让它给出两套方案:要么升级现有依赖,要么改用旧 API 实现。千万不要让它顺手帮你升依赖,很多时候升级牵连一大片,这是最后一步最典型的“局部扩大化”。
2.2 假测试与脆测试:绿灯不代表安全
有一次 AI 帮一个接口补了单测。我看输出结果全是绿色,差点就提交了。后来仔细一读才发现,它 mock 了所有外部依赖,断言也只验证了“方法被调用过一次”,对返回内容、边界行为完全没检查。这种测试等于给最后一步放了一颗烟雾弹:CI 绿得漂亮,但代码上线后该出的错一个不少。
踩过这个坑之后,我给自己定了一条规矩:AI 生成的测试,我必须重点看三样东西——一是断言是否断言了业务结果而不是调用过程;二是异常路径有没有覆盖;三是 mock 是不是太宽,把关键逻辑也给 mock 掉了。如果这三样不达标,我不会让它修复,而是直接把测试意图描述得更具体,比如“用户余额不足时要抛出错误并保持事务回滚”。
2.3 格式战争:AI 的审美和项目规范不是一回事
这个看起来最鸡毛蒜皮,实际上最容易引起 review 争议。我所在的项目长期使用双引号和分号风格,AI 默认输出单引号和无分号风格。代码本身没什么问题,但一提交,格式差异铺满了整个 diff,真正的业务改动反而看不清。
后来我不再把格式问题留给最后一步。解决方式是双管齐下:一方面,在提示词里附一小段现有代码作为风格模板,让 AI 模仿;另一方面,在提交前接了一个统一的格式化和静态检查钩子,不管谁生成的代码,提交前自动修正格式。格式化这类机械动作不该靠人眼判断,也不该靠 review 时的道德压力,交给自动工具是最便宜的。
2.4 环境差异:本地跑得好好的,一上测试环境就挂
这是我最痛的一次经历。AI 生成的模块在本地启动一切正常,我自认为最后一步已经走完,结果部署到测试环境直接起不来。查了半天,发现代码里一个配置文件路径是我本机的目录,数据库连接地址也写死了。AI 并不知道这些信息应该来自环境变量,它只是按照一种“默认可用”的逻辑填了进去。
这个坑的本质是:AI 生成的是“通用代码”,而项目需要的是“环境无关的代码”。现在我的最后一步里专门有一个环节叫“环境自检”,部署前检查硬编码路径、密钥占位符、外部服务地址是否都已经外置成配置项。与其依赖部署平台报错,不如在脚本里主动检索可疑写法。这几个案例看起来不相关,但共同点值得多说一句:AI 不是不靠谱,而是它缺少项目约束。给它越多的上下文,最后一步的惊喜就会越少。
3. 我试过有效的“收尾前置”策略:不要让最后一步最后才做
讲了这么多坑,核心其实是我意识到一件事:很多最后一步的痛苦,是因为我们把收尾动作全部推迟到了生成完成之后。后来我尝试把一部分收尾动作前移,在提示词阶段就埋下约束,效果立竿见影。
3.1 给 AI 补足项目上下文,而不是让它“自由发挥”
以前我常用的提示词是“写一个某某功能”,后来发现这是最差的用法。AI 没有上下文,就只能按照统计上最通用的做法来,而这些通用做法往往和当前项目有偏差。偏差积累到最后,全部变成收尾的工作量。
现在我至少会往提示词里塞三样东西。第一,项目的目录结构或关键文件路径,让 AI 知道代码该放哪、该和哪些模块交互。第二,依赖约束,明确告诉它以锁文件为准,不要默认升级。第三,代码风格示例,一段十几行的现有代码就够了。这么做之后,生成结果的返工率明显下降。
提示:把项目约束写进提示词,不是为了让 AI 更聪明,而是为了减少它“自由发挥”的误差空间。
3.2 用“完成的定义”约束生成结果
“完成的定义”是我从团队协作里借来的概念。以前说“让 AI 实现某功能”,它输出一段代码就算完成;现在我会约定,只有满足下面四个条件才算完成:所有测试通过、格式符合规范、没有新增无用依赖、附一段可读的变更摘要。
为了让这个定义可执行,我甚至会把一个收尾清单模板放进提示词,要求 AI 在代码结束后自己先按清单自查一遍。例如:
请实现内层模块的查询接口。项目约束如下: 1. 现有技术栈保持不动,不要新增依赖。 2. 核心逻辑必须有输入校验和错误返回。 3. 代码风格参照示例文件。 4. 完成后请提供变更摘要,包括改了哪些文件、影响什么功能、如何测试。不要让 AI 只给你代码,还让它给你“完成的证据”。证据出来了,最后一步的人工确认会轻松很多。
3.3 生成一段,立刻收尾一段:宁可小步走
我现在很少让 AI 一次生成一个几千行的大模块。不是它做不到,而是收尾会失控。几千行的 diff 里藏着一个错误的依赖或一个离谱的断言,排查起来非常痛苦。拆成小段之后,每段生成我马上跑一遍格式化和相关测试,有问题当场修,不要攒。
这有点像洗碗,刚吃完饭顺手就洗了,油污不会结块;堆到第二天再洗,就要多花一倍的时间。收尾工作最怕延迟,越晚处理越麻烦。AI 生成很快,那就让它小步快跑,不要让“完成度”堆积成必须一次性消化的包袱。
4. 把“最后一步”做成流水线:自动化兜底与人工兜底
哪怕前置策略做得再好,也总有漏网的收尾问题。所以到了最后,我干脆把“最后一步”做成了半自动化的流水线:能交给机器的交给机器,机器兜不住的才交给纪律和清单。
4.1 自动化能兜住的,就别让人手记
先别试图写一个全自动的魔法脚本,而是从最小粒度开始。我现在固定有三件自动化的事。
第一,提交前的格式化和静态检查。只要代码准备提交,钩子先跑一遍,不合适就直接改格式,不阻断但负责擦干净。
第二,合并前的完整测试和构建。我在代码托管平台上配了流水线,合并请求一开就自动跑全部测试和构建流程。AI 生成的代码必须同样过这个门禁,没有特权。
第三,部署前的环境自检脚本。脚本会检查环境变量是否存在、配置文件里有没有硬编码本地路径、迁移命令是否准备好了。这一步把我 2.4 里踩的坑给堵住了。
这些自动化动作的价值不是省多少时间,而是把“我可能忘了检查”变成“系统一定会检查”。人的记忆不可靠,机器不会。
4.2 自动化兜不住的部分:人工复查的禁区清单
自动化再强,也有几个地方我不敢全信,必须留出人工时间。一是业务语义,AI 可能字面上实现对了,但完全误解了需求的真实目的。二是安全边界,涉及越权、敏感数据、支付操作时,必须人肉确认。三是异常处理,我看过的 AI 代码里,重灾区就是缺少对异常分支的处理,常常理想化地认为外部服务永远正常。
所以我的最后一步里一直保留着“人工复查禁区清单”,不多,但每一条都顶用:有没有校验输入?失败时会不会把用户带到错误状态?错误日志里有没有把敏感信息打出来?核心操作有没有幂等保护?这些问题是 AI 最容易忽视的,也是线上事故最常见的来源。
4.3 门禁要一视同仁:AI 生成代码也要走同样流程
团队协作里最怕出现“AI 特快通道”。因为 AI 生成速度快,有人会觉得“它写的应该没问题,让它先上”,结果质量门禁形同虚设。我现在的立场是:不管代码是人类写的还是 AI 生成的,都必须走同一条提交流程、跑同一套检查、过同一个 Review 标准。
这不是对 AI 不信任,而是为了流程的可预测性。只要有一次开了例外,你没法向团队解释为什么这次可以跳过检查,下次就不行。最后一步本来就烦,如果还要在流程是否公平上扯皮,那才是真的灾难。
5. 我现在固定使用的收尾清单(可以直接抄)
说了这么多原则,总得给一份能直接照着做的清单。下面是我自己保留的“提交前 15 分钟”模板,覆盖了整个最后一步的常见支出。
5.1 “提交前 15 分钟”清单
| 顺序 | 动作 | 常见坑 |
|---|---|---|
| 1 | 查看变更范围,确认 diff 里没有无关文件 | AI 顺手改了配置,混在代码里 |
| 2 | 检查依赖变更,锁文件是否多出不必要的新增 | AI 擅自升级依赖版本 |
| 3 | 跑格式化和静态检查,让机械问题自动消失 | 格式差异淹没真正的代码审查 |
| 4 | 跑单测及相关模块的冒烟测试 | 测试全绿但断言是空的 |
| 5 | 人工复查核心逻辑的输入、异常、安全和边界 | 遗忘外部依赖失败场景 |
| 6 | 写提交信息:让 AI 帮忙起草,再自己改写一遍 | 信息写得像“修复 bug 优化代码”,毫无信息量 |
| 7 | 执行部署前环境自检,配置和密钥要就位 | 本地好使,线上启动就挂 |
这七步听起来多,但前几步都是自动化在跑的,真正需要人工的只有第 5、6 步。剩下的时间主要是等待命令跑完。
5.2 几个我踩过之后才改掉的习惯
第一,不让 AI 一次生成整个大型功能。拆成任务后,每个任务生成完就立刻验证,错了也好定位。
第二,不把 AI 生成的代码当黑盒。无论多信任工具,至少要读一遍核心函数。不是所有错误都能靠测试发现,尤其是业务理解偏差,只有人看了才会露馅。
第三,把最耗时的收尾动作做成“一键脚本”。我把自己环境里的检查和问题修复命令攒成了一个脚本,每次提交前只跑一条命令,它会输出该修还是能过。省下的时间不是一点半点。
第四,心理预期要调整。我会默认 AI 生成的代码只完成了四成,而不是八成。听起来很悲观,但反而让我的最后一步心态好了很多——因为我知道还有六成收尾工作要做,不会在中途因为频繁补救而烦躁。
还有一个小技巧,我会把这套清单文本放在常用提示词的末尾,要求 AI 在生成代码之后先自我检查一遍再提交结果。很多低级的错误,比如漏掉错误处理、缺少边界判断,在这一步就能被它自己发现,根本走不到我这边。
最后一步确实烦,但我越来越觉得,它不是 AI 编程时代的一个负担,而是一个必须重新设计出来的流程。以前我们靠人肉记忆去收尾,现在可以靠提示词约束、自动化门禁和一张逼自己别偷懒的清单。把能自动化的都自动化之后,真正留下来需要人判断的,往往是业务和安全这类最值得花时间的部分。如果你也在用 AI 写代码,我的建议是先别追求生成速度,先花点心思把收尾流程磨顺。磨顺之后,AI 写代码才真的开始省心。