Featured image of post Claude Code 团队的代码审查方法:六层检查、按风险分级审批、一个审批只覆盖一个 commit

Claude Code 团队的代码审查方法:六层检查、按风险分级审批、一个审批只覆盖一个 commit

Anthropic 的 Claude Code 团队在 10 月 15 日发了一篇工程博客,把自己团队怎么做代码审查讲了一遍。里面有一组数字值得先放在最前面:

从 6 月到 9 月,合并进 Claude Code 代码库的 PR 翻了一倍多,而其中被迫回滚的比例降了 40% 以上。同一时期,Anthropic 那个 monorepo 的日均落地变更数从 5 月的约 2,500 涨到 9 月的 30,000 以上。

在变更量涨一个数量级的前提下把返工率压下去,这本身就是个反常结果。而它靠的是把「审查」这件事的骨架换掉,跟模型是不是更聪明关系不大。

他们把人工时间拆成了两个账本

这套流程里最容易被漏掉的一句判断,藏在它对「省时间」的定义里。

团队把人工时间看成两笔账:一笔是花在审查上的时间,另一笔是花在修复坏代码上的时间。原文的说法是,如果只砍第一笔,而第二笔跟着涨,那就没有省下人工时间,只是把它挪了个位置。

于是 revert 率和事故率在这套流程里被当成护栏来用,不再是事后的附带统计。他们真正在优化的目标写得很清楚:Claude Code 那几个仓库(CLI、桌面、Web、移动端)的多数 PR,最多只要一个「人在环里」,这个人甚至可以是作者本人,前提是回滚率和事故率不上升。

度量方式也具体:每个 PR 的人工批准数(看中位数和第 95 百分位)、人工评论数、以及 PR 等一个人等了多久。三个指标一起看,才能区分「审查变快了」和「审查被跳过了」。

这个双账本框架是我觉得这篇文里最值得抄走的部分。很多团队引 AI 审查时只看「平均审查时长下降」,而返工时间悄悄转移到另一个地方去了,账面上看不出来。

六层,每层的洞都在不同地方

他们给出的结构是一张六层图,从上到下依次是:验证、必需 CI、AI 审查、安全审查、人工批准、合并队列加特性开关。原文的比喻是每层都有洞,而洞的位置各不相同,漏过某一层的问题会被后面某一层接住,穿过所有层的才落到最底下的快速回滚。

层 谁在跑 拦什么 关键设计
1 验证 作者(人或 Agent) 「做完了」其实是假的 给 Agent 一个能跑的东西(测试、构建、截图比对),要求它展示输出而不是声称完成
2 AI 审查 每个 PR 必过 常规错误 读 diff 对比整个仓库和 PR 声明的意图;发现项先由多个 Agent 投票复核,只投真实的
3 安全审查 自动 安全问题 与第 2 层一样可以阻塞合并:有未关闭的发现项就合不了
4 必需 CI 自动 平台兼容、类型、lint 一个 CLI PR 在有人看之前可能已经跑了几十个检查
5 人工批准 人 高风险判断 按变更风险分级,见下一节
6 合并队列 + 开关 自动 两两合并才炸的情况 合并队列把每个 PR 应用到 main 上重跑全部检查;新行为先给内部员工用

第一层里有个细节值得单独说。他们要求 Agent 展示可验证的输出,而不是汇报「已完成」。Boris Cherny(Claude Code 的作者)说这大概是拿到好结果最重要的一件事,他估的价值是最终效果变好两到三倍。这个习惯的落点是让「完成」这个词有证据。

第二层的机制也值得拆开:发现项在交给人之前,先由另一组 Agent 投票。几个 Agent 同时找 bug,然后另一组对每个发现项投票,票数够才发布出来。只涉及风格、诊断信息、清理类的发现项直接丢掉。他们还用自己仓库里的 PR 做过 A/B,确认这套过滤器值得留下。剩下的发现项按严重度排序,以行内评论的形式出现,并且标注为重要、小问题或先前就存在——后面这个标签的意义是:一个不是这次变更引入的 bug,不该看起来像作者的锅。

审批粒度缩到了单个 commit

第五层是整篇里最反直觉的部分。他们没有给「谁有权限批准」设计一套角色体系,而是让变更本身决定风险档位。

