Claude Code 团队公开了他们的代码评审方法:当 Claude 写下约 90% 的代码,人工逐行评审已经不可能,他们用六层检查(PR 前验证、AI 评审、安全评审、CI、按风险分级的真人审批、合并队列与灰度)+ 快速回滚来替代"一道闸门"。结果是在 PR 合并量翻倍的同时,回滚占比下降超过 40%。本文完整翻译原文,保留全部章节与 6 张配图。

原文:How the Claude Code team does code review,作者:Addy Osmani(@addyosmani)。

本文由 LobsterAI 自动翻译和发布。


文章封面

译者说明:以下为文章正文的完整中文翻译,保留原文章节、段落顺序与配图位置。图片为原图,图内英文未作改写。


我们之前提到过,Claude Code 大约 90% 的代码是 Claude 自己写的。那么在这个规模下,我们怎么做代码评审?到底谁在读代码?如果代码出了问题又会怎样?

本文介绍我们团队在 Claude Code 的代码评审流程,以及我们是如何改进它的。从 6 月到 9 月,合并进 Claude Code 代码库的 pull request 数量增长了一倍多,而不得不回滚的 pull request 占比下降了 40% 以上。

我们并没有放弃代码评审。相反,我们把大部分"读代码"的工作自动化了,并围绕"节省人的时间与精力"来做调优。我们还在提升"回滚错误的速度和便捷度"上做了大量投入。

重新思考代码评审的目标

当智能体开始写代码,变更量会快速膨胀。在我们的 monorepo 里,5 月每天大约合并 2,500 个变更,到今年 9 月已经超过每天 30,000 个。

Claude Code 虽然不在这个 monorepo 里,但它同样承受着很大压力。当你以这种速度前进时,逐行人工做代码评审会变得越来越难,甚至根本不可能。

代码评审有两个目的:

  1. 在发布前发现并修正缺陷
  2. 建立对代码库的共同理解

本文主要聚焦第一个目标,第二个目标会在文章末尾讨论。

我们把"有没有人读过这段代码"看作一种并不精确的度量;更准确的问法是:“这次变更可能出什么问题,而哪一种检查——自动的还是人工的——才是抓住它的正确手段?”

这个问题也改变了代码评审的时机。传统上,代码评审是"事后检查":代码写完了、PR 提了,然后等别人来看。而我们把代码评审整合进了工作流,它更像你写作时一直在做的拼写检查。

大多数变更在请人来看之前,至少已经被 Claude 评审并修正过一次。这样我们拦住了大部分常规缺陷,把人的注意力留给更高层面的考量。

人的时间与注意力

我们希望把人的时间和注意力留给只有人才能处理的事情,同时确保有足够的自动化覆盖其余部分。即便以我们现在的节奏,仍然有大量工作需要人来做,团队几乎每天都会遇到新的、前所未有的挑战。

也正因如此,人的时间是一种"双边成本":我们不仅要算人们花在代码评审上的时间,还要算他们花在修复烂代码引入的 bug 上的时间。如果只减少前者而不减少后者,我们并没有真正节省人的时间。

所以我们的目标是:Claude Code 各仓库(CLI、桌面端与 Web 应用、移动端应用)中的大多数 pull request,应当最多只有一个人在回路里(这个人可以是作者本人)就能合并,同时回滚率和事故率不上升。

我们用来衡量"人的时间"的指标是:每个 PR 的人工批准数中位数(以及 95 分位数)、人工评论数量、PR 等待某个人的时长。我们把回滚率和事故率当作护栏:如果这些指标上升了,说明我们并没有减少人的时间,只是把它挪到了别处。

一次变更是怎么走的?

一次典型的变更,无论来自工程师的笔记本还是 Slack 里的一个 Claude 会话,都要经过多层评审。

FIG A:一次变更的一生

FIG A:一次变更的一生

上图是 Claude Code 一次典型变更从作者环境到生产环境的路径。这个过程大部分是自动化的,人的注意力集中在有风险的部分。

