AI 交叉审查通过,代码为何仍偏离需求
作者让 Claude 与 Codex 相互审查,合并 30 个 PR 后才发现功能堆积了未要求的验证与复杂流程。案例指出,缺乏需求背景和范围约束的审查 Agent,可能认可局部代码却漏掉整体偏航。
AI 深度解读
让 Claude 和 Codex 互相审 PR,连续合并 30 个之后,功能仍然可能变成一座过度设计的迷宫。作者踩过这个坑:两位审查员都同意合并,却没人追问那些额外的校验、数据表和抽象到底是谁要的。
他的办法是改变审查顺序:先让 Agent 列出改动事实,再由人带着需求和产品计划逐项追问。对已经开始用 AI 批量写代码的程序员,这篇文章最有用的地方,是教你识别「代码看起来合理,东西却不该存在」的问题。
两个 Agent 互审,为什么仍然拦不住过度设计
作者用一天写出 100K 行代码、打开 30 个 PR 来描述 Agent 的产出规模。这不是吞吐量测试,而是在提出一个现实矛盾:代码生成速度已经超过人的逐行阅读能力。
他也试过常见做法:启动一个新 Agent,要求它审查 PR,人再扫一遍意见,然后合并。做某个完整功能时,他还让 Claude 和 Codex 交叉审查。结果是,30 个 PR 全部通过,等到人工测试才发现,整个功能塞满了没有要求过的验证步骤和严格校验。
这里失败的不是某条语法检查,而是需求边界判断。
新审查员没有参与实现过程,也不知道功能为什么做、只需要做到哪里、下个月准备接什么。面对通用重试框架或新缓存层,它容易默认「既然写出来了,应该有理由」,于是建议改名、补测试,却很少质疑这层东西是否需要存在。
代码内部自洽,不等于它符合需求。
交叉审查增加了阅读代码的角色,却没有自动补上产品上下文。两个 Agent 可能共同接受同一个未经验证的前提,最后形成一台高效输出 LGTM 的机器。
先问改了什么,别急着问写得好不好
作者说,这套追问方式已经用了约一年,现在他主要靠它发现过度设计。不过,原文没有提供缺陷率、耗时或前后对照数据,应当把它当作个人工作经验。
第一步仍然是启动新 Agent,但任务改成整理事实。下面这段提示词解决的是「先看清变更范围,再讨论质量」:
Read this PR and give me a report on exactly what it changes. List:
- Database schema changes (new tables, columns, indexes, migrations)
- Scheduled job / cron / background worker changes
- A summary of every feature added or modified
- New external services, APIs, or third-party libraries
- New environment variables or config flags
- New public endpoints or interfaces
- Anything deleted or deprecated
Don't judge the code yet. Just tell me what it does.
最要紧的是最后一行:先不要评价代码。否则 Agent 很容易直接给出「整体清晰、结构良好」之类的结论,把真正需要检查的改动埋在赞美里。
清单覆盖数据库结构、定时任务、后台 worker、功能变化、外部服务、API、第三方库、环境变量、公开接口,以及删除和废弃内容。它的价值在于,把几千行 diff 压成一份人能逐项核对的变更账单。
接下来要找的,是那些看似解释了、实际没解释的句子。例如,「新增 sync_state 表,提高同步可靠性」「新增后台任务,执行周期性维护」「增加 FEATURE_X_MODE,支持灵活配置」。
这些是作者编造的示例,并非实际 PR 摘录。它们的共同问题是:只讲了好处,没有交代具体需求、触发条件和使用者。
「提高可靠性」可以解释很多代码,也可能掩盖没人要求的复杂度。看到这种表述,应该继续问它究竟解决哪一种失败,以及现有实现为什么不够用。
追问存在理由,还要让回答落到证据
作者给了一个实际案例。他要求实现 Agent:第三方服务宕机并返回错误码时,给管理员发通知。结果 Agent 新增了两张表,分别跟踪 Slack 通知和邮件状态。
需求是一条消息,实现却长出了一个数据模型。原文没有展开这两张表的结构,也没证明所有通知场景都不需要持久化;它说明的是,新增机制应当有能说清的理由。
作者的追问集中在几类地方:
- 新表由谁提出,已有表为什么处理不了?
- 每晚凌晨 2 点运行的 cron 具体做什么,是否与现有服务重复?
- FEATURE_X_MODE 有谁会设置为非默认值?
- 新依赖解决什么,项目已有工具为什么不能完成?
- 非法输入在哪一行被拒绝,又由哪个测试证明?
- 功能只要求做 X,PR 中哪些部分超出了 X?
这里既检查「该不该有」,也检查「是否真的有效」。尤其是输入校验,不能停在「已经处理异常」的描述上,要看到代码位置和测试证据。
还要主动补充 Agent 不知道的事实。例如,产品永远只部署一个 region,某些区域抽象就需要重新解释;下一阶段要接入更多外部工具,则应该检查当前设计是否方便扩展。
这两种信息可能指向相反的设计选择。目标并不是一律删抽象,而是让设计服从真实计划。
作者用「Agent 几秒内能读 3,000 行代码」说明分工:机器负责读取,人负责解释目标。这个速度同样没有测试条件,不能当成性能承诺。可取的是审查方式:用问题把代码事实和需求边界接起来。
另外,Agent 一上来就回答「你完全正确」,并不代表问题已经查清。认同很容易,解释依赖关系、指出位置、拿出测试才有用。
我的判断
我认同把「变更事实」放在「质量评价」之前。它比一句宽泛的「review this PR」更容易暴露范围膨胀,也让人有机会在讨论命名和格式之前,先决定这段代码是否该进仓库。
但作者把新审查员描述为缺少上下文,这个前提可以改善。需求说明、验收标准、明确不做的事项和后续计划,都可以在第一次审查时提供。原文没有比较「带完整上下文的一次审查」与「多轮追问」的效果,无法据此判定后者一定更好。
另一个局限是,人仍然需要判断哪些问题值得追问。只读摘要,可能漏掉 Agent 没报告的权限变化、错误分支或数据处理问题。原文没有给出保障摘要完整性的办法,因此这套流程适合补强范围审查,不能据此推导出「大多数 diff 都无需阅读」。
让 Agent 为新增复杂度举证,比让它评价自己的代码更有用。
不过,人的质疑也不天然正确。缓存、重试、持久化都可能有合理需求;删除前同样需要核实调用关系和失败场景。最终该接受的是证据,不是 Agent 的赞同,也不是审查者的直觉。
能拿来做什么
- 在日常 AI 编程中,把原文提示词作为 PR 审查的第一轮输入,先核对新增功能、配置、依赖和接口,再提供需求边界继续追问。
- 审查 Node 服务时,重点解释新增表、cron 和 worker 的必要性。如果需求只是调用外部服务后通知管理员,就要求 Agent 说明为什么还需要持久化状态,以及失败后如何处理。
- 审查前端 PR 时,逐项检查新依赖和配置开关,让 Agent 说明现有工具为什么不够、谁会使用非默认配置,避免把「以后可能用到」直接变成维护负担。
- 对输入拒绝和异常处理,要求回答指向具体代码行与对应测试;对准备删除的额外机制,也要求给出依赖和影响证据,再决定是否裁掉。