我们之前提过,Claude Code 自己的代码大约 90% 是 Claude 写的。那么,代码产出这么快,我们怎么审?现在还有人读代码吗?要是出了问题,谁来兜底?
这篇文章介绍 Claude Code 团队的代码审查流程,以及它是怎么一步步演变过来的。从 6 月到 9 月,合并进 Claude Code 代码库的 pull request 数量翻了一倍多,而需要回滚的比例下降了 40% 以上。
我们并没有取消审查。我们做的是把大部分"读代码"的工作交给机器,围绕人的时间和注意力来做优化,并且下了很大力气,让犯错之后能快速、轻松地撤回。
重新思考代码审查的目的
当 agent 开始写代码,变更数量会迅速上涨。在 Anthropic 的 monorepo 里,每天合入的变更从今年 5 月的约 2,500 个涨到了 9 月的 30,000 多个。
Claude Code 有自己独立的仓库,不在 monorepo 里,但它承受的压力同样不小。交付速度这么快的时候,人工逐行审查即使不是完全不可能,也极其困难。
代码审查承担两项工作:
- 在上线前发现并修复问题
- 在团队内建立共享的上下文和对代码库的理解
本文主要讲第一项,第二项我们会在接近结尾时再谈。
我们认为,"有没有人读过这段代码?"只是一个粗糙的替代指标。真正该问的是:"这个变更可能出什么问题?由哪种检查(自动的还是人工的)来发现它最合适?"
问出这个问题,也改变了审查发生的时机。以前,审查是路尽头的一道关卡:写代码、开 PR、等同事来看。
现在,审查是持续进行的,更像写字时的拼写检查。大多数变更在请人来看之前,都已经被 Claude 至少审查并修复过一次。这能拦下大部分常规错误,把团队的注意力留给更高层次的问题和更严格的把关。
稀缺资源:人的时间和注意力
我们希望把人的时间和注意力留给只有人能解决的问题,其余的交给自动检查来发现。按照我们现在的变更速度,人要做的事依然多得惊人,团队几乎每天都会碰到前所未有的新挑战。
审查的代价其实有两面,都要算在人的时间里:人花在审查上的时间,以及人花在修复烂代码造成的破坏上的时间。如果前者省下来的时间只是转移到了后者,那就没有意义。
所以我们的目标是:Claude Code 各个仓库(CLI、桌面端和 Web 应用、移动端应用)里的大多数 pull request,在最多只有一个人参与的情况下合并(这个人可以就是作者),同时回滚和事故不增加。为了衡量人的时间,我们追踪每个 PR 的人工批准次数(中位数和 95 分位)、人工评论数,以及 PR 等人处理的时长。回滚率和事故率是护栏:如果它们上升,说明我们并没有省下人的时间,只是把时间挪了个地方。
一个变更的一生
一个典型的变更,从工程师的笔记本电脑(或 Slack 里的一个 Claude 会话)出发,要经过多层代码审查。
一个变更的一生
一个典型的 Claude Code 变更,从作者的机器到用户手中
逐层拆解
多层防线,而不是一道关卡
六层检查,底下还有回滚兜底
1. 在别人看之前,工作已经验证过
现在大部分审查在 PR 出现之前就发生了。我们给 Claude 一个它能实际运行的东西,比如测试、一次构建、或者一张用来和设计稿对比的截图,并要求它拿出输出结果,而不是只说"做完了"。然后由一个 subagent 用全新的上下文来审查 diff。这两个习惯都来自我们的最佳实践指南。
在开 PR 之前,我们很多人还会跑几个内部 skill:一个专门找 bug,一个精简 diff,一个做端到端的行为检查。UI 变更还会对照我们的设计规范检查一遍。
Claude Code 的创造者 Boris Cherny 说过,给 Claude 一个验证自己工作的手段,大概是最重要的一件事。他估计这能让最终结果好上两到三倍,并写道:"不同领域的验证方式各不相同。可能简单到跑一条 bash 命令,或者跑一套测试,或者在浏览器、手机模拟器里测一下应用。"
如果自动审查员反复指出同一个问题,我们就从源头上修掉: 改 skill、加一条 lint 规则,或者更新指引类文件,比如:
CLAUDE.md告诉 Claude 这个项目是什么:技术栈、约定、目标和非目标。每次审查都会加载它,所以在大型代码库里我们会让它保持简短,再从这里链接到其他文档。审查员也会指出违反其中约定的代码,以及让其中某一行变得过时的变更。REVIEW.md由代码 owner 编写,告诉审查员在他们负责的那部分代码里什么才是重点。我们会单独检查审查员是否真的遵守了它。好的REVIEW.md都很短。它们会说明在这个仓库里"出问题"长什么样(对文档来说是"内容说谎",对配置来说是"路由错了"),限制吹毛求疵的评论数量,列出要跳过的路径,并要求拿文件和行号作为证据,而不是凭一个名字去猜。它们还要求总结以一个数字开头,没有需要修的东西时就直说。
2. 每个 pull request 都有 AI 审查
每个 pull request 在人看之前都会先经过一次 Claude 审查。它会结合整个仓库和 PR 声明的意图来阅读 diff,并追问:这个变更解决了它要解决的问题吗?和系统契合吗?会不会弄坏别的东西?
对于 bug 修复,它会去找根本原因。公开版本 Code Review for Claude Code 现已在 Team 和 Enterprise 方案中正式提供,/code-review 则可以在本地运行一个轻量版。
我们推出研究预览版时,内部 PR 中收到实质性审查意见的比例从 16% 升到了 54%。超过 1,000 行的 PR 大多会有发现,小 PR 则大多干干净净地通过。管理员可以给每个仓库设置 Quick、Standard 或 Deep 三档。当漏掉一个 bug 的代价高于多花的审查时间时,Deep 档就值得。
发现的问题在人审之前,先由其他 agent 验证。 多个 agent 同时找 bug。然后另一组 agent 对每个发现投票,只有足够多的 agent 认为它确实存在,才会发出来。只涉及风格、诊断信息或清理的发现会被丢掉。这些过滤规则我们先在自己的 PR 上做过 A/B 测试才保留下来。剩下的发现以行内评论的形式出现,按严重程度排序。每条都会标上 Important、Nit 或 Pre-existing,这样一个并非由这次 PR 引入的 bug,就不会看起来像是作者的锅。
每个发现都必须得到回应。 要么修掉,要么写明理由后关闭讨论。很多时候会有 agent 在夜里接手修复,作者回来时,PR 已经又过了一轮。
3. 可以阻断合并的安全审查
安全也有自己专门的自动审查。只要 bug 审查或安全审查还有任何未解决的发现,PR 就不能合并。要么修掉,要么写一句简短理由后关闭讨论,误报就是这样清掉的。
如果后续的某个 commit 修复了问题,这条发现会自动消除。你也可以用开源的安全审查 GitHub Action 或 /security-review 命令搭出同样的流程,配置方法见 automated security reviews in Claude Code。
4. 会阻断合并的自动检查
都是常规项:lint、类型检查、全平台测试,以及一次构建。(我们写过一篇文章,讲怎么让 CI 撑住这个量级,在这里。)一个典型的 CLI pull request,在有人看之前可能已经跑了几十项检查。PR 模板会问这个变更是怎么测试的、要怎么撤回。这些可以由 Claude 来填,但要由提出这个变更的人签字确认。
5. 人工批准,按风险分级
修一处文档和改一下自动更新器,风险完全不同,所以审查方式也不同。
| 风险 | 示例 | 人工批准 |
|---|---|---|
| 低 | 文档、文案、仅内部使用的功能、放在 flag 后面的增量变更 | 自动审查和安全检查通过后无需人工批准 |
| 中 | 普通的产品变更,以及涉及缓存、自动更新器、系统提示词等敏感区域的变更 | 一人 |
| 高 | 风险最高的代码,外加一小部分始终需要两个人的路径 | 两人 |
风险等级由变更决定,而不是由作者决定。 一个分类器读取变更并判定它的风险等级。第二个分类器会追问:即使等级显示不需要,这个具体的变更是否仍然需要再多一个人看?有些路径始终需要人来看,包括 owner 列为敏感的所有内容,以及任何 CI workflow 文件。
只有受信任的作者才能走轻量路径。 只有在仓库受信任名单上的作者(大多是负责这个仓库的团队)才能走轻量路径,其他人都需要第二个人批准。agent 的批准也永远不能算作人的批准。自动审查可以放行低风险变更,但不能占掉一个需要人类的名额。
谁发起的变更,谁就要为它负责。 在 agent 写的 PR 上,指挥 agent 的那个人算作必需审查者之一,就和他亲手敲了这些代码一样。
一次批准只对应一个具体的 commit。 如果之后合入了系统无法担保的新 commit,比如 agent 的推送、或某人手动解决冲突的合并,就必须重新批准。
6. Merge queue、flag 和渐进式发布
Merge queue 会把变更应用到 main 上,再把所有检查重跑一遍,这样两个各自通过的 PR 就不会组合出一个坏掉的 main。新行为通常先放在 flag 后面,发给 Anthropic 员工使用,我们的高强度使用往往能在用户之前发现回归问题。公开发布先进入 latest 渠道,stable 渠道落后于它。
7. 快速发现,快速回滚
总会有些错误漏过去,所以最后一层的重点是尽快发现它们。Flag 不用部署就能关掉。Claude Code 有每周轮换的 on-call,事故复盘时会追问:本该由哪一层拦下这个问题?
发现一次糟糕的合并,目前仍然要靠作者和 on-call,以及回滚和事故看板。我们还没有在这些数字异动时自动收紧审批的熔断机制;在更多代码转向零人工合并之前,这里的工程师们希望先有它。
一位同事说过,放慢速度确实能降低错误率,但主要是因为你什么都做得更少了。我们宁愿在发现和修复问题上变得更快。
我们如何让审查员变得更好
我们的代码审查流程在一个循环中持续改进。
每一次事故现在都成了一项 eval。 团队收集了过去引发事故的 PR,连同对应的前向修复和回滚,并请熟悉那部分代码的工程师确认每个 bug 以及它所在的位置。审查员每天都会在这个数据集上跑一遍。
我们的事故就是 eval
由过往事故和回滚构建,每天运行
我们通过观察人们如何处理评论来衡量噪音。 只要不介意标出一大堆不是 bug 的东西,抓到更多 bug 是很容易的,所以必须有个制衡。我们把审查员留下的每条评论,按 PR 上的人如何回应来分类:修了、说它错了、说这个行为是有意为之、或者认为无关紧要。我们也会在已知干净的 PR 上跑审查员,统计它有多少次报告的内容会诱导人做出有害的改动。我们白纸黑字定下的优先级是:首先抓住真正的 bug,其次减少审查轮数。
当作者是 agent 时,噪音的代价更高。人几秒钟就能把一个吹毛求疵的意见挥手打发掉。agent 却会去修它,也就意味着多一个 commit,多一轮审查。
审查必须收敛。 agent 作者和 agent 审查员可以无休止地互相找活儿干。有一个 CLI PR 在一夜之间经历了五十多轮审查和修复,直到作者把它停下来。
我们对审查员反复指出的问题跑了 git blame,发现其中约 59% 是在审查员第一轮时就已存在的代码。审查员很少发现新 bug,它只是每一轮多挖出几个老 bug。
这一点我们自己也有责任。我们已经习惯了默认跑好几轮,因为每一轮都能抓到点新东西。对高风险的变更这么做也许值得,但不是每个 PR 都需要。团队现在把"通过所需轮数"和召回率一起追踪,并改了复审方式:
- 轮数上限。 三轮之后,审查员会请作者退一步,重新考虑整体思路。
- 前几轮之后只复审 diff。
- 指出 bug 的类别, 让作者修掉所有同类问题,而不是只改一行。
- 不重复 已经发过的发现。
审查 agent 还会每晚扫一遍现有代码,帮我们在老 bug 冒出来之前抓住它们。
会收敛的审查
内部 pull request,收敛改动前后对比
人在哪些环节保留参与
把一位资深工程师从深度工作中拉出来,只为了抓一个常规错误,代价远高于一次自动审查。在 Anthropic,我们的工作已经从阅读每一个 diff,转向了机器不擅长的决策:
- 这东西到底该不该做? 方向、品味和范围。有个团队宣布已经解决了审查瓶颈,但仍然对团队外部提交的变更保留人工审查,好让有人来问一句"我们真的该做这个吗?"
- 高风险代码,比如计费、自动更新器和 CI workflow。
- 架构,那些会被别人照搬的模式。
- 解读证据。 测试输出、跑了什么命令、返回了什么、截图、审查员的发现。如果你说不出这些检查会漏掉什么,就应该要求一次真正的审查,而不是草草批准。
- 对结果负责。 每一个上线的变更都有一个人为它负责,不管代码是谁敲的。
由人做决定的地方
五类决策留给人,其余地方都由检查覆盖
在团队内共享上下文
代码审查的第二项工作,一直是传播对代码库的理解。读队友的 diff,就是你了解什么变了、各部分如何拼在一起的方式。如果大部分阅读都交给机器,那人要怎么了解代码?
这件事我们没有正式的流程。团队里没有人在脑子里装着 Claude Code 的完整心智模型,我们也不认为有人需要这样。当有人想了解代码库的某个部分时,他们会让 Claude 专门针对那一块写一份讲解。这只需要几分钟,而且反映的是代码当下的样子。