我们有六层检查,外加一套回滚机制,来处理任何漏过去的问题。

FIG B:层层设防,而非一道闸门

FIG B:层层设防,而非一道闸门

每一层都会拦住一些 bug,也会漏掉另一些。很少有 bug 能穿过所有层;即便有,我们还有回滚机制兜底。

1. PR 之前的验证

我们大部分的评审都在创建 PR 之前完成。我们让 Claude 去做一些"可运行"的事情,比如跑测试、跑构建,或者把截图与设计稿做对比。我们不要它说"任务完成了",而要它把结果拿给我们看。随后,一个子智能体会带着全新的上下文评审 diff。这两种做法都写进了我们的最佳实践指南。

在开 PR 之前,我们中很多人还会跑几个内部 skill——找 bug、缩减 diff、端到端验证,以及按照设计规范检查 UI 变更。

Claude Code 的创造者 Boris Cherny 认为,让 Claude 具备一个验证流程,可能是获得更高质量结果的最大关键。

他估计,一个验证流程能把 Claude 输出的质量提升 2 到 3 倍。

不同领域的验证流程可能差别很大:

  1. 有时只是跑一条 bash 命令
  2. 有时是跑一整套测试
  3. 有时是在浏览器或手机模拟器里测试应用

如果自动评审者反复指出同一个问题,我们就在源头修掉它——改 skill 本身、加一条 lint 规则,或者更新下面这类文档文件:

CLAUDE.md —— 项目描述文件,用来给 Claude 提供项目上下文(技术栈、约定、目标、非目标)。Claude Code 在每一次评审时都会加载这个文件,所以对大型代码库我们会把它写得简短,其余文档在这里引用。当代码违反了这个文件里的约定,或者某次更新让文件里的内容过时了,评审者会把它标出来。

REVIEW.md —— 代码 owner 告诉评审者"这块代码里什么最重要"的文件。我们另外还会验证评审者是否真的遵守了这些准则。好的 REVIEW.md 都很简短,重点在于:

  1. 定义"项目失败"是什么(文档不能说谎;配置不能路由错)
  2. 限制吹毛求疵
  3. 定义要忽略的路径
  4. 要求给出文件 + 行号引用,而不是猜文件名
  5. 要求给出顶层摘要,包含问题数量,并明确说明"没有问题"

2. 每个 pull request 都有 AI 评审

每个 PR 在人工评审之前,都会先收到一次 Claude 评审。

diff 会结合整个代码库、以及 PR 作者声明的意图来分析。它会检查:

  1. 这次变更是否真的解决了那个问题
  2. 这次变更是否契合更大的系统
  3. 这次变更是否破坏了代码库的其他部分

在评审 bug 修复时,机器人会努力找出根因。

我们的公开产品 Code Review for Claude Code 现在已向所有 Team 和 Enterprise 客户开放。另外还有一个本地使用的轻量版——/code-review。

自研究预览版发布以来,我们内部 PR 中获得实质性评审意见的比例从 16% 升到了 54%。对于超过 1,000 行的 PR,Code Review 能发现其中大多数的问题;而对多数较小的 PR,它会返回"干净"的结果。仓库管理员可以配置 Quick、Standard 或 Deep 模式。当"漏掉一个 bug 的代价"高于"多花时间评审代码"时,Deep 模式很有用。

在进入人工评审前,发现的问题还会经过多个其他智能体的核验:

  1. 多个智能体并行搜寻 bug
  2. 由一次独立的投票决定某个发现是有效问题还是误报,只有票数足够的发现才会被确认
  3. 仅涉及风格/诊断/清理的发现会被过滤掉(在铺开之前,我们在自己的 PR 上对这些过滤器做了 A/B 测试)

剩下的发现会以行内评论的形式呈现,按严重程度排序,并标记为:

  1. Important(重要)
  2. Nit(吹毛求疵)
  3. Pre-existing(既有问题,这样不会因为不是本次 PR 引入的 bug 而苛责作者)

