AI 写代码的问题从来不是「写不出来」,而是「写出来了,而且看起来完全正确」。这篇是我自己在用的三层验收清单。
为什么 AI 代码特别需要验收
人类写的烂代码通常是显式的:命名混乱、结构臃肿、注释和实现不符 —— 你在读的时候就会起疑。
AI 写的代码往往相反:命名规范、结构清晰、注释齐全、风格统一。它「看起来对」的概率远高于「真的对」。
这就是风险所在 —— 读起来越顺,越容易跳过验证。
更麻烦的是,AI 的失败模式相当隐蔽:
- 可能发明一个不存在的 API,而且用法看起来极其合理
- 可能只处理了正常路径,边界条件全靠「大概不会出现吧」
- 可能静默改变副作用范围:你让它重命名,它顺手删了几个「看起来没用」的函数
- 它的自我解释和代码不一致 —— 说「我加了校验」,其实没加
所以验收不能靠「读一遍觉得没问题」,得有一条可执行的清单。
第一层:能跑吗(机械层)
目标:排除「根本跑不起来」这种最低级错误。
- 语法与依赖:能编译 / 能导入吗?依赖的包真的存在吗?版本对吗?
- 最小输入跑通:给一个最简单的输入,能产出结果吗?
- 错误路径也跑一次:这是最容易被跳过的一步。给一个明显不合法的输入,看它是优雅报错,还是直接崩掉 —— 或者更糟,静默返回错误结果。
静默失败是最危险的。程序崩了你会立刻知道;程序返回了一个错的结果,你可能一周后才发现。
第二层:对了吗(语义层)
目标:排除「跑起来了但结果不对」。
- 黄金样例:拿一个「我已知正确答案」的输入去校。注意 —— 答案必须是你自己确认过的,不要用 AI 给你的标准答案,那是循环论证。
- 边界值:空输入、超长输入、特殊字符、极值、并发。AI 默认只写「正常路径」。
- 交叉验证:换一个独立来源核对结果。比如它写了个解析器,就用另一个工具解析同一份数据,对比输出。
- 抽查中间结果:不要只看最终输出。让它打印中间变量,确认每一步都符合预期 —— 因为「每一步都对但最后拼错了」和「中间就错了」需要完全不同的修法。
第三层:稳吗(工程层)
目标:排除「这次对了,下次出事」。
- 幂等性:同一个操作跑两次,结果一样吗?如果不一样,第二次会带来什么后果?
- 失败可恢复吗:任务执行到一半中断,会留下半成品状态吗?下次能续上吗?还是必须先手工清理?
- 可观测吗:出错时日志里能看到足够信息定位吗?还是只有一句「操作失败」?
- 副作用范围:它会动哪些东西?这个范围是设计好的,还是「顺手」扩大的?尤其注意删除、覆盖、批量修改类操作。
这一层最容易被忽略,但代价最高 —— 前两层的问题通常只是「重做一遍」,这一层的问题可能造成不可逆的损失。
一条硬规则
任何「改数据 / 改配置」的操作,动手前必须先备份,或先做零改动探针。
零改动探针的意思是:让 AI 把目标原样读出来、原样写回去,然后比对前后是否一致。
- diff 为空 → 「读 — 改 — 写」链路可靠,可以放心改
- diff 有差异 → 先解决这个差异,否则它改 A 字段时可能顺手把 B 字段弄丢
我靠这条规则躲过一次事故:某个工具会把文件重新序列化,顺手抹掉所有注释。如果不做探针,我会以为「只改了一个字段」,而实际上注释全没了。
三个反模式
| 反模式 | 后果 |
|---|---|
| 相信「我改了但没测」 | 改动是否生效完全未知 |
| 把 AI 的自我解释当证据 | 解释和代码不一致是常态,要看代码、看输出 |
| 一次改动里混入多个变更 | 出问题时无法归因,回滚也不知道该回滚哪个 |
一个更根本的习惯
验收的收益,和验证的独立性成正比。
如果验证方式也是 AI 想出来的,而且是顺着它自己的实现思路想的,那这个验证很大概率会复现同样的盲区 —— 它没考虑到的边界,它的测试也不会考虑。
所以最有效的做法是:让「验证」来自另一个来源。
- 实现由 AI 写 → 测试用例由你从需求出发写
- 数据由 AI 解析 → 用另一个独立工具交叉核对
- 结论由 AI 给出 → 找一个反例去攻击它
小结
三层清单,从低到高:
- 能跑吗 —— 语法、入口、错误路径
- 对了吗 —— 黄金样例、边界值、交叉验证
- 稳吗 —— 幂等、可恢复、可观测、副作用范围
再加一条硬规则(改数据前先备份 / 先探针),加一个习惯(验证要独立)。
AI 写得快,这是它的价值。但快不等于可以省掉验收 —— 省掉验收省下的时间,最后都会以「排查一个不知道为什么错的结果」的形式还回来。