我们中有些人会更进一步,让 Claude 做一个小工件,把那部分代码可视化,并按自己喜欢的学习方式来讲解。
如果目标是把人的注意力用在刀刃上,这比逐行阅读琐碎的 diff 强得多。一处单行文案改动几乎教不了你什么,而一份针对你即将修改的模块的讲解,能教会你很多。我们默认在公开 Slack 频道里工作的文化也有帮助,因为别人在做什么,你能在他们做的时候就看到。
"你合进去的,你负责"
如果你合入了一个小型或中型变更,后来还是出了问题,按照团队惯例,通常由你来修。有几件事能保证这不会变成坏事:
- 变更都很小, 所以容易审查、调试和回滚。
- 一切都可撤回。 flag、回滚和分阶段发布能在几分钟内撤销大部分错误。
- 影响范围决定门槛。 代价高的代码会有更多人、更多检查。
- 回归问题能很快暴露, 靠的是遥测和内部使用。
- 这不是追责。 由你来修,是因为你掌握的上下文最多;事故复盘关注的是系统。
责任也是共担的。团队里很多人都在公开频道里工作,任何人都能看到别人在做什么,可以接过一条讨论线,或者让一个 Claude 去补某个空缺,反馈也会来自并不负责那段代码的人。正是这种开放,让我们在不对每一行设卡的情况下依然能保持高质量。
当我们改变方向时
agent 让开始一件事变得很便宜,所以有些工作本就不该合入。通常频道里会有人问为什么,指出之前的某个决定,或者建议一条更简单的路,然后作者就会关掉或重做这个 PR。
因为代码写得很快,关掉 PR 并不会让人觉得是损失。诀窍在于,要在讨论里尽早提出不同的方向,而不是在一个已经完成的 PR 上留一条阻断性的评论。
如何在你的团队采用这套流程
要在你的团队里做到这些,大部分组件今天就已经有了。
在 pull request 之前
- 给你的 agent 一些它能运行的检查,列在一份简短的
CLAUDE.md里,再加上运行 formatter 和 linter 的 hook。 - 在本地运行
/code-review和/security-review,或者用一个全新上下文的 subagent 做审查。 - 添加一份
REVIEW.md,把反复出现的审查意见写进去。
在每个 pull request 上
- 强制要求 CI 通过,并在 PR 模板里加上"Testing"、"Risk"和"How to revert"。
- 先在一个仓库上加 AI 审查员(Code Review for Claude Code,或 Claude Code GitHub Action),花几周调好
REVIEW.md,再推广开。 - 加上自动安全审查,并要求合并前所有审查讨论都已解决。
- 让审查力度和风险匹配:常规变更快速过一遍,漏掉 bug 代价高的地方深入审查(Code Review 中的 Quick、Standard 或 Deep)。
- 限制审查轮数;前几轮之后,只复审新的 diff。
批准
- 把风险等级写下来,由变更而不是作者来决定;一开始从严,有了证据再放宽。
- 把轻量批准路径留给你信任的作者。
- 用 CODEOWNERS 和分支保护,确保认证、计费、CI 和发布 workflow、安装程序始终需要人来批准。
- 绝不让 bot 的批准顶替必需的人工批准;有新 commit 时撤销已有批准。
合并及之后
- 使用 merge queue、feature flag、先内部使用、分阶段发布和一键回滚。
- 把事故和回滚整理成 eval 集,每当审查员的提示词、工具或模型变化时重新打分。
- 衡量人的时间,以回滚率和事故率作为护栏。
未来的方向
Code Review 的发布公告说过,批准一个 pull request 仍然是人的决定;产品只发布发现,不给出批准。我们自己的仓库走得更远,让 owner 决定哪些低风险变更可以仅凭自动检查合并,而这条线还在不断移动。
当一次审查漏掉了什么,我们会问:模型当时缺了什么信息?然后把修复写进 CLAUDE.md、一个 skill 或一项新检查里。目标始终没变:每个变更花的人力时间更少,回滚不比以前多。
如果你的团队也让 agent 开 pull request,我们很想听听你们自动化了哪些检查、哪些仍然需要人来做,以及你们觉得我们哪里做错了。
感谢 Emma Webb、Michael Segner 和 Amie Rotherham 的审阅。