原文:Anthropic,Solving code review at Anthropic。本文是按原文结构整理的要点笔记,用我自己的话转述,不是翻译;数字以原文为准。最后一节是我对照自己的 Skill 体系做的取舍,具体改动过程见《一次 Skill 迭代的全过程》。

这篇文章讲的是 Claude Code 团队在 AI 写出大部分代码之后,怎么重新设计代码审查。我读它的起因,是另一篇文章(我的笔记见《当智能体代劳时,如何保持批判性思维》)引用了它的一句经验法则:说不清这些检查会漏掉什么,就该请求一次真正的审查,而不是草草批准。

审查到底在解决什么

文章先把代码审查拆成两个作用:在发布之前发现问题,以及让团队对代码库保持共同的理解。

接着它换了一个问法。”有没有人读过这段代码”这个问题不够好,因为它没有回答真正要回答的事:这次改动可能出什么问题,该由人还是由自动化检查来拦。换了问法之后,审查也从”写完、提 PR、等同事反馈”的一道关卡,变成写代码过程中一直在跑的检查,像写作时的拼写检查。常见错误在早期就被拦下,人只需要处理更高层的问题。

稀缺的是人的时间和注意力

文章把人的成本算成两部分:花在审查上的时间,和花在修复漏网问题上的时间。只是把时间从前者挪到后者,不算节省。

团队的目标是:Claude Code 各个仓库里的大多数 PR,最多一个人参与就能合并,这个人可以是作者本人,前提是回滚和事故不增加。他们记录每个 PR 需要的人工批准次数、人工评论数量和等待时间;回滚率和事故率是反向指标,这两个数一涨,就说明省下的人工时间只是转移到了别处。

一个改动要经过的几层

每一层拦住一部分问题、漏掉另一部分,最后漏过去的靠回滚兜底。

  1. 提交前自验证。 跑测试、构建或截图对比,把输出给人看;让全新上下文的子 agent 审一遍 diff;反复出现的审查意见在源头修掉,比如改 skill、加 lint 规则或更新指令文件。CLAUDE.md 写项目背景、规范、目标和非目标;REVIEW.md 由代码负责人写,告诉审查者这块代码里什么最重要。好的 REVIEW.md 很短:给出这个仓库里”失败长什么样”的例子,列出要跳过的路径,要求证据给到文件和行号,总结先给数量,并说明什么情况下不用修。
  2. 每个 PR 都有 AI 审查。 几个 agent 并行找问题,另一组 agent 对每条发现投票,足够多的 agent 认为是真问题才发出来;只关于风格或清理的发现直接丢掉。留下来的按严重程度标成 Important、Nit 或 Pre-existing,Pre-existing 表示问题在这次改动之前就有。每条发现都要回应:修掉,或写明理由后关闭。上线后,收到实质性评论的内部 PR 从 16% 升到 54%。每个仓库还能选审查深度,漏一个 bug 的代价高于多花的审查时间时才开最深一档。
  3. 安全审查。 单独一轮。两种审查里未解决的发现都会阻止合并。
  4. 阻断式自动检查。 lint、类型检查、各平台的测试和构建。PR 模板要求写清楚怎么测的、怎么回滚。
  5. 按风险的人工批准。 低风险改动(文档、文案、受开关保护的新增代码)自动检查通过即可;中风险一个人;高风险两个人。分级由分类器根据改动本身判定,不看作者。agent 的批准永远不算人的批准;批准只对应某一个 commit。
  6. 合并队列、功能开关和分阶段发布。 合并队列在最新的 main 上带着这次改动重跑全部检查,防止两个各自通过的 PR 合在一起弄坏 main。
  7. 快速发现、快速回滚。 事故复盘问的是”哪一层本该拦住它”,不是追究个人。

审查怎么停下来

这一节对我最有用。审查一度停不下来:一个 CLI 的 PR 一夜之间被审了 50 多轮。审查器反复标出的问题里,约 59% 是这次改动之前就存在的旧代码,每一轮只是又挖出几个旧 bug。

他们的修法有四条:设轮次上限,几轮之后作者要重新考虑做法;前几轮之后只审 diff;标出问题的类别,同类实例一次修完;同一个问题不重复报。修完以后,一轮审查加修复就通过的比例从 65% 升到 78%,需要五轮以上的从 8.9% 降到 1.5%。每次事故还会变成审查器的评测用例,审查器的提示词、工具或模型一改,就在全部用例上重新打分。

人仍然留在哪些环节