风险档 例子 需要几个人批准
低 文档、文案、仅内部的功能、藏在开关后面的增量变更 自动审查与安全检查全绿后,不需要人
中 常规产品变更,以及缓存、自动更新器、系统提示词这类敏感区域的改动 一个人
高 风险最高的代码,加上一小撮「永远需要两个人」的路径 两个人

档位由分类器读变更内容来定,第二个分类器再单独判断「这次变更是否还需要额外一个人,哪怕它的档位说不需要」。有些路径永远需要人:代码所有者标为敏感的,以及任何 CI 工作流文件。

配套还有三条规则,我认为是这套流程在制度上最硬的部分:

只有受信任的作者走轻量路径。 只有在该仓库受信任名单上的人(基本就是拥有这个仓库的团队)才能免掉人工批准,其他人一律需要第二个人签字。

Agent 的批准永远不算人的批准。 自动审查可以放行一个低风险变更,但它不能去填一个要求由人填的位子。

谁下的提示词,谁对这个变更负责。 一个由 Agent 写出来的 PR,下指令的那个人算作必需审查人之一,跟这些代码是他自己敲的没有区别。

还有一条粒度上的规定:一个批准只覆盖一个具体的 commit。如果之后落进了系统无法担保的新提交,比如 Agent 自己推的、或者有人手工解决冲突产生的合并,批准就得重新给一次。

把这几条连起来看,审查的对象已经从「人」换成了「commit」。审批不再是一份持久的授权,而是对某个具体状态的一次性确认。这大概是「持续集成」在 Agent 时代的对应物:持续授权。

审查者自己也在被审查

第六层之后是这套流程里我觉得最有意思的部分:他们怎么让审查者本身变好。

事故变成评测集。 团队把历史上导致过事故的 PR 收集起来,连同当时的修复提交和回滚一起,让熟悉那段代码的工程师逐条确认 bug 是什么、在哪里。审查者每天对这套集合跑一遍,而每一次对审查者的改动,都要在这套集合上打分,分数涨了才留下。

噪声用人的行为来量。 想多抓 bug 很容易,代价是多报非 bug。他们不靠内部打分,而是把审查者留下的每条评论按 PR 上的人的反应归类:改了、说它错了、说这个行为是故意的、或者认为不重要。另外还会拿一批已知干净的 PR 跑审查者,统计它有多少次会让人做出有害改动。优先级写下来是:抓真 bug 排在减少审查轮次前面。

这里有一句我认为值得所有做 AI 审查的人记下来:当作者是 Agent 时,噪声的代价更高。 人可以在一秒内把一个小问题挥掉,Agent 会去修它,于是多一个提交、多一轮审查。

真正没解决的是收敛问题

如果这篇文只讲六层和分级,那它是一份漂亮的流程说明。但团队自己写出了这套机制的失败模式,而且写得比外部批评更狠。

Agent 作者和 Agent 审查者可以互相喂活,无限期。 有一个 CLI 的 PR,在一夜之间经过了五十多轮审查与修复,直到作者手动叫停。

他们去查了审查者反复标记的那些代码是谁写的:用 git blame 追下去,约 59% 是在审查者第一轮时就已经在那里的代码。也就是说,审查者很少发现新 bug,它每一轮多找出的是几个旧的 bug。

更诚实的是,他们把责任归到了自己身上:团队习惯了默认跑好几轮,因为每一轮总能捞出点新东西。这在一次高风险变更上可能值,但在每个 PR 上都跑就不值了。

对策有四条:三轮封顶,超过三轮审查者会要求作者退回去重新考虑方案;前几轮之后只审查增量 diff;按 bug 类别标记,让作者一次修掉所有同类实例而不只是一行;以及已经发过的发现项不再重复。

这一段是整篇里对我冲击最大的地方。人类代码审查的失败模式通常是漏看,而 Agent 审查 Agent 的失败模式是不收敛:双方都有耐心、都能一直干活,于是这里缺的东西变成了终止条件。「多审几轮更安全」在这里是个错觉,边际收益在几轮之后变成了对历史债务的重复曝光。

而且团队承认自己也没有全部解决:他们还没有「当回滚率或事故率上升时自动收紧审批」的熔断器,工程师们希望在更多代码走向零人工合并之前先把这东西做出来。

