Anthropic 代码评审实践:6 层防线 + 3 轮上限,AI 写 90% 代码后怎么保证质量

0 阅读9分钟

先看三个数:

  • Claude 写掉了 Claude Code 自己 90% 的代码
  • Anthropic 单体仓库日变更量:5 月约 2,500 → 9 月 30,000+
  • 同期 Claude Code 仓库合并 PR 翻倍,回滚率反而降了 40%+

在这种量级下,逐行人工评审不是"效率低",是物理上不可能。Anthropic 的做法不是取消评审,而是把"读代码"整体搬给机器,把人的时间压到"判断"上。

下面按工程视角拆一遍,能抄的直接给模板。

一、把问题重新定义一次

"有没有人认真读过这段代码"是一个很糙的替代指标。真正该问的是:

  1. 这次变更可能出什么问题?
  2. 哪一关(自动的 / 人的)最适合拦住它?

问题一换,评审的时机也变了。过去是流程末端的闸门(写完 → 开 PR → 等人看),现在评审持续发生,更接近 IDE 里的 spellcheck:大部分变更在人看到之前,已经被 AI 评审并修过至少一轮。

目标也被写得很具体:

Claude Code 各仓库的大多数 PR,最多一个人参与就能合并(可以是作者本人),同时回滚率和事故率不上升。

注意后半句。人的时间包含"评审时间"和"修烂代码的时间",只砍前者,成本会从后者冒出来。

度量口径:

指标作用
人工批准次数(中位数 + P95)效率
人工评论数效率 / 噪声
PR 等待人的时间效率
回滚率、事故率护栏,涨了就说明时间只是被搬走了

二、六层防线,而不是一道闸门

image.png

每层都拦一部分、漏一部分,漏下去的由下一层接住,最后兜底是回滚。

L0 · 让工作自证(PR 之前)

大部分评审发生在 PR 创建之前。给 agent 一个可验证的东西——测试、构建产物、可对比的设计稿截图——要求它拿出输出,而不是声称完成。

Boris Cherny(Claude Code 作者)认为这是"最重要的一件事",估计能让结果好 2~3 倍。原文的说法:

验证在每个领域长得都不一样。可能只是跑一条 bash 命令,跑一遍测试,或者在浏览器、手机模拟器里试一下应用。

配套做法:开 PR 前跑几个本地 skill —— 一个找 bug、一个简化 diff、一个端到端验证;UI 变更对照设计规范。

L1 · 每个 PR 的 AI 评审

评审把 diff 同时放在整个仓库上下文和 PR 声称的意图之间对照,问三件事:解决它要解决的问题了吗?符合系统现状吗?会不会破坏别的?

修 bug 的 PR 会额外找根因,而不是看表面补丁。

上线时的数据:内部拿到实质性评审意见的 PR 占比 16% → 54% 。超过 1,000 行的 PR 大多能查出问题,小 PR 大多干净返回。仓库可以设 Quick / Standard / Deep 三档。

L2 · 可阻断的安全评审

安全是独立一道。任一 bug 评审或安全评审的结论未关闭,PR 不能合并——修掉,或用简短理由关掉讨论(同时清掉误报)。后续提交修好了,结论自动消失。

L3 · 可阻断的自动检查

lint、类型检查、多平台测试、构建。一个典型 CLI PR 在人看之前可能已跑几十项。

PR 模板强制两项:怎么测的、怎么撤回。AI 可以代填,但发起变更的人要签字。

L4 · 按风险分级的人工批准

核心原则:分级由变更决定,不由作者决定。

一个分类器读变更给档位,第二个分类器判断"即使档位允许,这次是否仍需另一个人"。有些路径永远要人:owner 标记的敏感路径、任何 CI workflow 文件。

风险档典型内容人工批准
低文档、文案、纯内部、flag 后的增量0(自动检查 + 安全评审通过即可)
中常规产品变更;缓存、自动更新、系统提示词1
高最高风险代码;部分路径强制两人2

两条硬规则:

  • agent 的批准不能顶替人的批准。 自动评审可放行低风险变更,但填不上"必须有人"的坑。
  • 批准绑定 commit。 后续落了系统无法担保的新提交,必须重新批准。

L5 · 合并队列 + flag + 灰度

合并队列把变更应用到最新 main 上重跑全部检查,避免两个单独能过的 PR 合起来把 main 弄坏。

新行为先进 flag,先内部使用;对外发布先进 latest 通道,stable 跟在后面。

兜底 · 发现快、回滚快

flag 不发版就能关。团队每周轮 on-call,事故复盘问"本该哪一层拦住"。目前还没有"指标一动就自动收紧审批"的熔断,工程师希望在更多代码走向零人工合并之前补上。

三、AI 评审怎么降噪:结论先投票,再给人看

这是我觉得工程上最值得借鉴的一段。

多个 agent 并行找 bug,然后另一组 agent 对每条结论投票,票数够才发布。风格类、诊断类、清理类的意见直接丢弃(先在自家 PR 上做过 A/B 测试)。

剩下按严重度排序,以行内评论出现,标记 Important / Nit / Pre-existing。每条都必须被回应——修,或写明理由关闭讨论。

