跳到正文
北京时间
原文
Anthropic:Claude.dev 开发者博客· Addy Osmani, Morgan Westlee Lunt, and Qing Wang·· 16 小时前精选AI 评分77

Claude Code 团队如何做代码审查

How the Claude Code team does code review

AI 导读

Anthropic 团队介绍 Claude Code 的代码审查流程,机器承担大部分阅读,人类注意力集中在高风险决策。6 月至 9 月合并 PR 翻倍以上,回退率下降超过 40%;monorepo 每日变更量从 5 月约 2,500 增至 9 月超过 30,000。

推荐理由

Claude Code 团队亲述自家六层代码审查流程与衡量指标,读者可对照调整自己团队的 AI 审查与人工介入方式。

正文 · AI 翻译

我们之前说过,Claude 编写了 Claude Code 自身约 90% 的代码。那么,在我们产出如此庞大的代码量下,我们如何审查代码?现在还有人读代码吗?如果出了问题,谁来发现?

这篇文章介绍了 Claude Code 团队的代码审查流程及其演变过程。从六月到九月,合并到 Claude Code 代码库的拉取请求数量翻了一倍多,而需要回滚的比例下降了 40% 以上。

我们并没有取消审查。但我们确实把大部分阅读工作交给了机器,以优化人类的时间和注意力,并大力投入让撤销错误变得快速而简单。

重新思考代码审查的目的

当智能体编写代码时,变更数量会迅速增长。在 Anthropic 的 monorepo 中,每天落地的变更从今年五月的约 2,500 个增加到九月的 30,000 多个。

Claude Code 拥有自己独立的代码库,与 monorepo 分开,但它也面临着巨大的压力。当你以如此快的速度交付时,手动逐行审查代码即使并非不可能,也是极其困难的。

代码审查有两个作用:

  1. 在代码发布前发现并修复问题
  2. 在团队中建立共享上下文和对代码库的理解

本文主要关注发现和修复问题,但我们会在结尾回到第二个作用。

我们认为,“有没有人读过代码?”并不能精确地代表“这次变更可能出什么问题,哪种检查——自动的还是人工的——才是发现它的正确方式?”

提出这个问题也改变了审查发生的时机。以前,审查是道路尽头的关卡:写代码、开 PR、等同事。

现在,审查持续运行,更像是写作时的拼写检查。大多数变更在请人查看之前,已经由 Claude 至少审查和修复过一次。这能捕获大多数常规错误,并将团队注意力留给更高层次的问题和审查。

稀缺资源:人类的时间和注意力

我们希望将人类的时间和注意力保留给只有人类才能解决的问题,并确保自动化检查能捕获其余问题。在我们当前的变更速度下,人类仍有大量工作要做,我们的团队几乎每天都会遇到新的、前所未有的挑战。

人类时间涵盖了审查成本的两方面:人们花在审查上的时间,以及他们花在修复劣质代码所破坏的东西上的时间。如果削减前者只是让时间出现在后者上,那就没有帮助。

因此,目标是 Claude Code 代码库(CLI、桌面和 Web 应用以及移动应用)中的大多数拉取请求在合并时,最多只需一个人参与,这个人可以是作者,且回滚或事故不增加。为了衡量人类时间,我们追踪每个 PR 的人工批准数(中位数和第 95 百分位数)、人工评论数,以及 PR 等待某人的时长。回滚率和事故率是护栏:如果它们上升,我们就没有节省人类时间,只是把它转移了。

一次变更的生命周期

一次典型的变更,从工程师的笔记本电脑(或 Slack 中的 Claude 会话)出发,经过多层代码审查。

一次变更的生命周期

一次典型的 Claude Code 变更,从作者的机器到用户

图 A大部分路径是自动化的;人的注意力用在风险所在之处

逐层解析

是层层把关,而非一道关卡

六层检查,其下还有回滚机制

图 B每一层都能捕获一些问题,也会漏掉另一些。很少有变更能通过所有层,剩下的由回滚处理。

1. 工作成果在任何人查看之前就已得到验证

如今大多数审查都发生在 PR 存在之前。我们给 Claude 一些可以运行的东西——比如测试、构建,或一张用来与设计稿对比的截图——并要求它展示输出结果,而不是只说完成了。然后一个子代理会用全新的上下文审查 diff。这两个习惯都来自我们的最佳实践指南。

在打开 PR 之前,我们中的许多人还会运行一些内部技能:一个查找 bug,一个简化 diff,一个端到端检查行为。UI 变更也会对照我们的设计准则进行检查。

