Skip to content

我拒绝合并那段 AI 补丁:打开 diff 后先看的三处 ​

上周我拒绝合并一段看起来能跑的 AI 补丁。不是因为它报错,是因为它太会说话:函数名像文档,注释像保证书,测试像摆拍。合进去会让仓库「看起来更完整」,也会让下一次真实故障更难定位。

这篇不是编程助手怎么选,也不是 Copilot 键位。这是我打开 diff 之后的现场笔记:先看哪、为什么拒、拒的时候说什么。工具背景可去 GitHub Copilot 使用教程 和 AI 编程助手怎么选;下面默认补丁已经出现在你的审查框里。

我先看的不是「智不智能」 ​

我会把 diff 滚到最无聊的地方。AI 喜欢把力气花在新文件和新抽象上,真正的事故往往在:

  • 错误被吞掉的 catch;
  • 默默改掉的返回值形状;
  • 测试里被写死的时间、路径、网络;
  • 为了让类型通过而加的 any / 强制断言。

这些改动通常不多行,也不出现在聊天窗的总结里。聊天总结是广告;diff 才是现场。

审查顺序:先看错误处理与测试,再看新抽象

助手越会写说明,你越要先看它不好意思写进总结的那几行。

现场一:它把失败变成了成功 ​

有一次补丁在解析配置失败时返回空对象,并写了注释:优雅降级,避免中断用户体验。

我拒的理由只有一句:失败被做成了空配置,调用方会以为键不存在,而不是文件坏了。

AI 很擅长把「不要抛错」理解成「永远成功」。审查时问自己:这个空返回,是产品决策,还是模型为了让示例能跑?如果提交说明里没有写出降级的可观察信号(日志、指标、明确的错误码),我就当它是为了绿而绿。

现场二:测试在配合函数演戏 ​

另一段补丁加了 40 行测试,全部通过。点开发现:文件系统被换成内存假对象,时间被冻死,断言只查「返回了数组」。

这不是测试,是合影。

我要求补两种东西之一,否则不谈合并:

  • 一个真实会失败的例子(坏输入、超时、权限);
  • 或者明确标注:这是示例,不充当回归网。

没有失败用例的测试,会阻止你以后改代码——因为假绿灯比没测试更危险。

现场三:抽象比问题先到 ​

第三段补丁引入 Manager、Factory、Helper 各一个,调用链变长,业务规则仍写在注释里。

我的经验是:AI 一遇到「以后可能会」,就会提前建庙。审查口令很土:这个抽象删掉后,diff 是否仍能说清这次要修的那一个行为? 能,就删。不能,才留。

选型文会告诉你何时换 AI IDE;审查现场只问这一刀。多文件改动越大,越要用这句压范围,而不是反过来夸「上下文真完整」。

我会写进拒绝理由的原话 ​

与其说「写得不好」,不如用可以执行的句子。我常用这几条,几乎不用改:

  1. 失败路径被改成空成功,请恢复可区分的错误。
  2. 测试没有反例,请加一条应当失败的输入。
  3. 新抽象与本票无关,请先以内联方式修行为。
  4. 提交说明在复述代码,请改成:用户可感知的变化 + 风险。
  5. 注释在保证正确,请删保证,留约束(输入范围、线程、超时)。

提示词侧若总产出「保证书注释」,按幻觉与正确废话修,见 提示词失效排查。审查是最后一道门,不负责替模型把课补完。

拒绝合并时要写清的五类理由

拒绝理由要让作者知道下一份 diff 改哪,而不是让他去「再生成一次更完善的」。

什么时候我会同意合 ​

不是「看起来专业」,是这三件事同时成立:

  • 行为变化能用一句话说给不写代码的同事听;
  • 失败可被看见(日志或错误类型),不是被消化成默认值;
  • 我能指出哪条测试会在回归时红。

满足这三条,文笔华丽与否不重要。不满足,模型名再强也不合。

我贴在 PR 里的短评骨架 ​

不写小作文。三行够了:

  • 看见了什么(空返回 / 无反例 / 多余类型)
  • 为什么这比「能跑」更贵(下次故障不可见 / 假绿灯 / 范围膨胀)
  • 下一份 diff 最小改什么

作者如果回「那我让模型再生成一版完整的」,我会直接回:不要完整,要这一刀。完整是说明书腔在代码里的对应物。

如果你已经合进去了 ​

别回溯羞辱作者。开一个极小的后续票:只补失败路径或只补反例测试。AI 补丁的后遗症,适合用小票还,不适合用更大的生成来洗。

我自己的习惯是:合入后 48 小时内,用一次真实失败打它。打不红,再信。


笔记来自审查习惯,不是某产品的官方规范;语言与框架不同,但「空成功、摆拍测试、提前建庙」这三类味道很稳。