所有发现都必须被处理——要么修掉,要么在讨论串里给出理由后关闭。很多情况下,智能体会通宵把问题修好,作者第二天回来时,PR 已经又经过了一轮评审。

3. 能卡住合并的安全评审

我们还会跑自动化的安全评审。只要还有未解决的发现(无论来自代码评审还是安全评审),PR 就不能合并。作者可以修复问题,或者用一段简短理由关闭讨论串(这是我们处理误报的方式)。

如果后续的 commit 解决了该问题,对应的发现会自动关闭。你可以用开源的 security review GitHub Action,或者 /security-review 命令做同样的配置。具体搭建方法见 Claude Code 中关于自动化安全评审的指南。

4. 能卡住合并的自动化检查

标准检查包括:lint、类型检查、所有平台上的测试、构建。(我们在这里写过如何扩展 CI。)一个典型的 CLI PR 在任何人看之前,可能会跑几十项检查。PR 模板里会问这次变更如何测试、如何回滚。Claude 可以替你回答这些问题,但作者有责任自己复核。

5. 按风险分级的真人审批

有些变更比另一些风险更高(比如修文档 vs. 改自动更新器),因此需要不同级别的评审。

FIG C:风险分级

FIG C:风险分级

风险举例人工审批
低文档、文案、仅内部可见的功能、开关后面的增量变更自动评审与安全检查全绿后,无需人工
中常规产品变更;对缓存、自动更新器、系统提示等敏感区域的变更一个人
高风险最高的代码,以及一小批永远需要两个人的路径两个人

分级由"变更"决定,而不是由"作者"决定。一个分类器根据变更判定风险等级;另一个分类器判断某个具体变更是否实际需要额外一次批准,即使分级本身没这么要求。有些路径永远需要额外批准——被 owner 标记为敏感的任何内容,以及任何 CI workflow 文件。

只有仓库"可信名单"上的作者才能享受放宽的评审要求,而这通常就是拥有该仓库的团队。其他所有人都需要第二个人批准。智能体的批准不算作人工批准。自动评审可以为低风险变更放行,但在需要真人的场合,它不能替代真人。

变更的作者对它负责;所以如果 PR 是由智能体写的,智能体的操作者始终算作必需的评审人之一。一次 PR 批准只对一个 commit 有效。如果来了一个不可信的新 commit(比如智能体推了东西,或有人手工解决了合并冲突),PR 必须重新批准。

6. 合并队列、功能开关与分阶段发布

合并队列确保所有检查都是在"变更已应用的最新 main"上运行的。这可以避免把两个各自成功的 PR 合进一个坏掉的 main。通常,新功能会在功能开关后面发布,Anthropic 员工先拿到访问权限。因为我们自己大量使用这个产品,往往能在用户之前发现回归。

对外发布时,变更先进 latest 通道,stable 通道随后跟上。

7. 快速反馈与回滚

总有一些坏的合并会漏过去。这是我们的最后一道防线,重点是尽可能快地识别它们。开关后面的变更无需部署就能关掉。

Claude Code 有每周轮值的 on-call,任何事故复盘都会回溯"哪一层本该拦住这个问题"。除了盯着回滚和事故看板,发现坏合并仍然要靠作者和 on-call。我们目前还没有根据这些指标自动调整审批要求的熔断机制,但随着我们在"减少合并环节人工干预"上不断推进,这已经在考虑之中。

一位团队成员提到,放慢速度、减少产出其实能提升准确率,但这主要是因为总工作量变少了。我们真正的优先级,是提升快速发现并纠正问题的能力。

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

我们的代码评审流程是一个持续改进的循环:

我们把事故当作评估指标。团队收集了引发事故的 PR、它们的 fix-forward 和回滚,并让理解代码库的工程师核验每个 bug 的位置与正确性。评审系统每天都会针对这个集合做评估。

FIG D:事故就是我们的评估集

FIG D:事故就是我们的评估集

每一起事故都会加入集合,评审系统的每一次改动都会针对整个集合做评估。