创建 Claude Code 的 Boris Cherny 曾表示,给 Claude 一种验证其工作成果的方式可能是获得好结果最重要的事。他估计这能让最终结果好上两到三倍,并写道:“验证在每个领域看起来都不一样。它可能简单到运行一条 bash 命令,或运行一个测试套件,或在浏览器或手机模拟器中测试应用。”

当自动审查器反复标记同一件事时,我们从源头修复它:在技能中、用 lint 规则,或更新诸如以下这类说明性文件:

  • CLAUDE.md 告诉 Claude 这个项目是什么:技术栈、约定、目标和非目标。每次审查都会加载它,所以在大型代码库中我们让它保持简短,并从那里指向其他文档。审查器还会标记违反其中约定的代码,以及让其中某一行过时的变更。
  • REVIEW.md 由代码所有者编写,告诉审查器在他们负责的那部分代码中什么才是重要的。我们会单独检查审查器是否真的遵循它。好的那些都很简短。它们说明在那个仓库中失败是什么样子(对文档来说是“它在撒谎”,对配置来说是“它路由错了”),限制琐碎意见的数量,列出要跳过的路径,并要求以文件和行号作为证据,而不是根据名称猜测。它们还要求一份以计数开头的摘要,并在没有需要修复的内容时直说。

2. 每个拉取请求都进行 AI 审查

每个拉取请求在有人查看之前都会先得到 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 是值得的。

发现会在人类审查之前由其他代理验证。多个代理同时查找 bug。然后一个单独的小组对每个发现进行投票,只有当足够多的代理同意它是真实的时,它才会被发布。仅涉及风格、诊断或清理的发现会被丢弃。我们在自己的 PR 上对这些过滤器进行了 A/B 测试,然后才保留它们。剩下的会以按严重程度排序的行内评论形式出现。每个都被标记为 Important、Nit 或 Pre-existing,这样 PR 没有引入的 bug 就不会看起来像是作者的错。

每个发现都必须得到回应。你要么修复它,要么带着理由关闭该讨论串。通常智能体会在夜间接手修复,所以作者回来时会看到一个已经过另一轮处理的 PR。

3. 可阻断的安全审查

安全也有自己的自动化审查。只要缺陷审查或安全审查中还有任何未关闭的发现,PR 就不能合并。你要么修复它,要么用简短的理由关闭讨论串,这就是清除误报的方式。

如果后续提交修复了该问题,该发现会自动清除。你可以用开源安全审查 GitHub Action 或 /security-review 命令设置同样的机制。设置方法见Claude Code 中的自动化安全审查。

4. 可阻断的自动化检查

常规的那些:lint、类型检查、各平台上的测试,以及构建。(我们写过如何为这种规模扩展 CI,见此处。)一个典型的 CLI 拉取请求在任何人查看之前可能会运行几十项检查。PR 模板会询问该改动是如何测试的,以及如何撤销。Claude 可以填写这些内容,但提出该改动的人要签字确认。

5. 按风险分级的人工批准

文档修复和自动更新器改动带来的风险不同,因此审查方式也不同。

风险示例人工批准
低文档、文案、仅内部功能、标志位后的增量改动一旦自动化审查和安全检查通过,无需批准
中普通产品改动,以及对缓存、自动更新器或系统提示等敏感区域的改动一人
高风险最高的代码,以及一小部分始终需要两人的路径两人

决定层级的是改动本身,而不是作者。一个分类器读取改动并选定其风险层级。第二个分类器则判断这个具体改动是否需要另一个人,即使其层级表明不需要。有些路径始终需要一个人,包括所有者列为敏感的任何内容以及任何 CI 工作流文件。

只有受信任的作者才能走较轻的路径。只有仓库受信任名单上的作者才能走较轻的路径,主要是拥有该仓库的团队。其他所有人都需要第二个人批准。智能体的批准也永远不算作人的批准。自动化审查可以放行低风险改动,但无法填补需要人工的位置。

谁发起改动,谁就要对它负责。在智能体编写的 PR 上,指挥智能体的人算作所需审查者之一,就如同他们自己敲了这些代码一样。

一次批准只覆盖一个特定提交。如果新的提交落地而系统无法为其担保,比如智能体的推送或有人手动解决的合并,就必须重新批准。

6. 合并队列、标志位和逐步发布

合并队列会在应用改动后针对 main 重新运行每一项检查,这样两个各自通过的 PR 就不会合并成一个损坏的 main。新行为通常会先通过标志位发布给 Anthropic 员工,而我们的大量使用往往会在用户之前发现回归问题。公开发布先进入 latest 渠道。stable 渠道随后跟进。

7. 快速检测,快速回滚

有些错误仍会漏过去,所以最后一层就是快速捕获它们。标志位无需部署即可关闭。Claude Code 有每周轮值的 on-call,事故复盘会追问哪一层本应捕获该问题。

