跳到正文
Hacker News· pretext·· 17 天前精选AI 评分69

Claude Code 团队如何做代码评审

How the Claude Code team does code review

AI 导读

Anthropic 公开 Claude Code 团队的代码评审流程,约 90% 的代码由 Claude 自身编写。流程覆盖 7 层机制:执行前验证、AI 评审、安全审查、自动化检查、按风险分级的人工批准、合并队列与灰度发布、快速回滚。

推荐理由

Anthropic 把 AI 主导环境下七层代码评审流水线与具体指标一并公开,读者可据此评估哪些环节适合引入自己的团队。

正文 · AI 翻译

我们此前说过,Claude Code 的代码大约有 90% 是由 Claude 自己编写的。那么,面对如此庞大的产出量,我们如何进行代码审查?现在还有人真正读代码吗?如果出了问题,谁来兜底?

本文将介绍 Claude Code 团队的代码审查流程及其演变过程。从六月到九月,合并到 Claude Code 代码库的拉取请求增加了一倍以上,而需要回退的比例下降了超过 40%。

我们并没有取消审查。但我们确实把大部分"阅读"工作转移给了机器,优化了人的时间和精力,并在快速、便捷地撤销错误方面投入了大量精力。

重新审视代码审查的目的

当智能体开始编写代码后,变更数量会迅速增加。在 Anthropic 的 monorepo 中,每天的落地变更从今年 5 月的约 2,500 项增长到了 9 月的 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 审查。它会对照整个仓库和拉取请求所声明的意图来阅读差异,并追问:这次改动是否解决了它要解决的问题,是否契合系统,还是破坏了其他东西?

对于缺陷修复,它会寻找根本原因。公开版本 Code Review for Claude Code 现已在 Team 和 Enterprise 套餐中正式上线,/code-review 则在本地运行一个更轻量的版本。

当我们 推出研究预览版 时,内部拉取请求中获得实质性审查意见的比例从 16% 上升到 54%。大多数超过 1,000 行的拉取请求都会收到反馈,而大多数小型拉取请求则会干净通过。管理员可将每个仓库设置为 Quick、Standard 或 Deep。当一个被遗漏的 bug 的代价超过额外审查时间时,Deep 就值得使用。

反馈结果会在人类审查之前先由其他智能体进行验证。多个智能体同时寻找 bug。然后一个独立的群体对每条反馈进行投票,只有在足够多的智能体认定它确实存在时才会发布。仅涉及风格、诊断或清理的反馈会被丢弃。我们在保留这些筛选条件之前,先在我们自己的拉取请求上做了 A/B 测试。最终留下来的以行内评论的形式按严重程度排序呈现。每条评论都会打上 Important、Nit 或 Pre-existing 标签,这样拉取请求未引入的 bug 看起来就不是作者的责任了。

每条问题都必须处理。你要么修复它,要么说明理由并关闭该讨论。代理常常会在夜间接手修复,所以作者回来时 PR 已经又经过了一轮。

3. 可以阻塞合并的安全审查

安全审查也有自己的自动化检查。只要 bug 审查或安全审查中存在任何未关闭的发现,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 都该这样。团队现在把通过轮次与召回率一起追踪,并修改了复审方式:

  • 轮次上限。 三轮之后,评审器会请作者退一步,重新考虑方案。
  • 前几轮之后进行仅差异复审。
  • 标记 bug 的类别,这样作者就能修复所有实例,而不仅仅是一行。
  • 已经发布过的发现不再重复。

评审智能体也会每晚在已有代码上运行,帮我们在旧 bug 出现在别处之前抓住它们。

收敛的评审

内部 pull request,收敛改动前后

图 D收敛改动之后,每个内部 pull request 的评审轮次更少

人类在环中的位置

把资深工程师从深度工作中拉出来抓一个常规错误,代价远高于自动化评审。在 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,再加上运行格式化和 lint 工具的 hooks。
  • 本地运行 /code-review 和 /security-review,或者让一个全新上下文的子智能体进行评审。
  • 添加一个 REVIEW.md,把反复出现的评审意见移进去。

每次 Pull Request

  • 要求跑 CI,并在 PR 模板中加上"测试"、"风险"和"如何回退"。
  • 在一个仓库里加入 AI 评审(Code Review for Claude Code,或 Claude Code GitHub Action),先花几周调优 REVIEW.md,再扩大范围。
  • 加入自动化安全评审,并要求每条评审线程在合并前都已解决。
  • 让评审力度与风险相匹配:常规改动快速过,漏掉一个 bug 代价高昂的则深入评审(Code Review 中的快速、标准或深度)。
  • 限制评审轮次;前几轮之后,只重新评审新增的 diff。

审批

  • 写下风险分级,按改动而非作者来定;先从严,再用证据逐步放宽。
  • 为你信任的作者保留更轻量的审批路径。
  • 使用 CODEOWNERS 和分支保护,让 auth、billing、CI 和发布工作流,以及安装脚本始终需要人工。
  • 绝不让 bot 的审批顶替必需的人工审批;新提交后撤销既有审批。

合并及之后

  • 使用合并队列、功能开关、先内部使用、分阶段发布和一步回退。
  • 把事故和回退做成评估集,每当评审器的提示词、工具或模型变化就重新打分。
  • 以人工耗时作为指标,用回退率和事故率作为护栏。

走向何方

Code Review 的 公告 提到,批准 pull request 仍是人工的决定;产品只发布发现,不做批准。我们自己的仓库更进一步,让维护者决定哪些低风险改动可以仅凭自动化检查合并,这条线还在向前推进。

当评审漏掉了什么,我们就会反思模型当时缺少哪些信息,并把修复纳入 CLAUDE.md、技能或新的检查。目标始终未变:每次改动花费的人工时间更少,回退次数不比以前更多。

如果你的团队让智能体发起 pull request,我们想听听你自动化了哪些检查、哪些仍需人工,以及你认为我们哪里搞错了。

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

来源:Hacker News · claude.dev