为了衡量噪音,我们观察人对评审意见的反应。如果我们不介意报出一大堆"非 bug",提升找 bug 的命中率是很容易的,所以这必须被约束。我们把每条评审意见按 PR 上的人如何处理它来分类:修掉了、驳回了、说明是有意为之、或者认为不重要。此外,我们还会在已知"干净"的 PR 上跑评审系统,观察它多频繁地报出"如果照做反而有害"的改动。

我们公开的优先级是:第一,发现真正的 bug;第二,减少评审轮次。

当作者是智能体时,处理一条评审意见的代价更高。人 dismiss 一条吹毛求疵的意见只需要一秒,但智能体要去处理它,就会产生又一个 commit、又一轮评审。

评审过程必须收敛。智能体作者和智能体评审者是有可能无限循环下去的。我们观察到 CLI 项目里有一个 PR,因为智能体作者和智能体评审者之间的来回,一夜之间循环了五十多次。这次循环是被人工叫停的,否则它可能一直转下去。

我们对评审系统反复标记的内容跑了 git blame,发现约 59% 是自第一轮评审起就一直存在的代码。本质上,评审系统很少发现新 bug,只是每一轮又多找出几个老 bug。

我们让情况更糟了,因为我们养成了"默认跑好几轮评审"的习惯,理由是每一轮都可能发现不同的 bug。对于高风险变更,多跑几轮确实有用,但不是每个 PR 都需要。团队开始同时跟踪"通过所需的轮数"和召回率,并把"重新评审"改成:

  1. 限制轮数。超过三轮后,评审系统会要求作者重新评估整体方案。
  2. 初始几轮之后,重新评审只看新的 diff。
  3. 说明 bug 的类别,让作者能一次改掉一整类问题,而不是只改一行。
  4. 不重复报告已经指出过的问题。

评审智能体还会在夜间针对当前代码库运行,帮我们在历史 bug 扩散之前把它们找出来。

FIG E:会收敛的评审

FIG E:会收敛的评审

Convergence 之后,每个内部 pull request 平均评审轮数的下降情况。

回路中的人

让一位资深工程师停下手上的工作去评审一个常见错误,比把这个过程自动化要昂贵得多。但在某些任务上,人类评审者仍然不可或缺:

  1. 这个东西到底该不该做?决定要不要做、设定产品的愿景、品味和目标。
  2. 有一个团队声称已经解决了代码评审的瓶颈,但他们对于来自团队外部的变更仍然做人工评审,以确保做的是"对的东西"。
  3. 审计危险代码(计费、自动更新器、CI workflow)。
  4. 架构评审,因为这些选择会设定其他人遵循的约定。
  5. 核验证据(测试输出、跑过的命令、截图、评审意见)。如果你说不出这些检查会漏掉什么,那就别只求一个 👍,你需要的是真正的评审。
  6. 承担责任。每一次上线的变更,不管是谁敲的,都要有一个人对它负责。

团队上下文

代码评审一直承担着沟通代码库变更的双重作用。读同事的 diff,是人们理解"改了什么"以及"代码库如何运作"的常见方式。如果大部分阅读都由机器完成,那大家靠什么来学习代码库?

我们并没有任何正式机制。团队里没有人脑子里装着 Claude Code 的完整心智模型,我们也不认为需要这样。相反,当有人想了解代码库的某一部分时,就让 Claude 针对那一小块生成一份讲解。这只需要几分钟,而且讲解始终保持最新。

FIG F:如果机器做了大部分阅读,人怎么学习代码?

FIG F:如果机器做了大部分阅读,人怎么学习代码?

对我们中的一些人来说,还会更进一步:让 Claude 生成一个小 artifact,把这块区域可视化,并用自己偏好的方式讲给自己听。

比起逐行阅读琐碎的 diff(那教不了你什么,比如一行的文案改动),这是集中人类注意力的更好方式。为你要改的模块生成一份讲解,能让你学到很多。

另外,我们默认是开放的:在公开的 Slack 频道里工作,别人一边做你一边就能看到。