被让出去的那一半职责

文章开头说代码审查干两件事,第二件是在团队内部建立共享理解和代码库认知。这一半在机器审查主导之后怎么办,文章到很后面才回答。

给出的答案是几个偏文化的做法:有人会让 Claude 生成一个小产物,把要改的那部分代码可视化出来,用这个人自己喜欢的方式讲给他听;团队默认在开放的 Slack 频道里干活,于是别人在建什么你能看见。原文的论证是:一个一行的文案改动几乎教不会你什么,而一个即将修改的模块的讲解能教会你很多,所以后者比逐行读琐碎的 diff 更值。

这个回答我持保留意见。它用一个由 AI 生成的产物去补一个原本发生在人与人之间的过程。讲解质量、提问与追问、以及「我知道隔壁组为什么这么设计」这类隐性知识的传递,是不是能被等价替代,文章没有给出证据。这是我认为这套流程里最值得观察的一个缺口。

「你合的,你负责」:把回滚当成一等公民

六层之外还有一层不写在图里的东西,是团队对责任的处理方式。

他们的默认规范是:你合入一个中小变更,之后它出了问题,通常由你来修。这条听起来像追责,但原文列出的几条前提把它的性质改了。变更都很小,所以好审、好查、好回滚;所有东西都可逆,特性开关、revert 和分阶段发布能在几分钟内撤销大部分错误;影响面决定规格,贵的代码自然拿到更多人和更多检查;回归通过遥测和内部使用快速浮出水面;以及最关键的,这不算追责,负责修的人恰好掌握最多上下文,而事故复盘看的是系统而不是人。

这一段解释了为什么前面那些自动层敢放开。流程之所以能把大部分阅读交给机器,是因为兜底成本被压得很低:错了能几分钟撤掉,撤掉之后有人认领。如果回滚一次要半天、要开三个会,那自动放行就是不负责任。所以这套设计里,可逆性不是附属能力,它和审查层是同一个结构的两半。

团队也写了「方向要变的时候怎么办」:Agent 让起一个事情变得很便宜,所以有些工作本来就不该落地。通常的做法是有人在频道里问一句为什么、指一个早先的决定、或者提一个更简单的路子,然后作者关掉或者改写那个 PR。因为代码写得快,关掉一个 PR 不觉得是损失。前提是这种不同方向要早说,而不是等到 PR 做完了再给一条阻塞性评论。

能直接拿走的部分

抛开 Anthropic 的规模,这套流程里有几件小事是可以明天就开始做的。

在 PR 之前:给 Agent 一些它能跑的检查,写进一份简短的 CLAUDE.md;用钩子跑格式化和 lint;本地先跑一遍 /code-review 或 /security-review;再加一份 REVIEW.md,把反复出现的审查意见沉淀进去。

在 PR 上:要求 CI;把「怎么测试的」和「怎么回滚」写进 PR 模板;先在一个仓库上加 AI 审查,调几周 REVIEW.md 再铺开;让审查努力匹配风险,常规变更快过一遍,一个漏掉的 bug 很贵的路径上深查。

关于 REVIEW.md 的写法,原文给了一条很实用的标准:好的 REVIEW.md 很短,它会写清在这个仓库里「失败长什么样」(文档仓库的失败是「它在撒谎」,配置仓库的失败是「它路由错了」),限制小问题的数量,列出该跳过的路径,并且要求证据给到文件和行号而不是从名字猜。它还会要求总结以数字开头,没什么可修的时候要明说。

写在最后

我读完这篇的主要收获,是它把「AI 审查」这件事从工具选型问题变成了流程设计问题。杠杆落在六层怎么分、审批粒度切到多细、以及谁为 Agent 写的代码签字,而不是换一个更强的审查模型。其中「Agent 的批准永远不算人的批准」和「一个批准只覆盖一个 commit」这两条,是制度设计而不是技术设计,而它们恰好是最容易被抄漏的部分。

另一面是,他们公开了自己流程的失败模式(五十轮不收敛、59% 是旧 bug、没有熔断器),这部分的价值我认为高于流程本身。一份把缺陷写出来的工程文,比一份只讲做法的工程文可信得多。

相关阅读:

By AI博士 万戈