发现糟糕的合并仍然依赖于作者和值班人员,以及回滚和事故仪表盘。我们还没有在那些数字变动时收紧审批的熔断机制;这里的工程师希望在更多代码转向零人工合并之前拥有它们。

一位同事说,放慢速度确实能降低错误率,但主要是因为你在所有事情上做得更少。我们更愿意在发现和修复问题上变得更快。

我们如何让审查者变得更好

我们的代码审查流程在一个循环中不断改进。

任何事故现在都成为一项评估。团队收集了过去导致事故的 PR,连同它们的向前修复和回滚,并让了解那段代码的工程师确认每个 bug 及其所在位置。审查者每天针对该集合运行。

我们的事故就是评估集

由过去的事故和回滚构建,每天运行

图 C评估集随着每次事故而增长,对审查者的每一次更改都会针对全部评估集进行评分。

我们通过观察人们对评论的反应来衡量噪音。如果你不介意标记大量非 bug,就很容易捕获更多 bug,所以我们需要对此进行检查。我们根据 PR 上的人如何回应来对审查者留下的每条评论进行分类:他们修复了它、说它是错的、说该行为是有意为之,或者认为它无关紧要。我们还在已知干净的 PR 上运行审查者,并统计它报告会导致某人做出有害更改的情况的频率。我们写下来的优先事项是:首先捕获真正的 bug,其次减少审查轮次。

当作者是智能体时,噪音的代价更高。人可以几秒钟就打发掉一个吹毛求疵的意见。智能体会修复它,这意味着又一次提交和又一轮审查。

审查必须收敛。智能体作者和智能体审查者可以让彼此无限期地忙碌下去。一个 CLI PR 在一夜之间经历了五十多轮审查和修复,直到其作者叫停。

当我们对审查者一直标记的内容运行 git blame 时,其中约 59% 是自审查者第一轮以来就存在的代码。审查者很少发现新 bug,它每轮都在发现一些更旧的 bug。

我们自己让它变得更糟。我们已经习惯于默认运行几轮,因为每一轮都会捕获一些新东西。这在有风险的更改上可能值得,但不是每个 PR 都值得。团队现在将通过所需轮次与召回率一起跟踪,并改变了重新审查方式:

  • 轮次上限。三轮之后,审查者会要求作者退后一步,重新考虑方案。
  • 前几轮之后仅对 diff 重新审查。
  • 标记 bug 类别,以便作者修复每一个实例,而不是一行。
  • 不重复已发布的发现。

审查智能体还会每晚对现有代码运行,帮助我们在旧 bug 出现在其他地方之前捕获它们。

收敛的审查

内部拉取请求,在收敛更改之前和之后

图 D收敛更改后,每个内部拉取请求的审查轮次更少

人类在何处保持参与

把一位资深工程师从深度工作中拉出来去捕获一个常规错误,代价远高于自动审查。在 Anthropic,我们的工作已从阅读每一个 diff 转变为处理机器不擅长的决策:

  • 我们到底该不该做这个?方向、品味和范围。有一个团队宣称其评审瓶颈已经解决,但对于来自团队之外的变更仍然保留人工评审,所以有人会问“我们到底该不该做这个?”
  • 高风险代码,例如计费、自动更新器和 CI 工作流。
  • 架构,即会为他人树立范式的部分。
  • 阅读证据。测试输出、所运行的命令及其返回结果、截图、评审者的发现。如果你说不出这些检查会漏掉什么,那就要求一次真正的评审,而不是快速批准。
  • 对结果负责。每一个上线的变更都要有一个人负责,无论代码是谁敲的。

由人决定的地方

五项决策仍由人来做;其余所有地方都运行检查

图 E人们从阅读每一份 diff 转向机器不擅长的决策

在团队中共享上下文

代码评审的第二项职责一直是传播对代码库的理解。阅读队友的 diff 是你了解发生了什么变化、各部分如何拼合的方式。如果大部分阅读都由机器完成,那大家还怎么学习代码?

我们没有为此设立正式流程。团队里没有人脑中保有 Claude Code 的完整心智模型,我们也认为没人需要这样。当有人想理解代码库的某一部分时,他们就让 Claude 针对那一小块写一份讲解。这只需几分钟,而且反映的是代码当下的样子。