决定做不做(方向、品味、范围);高风险代码,比如计费、自动更新和 CI 配置;别人会照抄的架构;读证据,也就是测试输出、命令和结果、截图和审查结论;以及为结果负责,每个上线的改动都有一个人负责。

最后一节讲共享上下文:没有人掌握整个代码库的完整模型。需要理解某一块时,让 Claude 现场写一份解释,几分钟就好,而且反映的是当前的代码。

我们的取舍

读完之后,我对照自己的 Skill 体系逐条讨论,决定吸纳什么、不吸纳什么,当天全部实施。代码在 design-integrity-guardrails、reality-evidence-engineering 和 agent-skills-index。

吸纳了的

文章的做法 我们的版本 落在哪里
REVIEW.md 由代码负责人写 同样由负责人写,另加一节机器可读的 ## Acceptance surfaces(验收面 glob),scanner 只读这一节,其余内容给两个审查 Skill 读。新增 review-md 负责起草:只在我点名时运行,只问代码答不出的问题,我批准后才保存 guardrails 仓库的 review-md/ 和 design_integrity.py
全新上下文的子 agent 审 diff design-integrity-review 恢复干净上下文:只给目标、非目标和验收条件,不给实现理由;每条 FAIL 附一个能走到被标代码的输入 design-integrity-review
Pre-existing 标签 标 [pre-existing],不阻断本次改动,单独开票 两个审查 Skill、evidence-first-testing
标出问题类别、同类一次修完、不重复报 报告写类别并列出全部实例;修复时整类修完;复审只检查旧问题修好没有 同上
轮次上限 原来就有:一次审查,修完最多一次定向复审;同一条不变式再被打破时返回 needs model,改为写出不变式、系统地搜索 两个审查 Skill、全局 AGENTS.md
总结先给数量 报告第二行是 SUMMARY:,没有问题就写 No findings. 两个审查 Skill
按改动本身分风险等级 低、中、高三级:文档、文案、配置值不跑审查 Skill,diff 就是证据;普通代码按路由表;敏感路径、验收面、schema、迁移、公共接口两个审查都跑,再由我看证据批准 全局 AGENTS.md 的 Review depth
PR 模板写怎么测、怎么回滚 完成报告:状态词,加 Works / No regression / Risk / undo 三行 全局 AGENTS.md
合并队列在最新 main 上重跑全部检查 全量测试放在合入点;仓库的 CI 会跑全量时,本地提交前不重复跑 AGENTS.md 的 Testing、evidence-first-testing、项目模板
反复出现的审查意见在源头修 同类问题再出现时,先给出能拦住它的检查及运行位置;机器判定不了才写文字规则;区域级要点写进 REVIEW.md 两个审查 Skill
说不清检查会漏掉什么,就要真正的审查 LIMITATIONS 写没覆盖的范围;没复现的问题写 “may”,不写成事实 两个审查 Skill

没有吸纳的

  • 多 agent 并行找 bug、再投票验证。 成本高。我们用”问题必须先复现才算数”达到同样的目的:每条 FAIL 都要附一个能走到它的输入,只靠读代码推出来的问题只能写成 “may”。
  • 风险分类器、CODEOWNERS、分支保护、功能开关和分阶段发布。 这是团队级基础设施,个人 Skill 用不上。
  • 把事故做成审查器的评测集。 方向对,但需要数据量。以后可以拿多 agent 项目的审查报告当素材。
  • 人工时间、回滚率这类指标。 个人使用,样本太少,统计不出东西。

文章没覆盖、我们补上的

  • agent 改验收测试。 文章默认测试本身可信。可我实际遇到过 Codex 在功能没做完时改测试、宣布完成。我们把验收面写进 REVIEW.md,实现期间冻结;Stop hook 发现验收面被改就阻断。REVIEW.md 本身也冻结,否则删掉一条 glob 就能绕过。
  • 比较的起点。 SessionStart 时记下会话起点,scanner 从起点开始比,agent 中途 commit 的改动也看得见。
  • Codex 上的 hook 会静默失效。 未被信任的 hook 在 codex exec 里会被跳过,没有任何警告。所以检查装好后,要用一次真实运行确认它确实跑了。
  • 测试的成本。 跑测试前先说这次要证明什么;全量通过只能证明没有回归,不能证明新功能是对的。

这篇文章写的是一个团队的系统:分类器、投票、合并队列、发布开关,个人很难照搬。但它背后的几个判断对个人一样成立:人的注意力是最贵的资源;审查要能停下来;问题要先能复现,才算问题。