“你合入的,你负责”

如果你合并了一个小或中等的变更,结果还是出了问题,通常的预期是你自己修。这之所以不会失控,有几个原因:

  1. 变更小,容易评审、调试和回滚。
  2. 一切都能回滚。功能开关、revert 和分阶段发布能在几分钟内逆转大多数错误。
  3. 影响范围决定标准。越昂贵的代码,越多人看、越多验证。
  4. 回归会被遥测和内部使用快速发现。
  5. 这不是甩锅游戏。修问题的人拥有它,因为他掌握最多上下文。事故复盘针对的是系统。
  6. 另外,团队里很多职责是通过公开频道共享的。任何人都能看到别人在做什么、加入讨论,或者让 Claude 去找缺口。反馈也来自非代码 owner。这种开放性是质量能在"不必逐行设卡"的前提下保持高位的重要原因。

改变方向

有了智能体,启动新工作的成本很低,但有时候你不该启动。典型情况是:频道里有人指出这个提议的变更走不通(引用之前的决策,或建议更好的路径),作者要么关掉 PR,要么改实现。

因为一开始写代码就很容易,关掉 PR 并不会让人觉得是损失。关键在于在讨论串里尽早转向,而不是等到最后对一个已经定稿的 PR 提阻塞性意见。

如何落地

这套方法的大部分组件,你的团队现在就能用。

提交 pull request 之前

  1. 给智能体一份可运行的检查清单,把这些检查写进一个简短的 CLAUDE.md
  2. 设置智能体通过 hook 调用 formatter 和 linter
  3. 在本地跑 /code-review 和 /security-review,或者让一个全新上下文的子智能体来评审变更
  4. 创建 REVIEW.md,用它跟踪反复出现的评审问题

每个 pull request

  1. 强制 CI
  2. 在 PR 模板里加上“测试”“风险”“如何回滚”
  3. 先在一个仓库上接入 AI 评审者(Code Review for Claude Code,或 Claude Code GitHub Action),用几周时间迭代 REVIEW.md,再推广到更多仓库
  4. 加上自动化安全评审
  5. 要求所有评审讨论串在合并前都已解决
  6. 按风险施加相应强度的评审:常规变更快速过一遍,高风险区域(bug 代价高昂)做彻底评审。Code Review 提供 Quick、Standard、Deep 三档
  7. 限制评审轮数,几轮之后只看新的 diff

审批

  1. 定义风险等级,按变更而不是按作者设定。一开始从严,看到证据后再逐步放宽
  2. 把宽松的审批路径留给可信作者
  3. 机器人批准不能用来满足必需的批准。有新 commit 推送时,撤销已有批准
  4. 指定真人对涉及 auth、billing、CI、发布 workflow 的代码与策略,以及安装器做审批。用 CODEOWNERS 和分支保护来实现

合并与合并之后

  1. 搭好合并队列
  2. 试验功能开关
  3. 先向内部用户发布
  4. 分阶段放量
  5. 做一键回滚机制
  6. 通过记录事故和回滚,建一套 eval
  7. 每次改动评审者的 prompt、工具或模型,都重新给它打分
  8. 跟踪人的投入,把回滚率和事故率当作护栏

这件事会走向哪里

Code Review 发布时的意图是:最终批准决定仍然由人来做,产品只负责把发现贴出来。但在我们自己的仓库里,我们走得更远:owner 正在决定哪些低风险变更可以在自动检查通过后自动合并。这个边界还会继续演进。

所以每当评审者漏掉什么,我们都会问自己:模型的上下文里缺了什么?然后把它变成一个 CLAUDE.md、一个 skill,或者一项新检查。而我们的目标没有变:在保持回滚率不高于当前水平的同时,降低每次变更所需的人工投入。

如果你也允许智能体创建 pull request,欢迎告诉我们你自动化了哪些检查、哪些必须由人来做,以及我们哪里做错了。

本文是我们原文的重写,并经 Pangram 检查。