Diagram titled "If machines do most of the reading, how does anyone learn the code?" On the left, a grid stands for the codebase, with a few squares highlighted as managed settings, the part you want to understand; nobody keeps a complete mental model of it in their head. An arrow labeled "Ask Claude about exactly that slice", with the example question "How does Claude Code retrieve managed settings?", leads to two outcomes: Claude writes an explainer, which takes minutes and reflects the code as it is today; and, for some, Claude builds a small artifact that visualizes that part of the code and teaches it the way that person likes to learn. Along the bottom: reading trivial diffs line by line, a one-line copy change teaches you almost nothing; an explainer for the module you are about to change teaches you a lot.

我们中有些人更进一步,让 Claude 构建一个小工件,把代码的那部分可视化出来,并按那个人喜欢的学习方式来讲解。

如果目标是把人的注意力花在刀刃上,这比逐行阅读琐碎的 diff 要好。一行文案改动几乎教不了你什么。而针对你即将修改的模块的讲解能教给你很多。我们默认在开放的 Slack 频道里工作的文化也有帮助,因为你能在别人构建时看到他们在做什么。

“你合入,你负责”

如果你合入了一个小型或中型变更,而之后仍然出了问题,团队惯例通常是由你来修复。有几件事可以防止这变得糟糕:

  • 变更很小,因此容易评审、调试和回滚。
  • 一切皆可逆。功能开关、回滚和分阶段发布能在几分钟内撤销大多数错误。
  • 影响范围决定门槛。代价高昂的代码需要更多人、更多检查。
  • 回归会很快浮现,通过遥测和内部使用。
  • 这不是追责。负责修复意味着你掌握最多上下文;事故复盘看的是系统。

责任也是共享的。团队中很多人工作在开放频道里,任何人都能看到别人在构建什么、接上一个话题,或让 Claude 去填补某个空白,反馈也来自并不拥有这段代码的人。这种开放性是质量在没有对每一行设关卡的情况下仍能保持高水平的重要原因。

当我们改变方向时

智能体让启动事情变得很便宜,所以有些工作不该合入。通常频道里会有人问为什么、指出早先的某个决定,或建议一条更简单的路径,然后作者关闭或重塑这个 PR。

既然代码写起来很快,关闭 PR 就不会让人觉得是损失。诀窍是在话题里尽早提出不同的方向,而不是在一个已完成的 PR 上作为阻塞性评论提出。

你如何采用这套流程

与你的团队一起做这件事所需的大部分组件今天都已存在。

在拉取请求之前

  • 给你的 agent 提供它可以运行的检查,列在一个简短的 CLAUDE.md 中,再加上运行格式化和 linter 的 hooks。
  • 在本地运行 /code-review 和 /security-review,或者进行一次全新上下文的子 agent 审查。
  • 添加一个 REVIEW.md,并把重复出现的审查意见移入其中。

在每个 pull request 上

  • 要求 CI,并把 "Testing"、"Risk" 和 "How to revert" 加入你的 PR 模板。
  • 在一个仓库上添加 AI 审查者(Code Review for Claude Code,或 Claude Code GitHub Action),用几周时间调优 REVIEW.md,然后再扩大范围。
  • 添加自动化安全审查,并要求每个审查线程在合并前都已解决。
  • 让审查力度与风险匹配:对常规变更快速过一遍,对漏掉 bug 代价高昂的地方深入审查(Code Review 中的 Quick、Standard 或 Deep)。
  • 限制审查轮数;前几轮之后,只重新审查新的 diff。

审批

  • 写下风险层级,根据变更而非作者来设定;从严开始,再根据证据放宽。
  • 对你信任的作者保留更轻量的审批路径。
  • 使用 CODEOWNERS 和分支保护,让 auth、billing、CI 和发布工作流以及安装程序始终需要真人参与。
  • 绝不让 bot 的审批替代必需的人工审批;在新提交上撤销审批。

合并及之后

  • 使用 merge queue、feature flags、先内部使用、分阶段发布和一步回滚。
  • 把事故和回滚转化为 eval set,并在审查者的 prompts、工具或模型变化时重新评分。
  • 衡量人工时间,并以回滚率和事故率作为护栏。

走向何方

Code Review 的 announcement 表示,批准 pull request 仍然是人的决定;该产品发布的是发现,而不是批准。我们自己的仓库走得更远,让 owner 决定哪些低风险变更仅凭自动化检查即可合并,而这条界线还在不断移动。

当审查漏掉某些东西时,我们会问模型当时面前缺少什么,并把修复放进 CLAUDE.md、skill 或新的检查中。目标没有改变:每次变更花费更少的人工时间,并且回滚不比以前更多。

如果你的团队让 agent 打开 pull request,我们想听听你自动化了哪些检查、哪些仍然需要真人,以及你认为我们哪里做错了。

感谢 Emma Webb、Michael Segner 和 Amie Rotherham 的审阅。

来源:Anthropic:Claude.dev 开发者博客 · claude.dev