为什么要投票? 因为噪声成本不对称:

人看到一句吹毛求疵的评论,几秒钟就能挥手略过;agent 会老老实实去修,于是多一个 commit、多一轮评审。

噪声的度量方式也很"产品":按人对评论的实际反应分类(修了 / 说它错了 / 有意为之 / 无关紧要),再把评审跑在已知干净的 PR 上,统计误报。

优先级写死:先抓真 bug,再减少评审轮次。

四、评审必须收敛:3 轮上限

agent 作者 + agent 评审者可以无限互卷。有个 CLI PR 一夜之间 50 多轮评审和修复,最后作者手动叫停。

git blame 显示:评审者反复指出的问题里约 59% 是第一轮就已存在的代码——它每轮只是在挖更多老 bug,不是发现新问题。而团队此前已经把"多轮评审"当成默认。

四个收敛手段:

手段做法
轮次上限3 轮后评审者请作者退一步重新考虑方案
只看新 diff头几轮之后只重评审新增部分
指出 bug 类别让作者一次修掉所有同类实例,而不是改一行
不重复已发过的结论不再发

效果:

image.png

  • 一轮通过:65% → 78%
  • 5 轮以上:8.9% → 1.5%
  • 平均轮次:2.08 → 1.32

另外,评审 agent 会每晚扫存量代码,挖历史遗留 bug。

五、把事故变成 eval

他们收集历史上导致事故的 PR(含 fix-forward 和回滚的),请熟悉情况的工程师逐条确认"这是 bug、bug 在哪",形成基准集,每天跑一遍。

image.png

规则:评审者的 prompt / 工具 / 模型一改,就在全集上重新打分,分数涨了才留。

六、可直接抄的模板

以下是我按原文思路整理的参考模板,不是 Anthropic 的原始文件,按自己仓库改。

6.1 CLAUDE.md(要短)

# 项目上下文

## 技术栈
- Java 17 + Spring Boot 3,MySQL 8,Redis 7
- 前端:React 18 + Vite

## 约定
- 分层:controller → service → repository,禁止跨层调用
- 所有外部调用必须有超时和降级
- 新增接口必须补集成测试,覆盖率不低于 70%

## 非目标
- 不引入新的 ORM 框架
- 不做跨库事务

## 更多细节
- 发版流程见 docs/release.md
- 数据库变更规范见 docs/db-migration.md

要点:大仓库里必须短,细节指向其他文档。评审时它会整篇加载。

6.2 REVIEW.md(由 owner 写)

# 本目录的评审重点

## 在这里,失败长什么样
- 计费相关:金额算错、重复扣款、退款不回滚
- 缓存相关:脏读、击穿、key 冲突

## 必看
- 任何改动 `src/billing/**` 的 PR:金额单位、边界值、幂等
- 任何改动 `src/cache/**` 的 PR:失效策略、并发写

## 跳过
- `**/*.md`、`**/generated/**`

## 输出要求
- 每条结论必须给 文件 + 行号
- 先给数量(发现 N 个问题),没问题就直说"无需修改"
- 风格类意见最多 3 条

6.3 PR 模板

## 改了什么

## 怎么验证的
- [ ] 单测 / 集成测试
- [ ] 本地跑通
- [ ] 截图 / 录屏(UI 变更必填)

## 风险
- 风险档:低 / 中 / 高
- 影响范围:
- 是否在 flag 后:

## 如何撤回
- 关闭 flag:`xxx`
- 回滚命令:

6.4 落地顺序

  1. 先给 agent 可自跑的检查,写 CLAUDE.md,加 format/lint hook
  2. 本地接 /code-review、/security-review,或全新上下文的子 agent 评审
  3. 一个仓库接 AI 评审,调 2~3 周 REVIEW.md,再铺开
  4. 接安全评审,要求合并前所有讨论已解决
  5. 上风险分档(先严后松),CODEOWNERS 锁死鉴权 / 计费 / CI
  6. 合并队列 + flag + 灰度 + 一键回滚
  7. 把事故和回滚变成 eval 集,度量人的时间,用回滚率做护栏

七、这套东西的适用边界

说点泼冷水的话。

这套流程真正的杠杆不是"AI 评审",是"可验证 + 可回滚"。 AI 评审只是让问题更早暴露;敢把闸门放下的底气,来自 flag、合并队列和一键回滚。

所以顺序不能反:先把回滚和灰度建起来,再砍人工评审。 反过来做,就是把风险直接推给线上。

另外几个前提:

  • 变更要小。 "你合的,你负责"能成立,前提是变更小、可逆、爆炸半径清楚。变更大,这句话就变成甩锅。
  • 需要有人维护 REVIEW.md。 它会腐化,腐化之后评审质量掉得比没有还快。
  • 需要有 eval 集。 没有基准集,你无法判断"换了模型之后评审是变好还是变坏"。

原文里还有个我很喜欢的细节:评审者会指出 "让 CLAUDE.md 里某句话过时的变更" 。

也就是说,文档不是写完就完的说明书,它需要跟着代码一起被维护。这个成本要提前算进去。