跳到正文
Anthropic:Claude.dev 开发者博客· Addy Osmani, Morgan Westlee Lunt, and Qing Wang·· 14 天前精选AI 评分76

Claude Code 团队如何做代码审查:六层检查与风险分级审批

How the Claude Code team does code review

AI 导读

Anthropic Claude Code 团队详解在 Claude 写约 90% 代码的情况下如何组织代码审查:审查从终点关卡变为持续进行,每个 PR 经过验证、必需 CI、AI 审查、安全审查、按风险分级的人工审批和合并队列六层检查,再辅以快速回滚。

推荐理由

Anthropic 自家团队复盘 Claude 代码审查流程,给出分层检查、风险分级审批等可直接迁移的落地做法和真实收敛数据。

正文 · 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 变更,从作者的机器到用户

一个拉取请求的检查项,从上到下。在拉取请求之前,工作已经过验证。对每个拉取请求:必需的 CI、Claude 代码审查和安全审查,全部自动化,然后是基于风险的审批,由人来做决定。接着是合并队列,合并之后先内部使用的功能开关、逐步发布,最后一步是观察、向前修复或回滚。

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

逐层来看

是分层,不是一道闸门

六层检查,回滚在其之下

六层从上到下堆叠:验证、必需的 CI、AI 审查、安全审查、人工审批,以及合并队列加功能开关。每一层在不同地方都有漏洞。从某个漏洞漏过的问题会被后面的层接住;只有一个问题穿过每一层的漏洞,才会落到最底部的快速回滚。

图 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 是值得的。

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

每条发现都必须得到回应。你要么修复它,要么说明理由并解决该讨论串。通常会有 agent 在夜间接手修复,因此作者回来时会看到一个已经过另一轮处理的 PR。

3. 可阻止合并的安全评审

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

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

4. 可阻止合并的自动化检查

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

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

文档修复和自动更新器变更的风险不同,因此评审方式也不同。

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

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

只有受信任的作者才能走更轻量的路径。只有仓库受信任列表上的作者才能走更轻量的路径,主要是拥有该仓库的团队。其他所有人都需要第二个人批准。agent 的批准也永远不能算作人工批准。自动化评审可以放行低风险变更,但不能填补需要人工的名额。

提出变更的人要对其负责。在 agent 编写的 PR 上,指导 agent 的人算作所需评审人之一,就如同他们自己编写了代码一样。

一次批准只覆盖一个特定提交。如果之后出现了系统无法担保的新提交,比如 agent 推送的提交或有人手动解决的合并,就必须重新批准。

6. 合并队列、功能开关与渐进式发布

合并队列会在应用变更后针对 main 重新运行每一项检查,因此两个单独通过的 PR 不会合并成一个坏掉的 main。新行为通常先通过功能开关只对 Anthropic 员工开放,而我们的大量使用往往能在用户之前暴露回归问题。公开发布先进入 latest 渠道,stable 渠道随后跟进。

7. 快速检测,快速回滚

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

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

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

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

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

任何事故现在都是一项 eval。团队收集了过去引发事故的 PR,连同它们的修复前推和回滚,并让熟悉那段代码的工程师确认每个 bug 及其所在位置。审查者每天针对该集合运行。

我们的事故就是 eval

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

一次事故或回滚会变成一个经专家检查的用例,并被加入 eval 集合。对审查者的改动会进入每日 eval 运行,针对每个用例评分;分数上升就保留该改动,否则丢弃,然后循环从下一个改动重新开始。

图 Ceval 随每次事故增长,对审查者的每一次改动都会针对全部用例评分。

我们通过观察人们如何处理评论来衡量噪音。如果你不介意标记大量非 bug,多抓 bug 很容易,所以我们需要对此加以检查。我们按 PR 上的人如何回应来对审查者留下的每条评论分类:他们修复了它、说它是错的、说该行为是有意为之,或认为它无关紧要。我们还会在已知干净的 PR 上运行审查者,并统计它报告了多少次本会引导人做出有害改动的问题。我们写明的优先级是:先抓真正的 bug,其次减少审查轮次。

当作者是 agent 时,噪音的代价更高。人可以几秒钟内挥开一个吹毛求疵的意见。agent 会去修复它,这意味着又一个提交和又一轮审查。

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

当我们对审查者反复标记的内容运行 git blame 时,约 59% 是审查者第一轮时就已存在的代码。审查者很少发现新 bug,它每轮只是发现几个更旧的 bug。

我们自己把它弄得更糟了。我们习惯了默认跑好几轮,因为每一轮都能发现新问题。对于有风险的改动,这或许值得,但不是每个 PR 都值得。团队现在除了召回率之外,还跟踪通过所需的轮数,并改变了复审方式:

  • 轮数上限。三轮之后,审查者会请作者退一步,重新考虑方案。
  • 仅针对 diff 的复审,在前几轮之后进行。
  • 标记缺陷类别,让作者修复每一处实例,而不是只改一行。
  • 不重复已经发布过的发现。

审查智能体还会每晚对现有代码运行,帮助我们在旧缺陷出现在别处之前发现它们。

趋于收敛的审查

内部拉取请求,在收敛改动之前与之后

收敛工作前后对比:经过一轮审查和修复就通过的拉取请求从 65% 升至 78%;需要五轮或更多轮的拉取请求从 8.9% 降至 1.5%;通过所需的平均审查次数从 2.08 降至 1.32。

图 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 的阻塞性评论。

你如何在团队中采用这一流程

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

在拉取请求之前

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

在每个拉取请求上

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

批准

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

合并及之后

  • 使用合并队列、功能开关、先内部使用、分阶段发布和一步回滚。
  • 将事故和回滚转化为评估集,并在审查者的提示、工具或模型变化时重新评分。
  • 衡量人工时间,以回滚率和事故率作为护栏。

这件事的走向

Code Review 的公告表示,批准拉取请求仍然是人的决定;该产品发布发现,而非批准。我们自己的仓库走得更远,让所有者决定哪些低风险变更仅凭自动化检查即可合并,而这条线还在不断移动。

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

如果你的团队允许智能体发起拉取请求,我们想了解你们已经自动化了哪些检查、哪些仍然需要人工处理,以及你认为我们哪里做得不对。

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

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