2026-10-19阅读笔记52 min read

Claude Code 团队的代码审查:学习笔记

claude.dev 工程博客《How the Claude Code team does code review》精读。目标改写:revert 与事故不升,人时最小;七层防线与风险分级审批;事故即 eval · 噪音度量 · 审查收敛;对照自身四道关与苹果支付验证链,分三批改进。

AnthropicClaude Code代码审查质量门禁Agent 工程

基于 claude.dev 工程博客文章《How the Claude Code team does code review》整理。学习时逐节对照了我自己的 agent 开发流程,讨论内容放在第 8 到 10 节。


0. 来源与关联

0.1 原始文章

项目 内容
标题 How the Claude Code team does code review
作者 Addy Osmani、Morgan Westlee Lunt、Qing Wang
发布时间 2026-10-15
发布位置 claude.dev 博客,Engineering 栏目
原文链接 https://claude.dev/blog/solving-code-review-at-anthropic/
原文 Markdown 版 https://claude.dev/blog/solving-code-review-at-anthropic.md
阅读时长 约 14 分钟
致谢的审稿人 Emma Webb、Michael Segner、Amie Rotherham

文章做了三件事:

  1. 重新定义代码审查的目标:不是"有人读过",而是在 revert 和事故不上升的前提下,尽量少花人的时间;
  2. 拆开 Claude Code 团队一个改动从作者机器到用户要经过的七层检查,以及他们怎么持续改进 AI reviewer;
  3. 说明人还保留哪些职责、代码没人逐行读之后团队怎么理解代码,最后给出一份落地清单。

0.2 文章中提到的工具

工具 是什么 文档
/code-review 在本地跑的轻量版 AI 审查 —
Code Review for Claude Code 托管版 PR 审查,已在 Team / Enterprise 计划正式发布;管理员按仓库选 Quick / Standard / Deep 三档深度 https://code.claude.com/docs/en/code-review
/security-review 与开源的 security review GitHub Action 自动安全审查,本地命令和 CI 两种用法 https://support.claude.com/en/articles/11932705-automated-security-reviews-in-claude-code
Claude Code GitHub Action 落地清单里给出的另一种接入 AI reviewer 的方式 —

容易误解的一点:文章说 Code Review 这个产品只发审查意见,不替人批准 PR。他们自己仓库里"低风险改动只靠自动检查就合入",是仓库 owner 自己定的规则,不是产品默认行为。

0.3 文章中引用的延伸阅读

0.4 本笔记的阅读约定

  • 【原文】 标注的内容来自文章,均为意译和归纳,不是原文摘录。
  • 【解读】 标注的内容是学习过程中的理解、例子和推演,用于帮助吃透原文。
  • 【批判性阅读】 标注的内容是用文章自己的标准反过来审视文章的结果,原文中没有。
  • 第 1 到 7 节按原文顺序走;第 8 到 10 节是学习时对照我自己流程的讨论,"我"指笔记作者。
  • 原文的 Fig A 到 Fig D 四张编号图和一张未编号的示意图已嵌入对应小节,图注标原文编号。图里的文字是英文,正文在图下给出读图方法和中英对照。配图从原文页抓取,托管在自有 CDN(2026-10-26)。
  • ⚠️更正:初稿写的是"原文有五张图(Fig A 到 Fig E)"。2026-10-26 抓图时核对,现版原文只有 A 到 D 四张编号图,外加 6.2 节那张未编号的示意图;6.1 节原先引用的"Fig E"已删去。

1. 全文主线

核心矛盾:agent 写代码的产量远远超过人能读的量。Claude Code 约 90% 的代码由 Claude 写;Anthropic monorepo 每天合入的改动从 5 月的约 2,500 个涨到 9 月的 30,000 多个。逐行人工审查已经做不到,但不审又会出事。

全文一句话:在 revert 率和事故率不上升的前提下,最小化每个改动花掉的人的时间。

结果:6 月到 9 月,Claude Code 仓库合并的 PR 翻了一倍多,需要 revert 的 PR 占比下降了 40% 以上。

三招:

  1. 把大部分"读代码"交给机器,在人看之前设多层自动检查;
  2. 人的注意力按风险分配,改文档不用人,改计费要两个人;
  3. 重点投资"能快速撤销":开关、灰度、一键回滚。

全文结构:

重新定义审查(目标函数)
        ↓
七层防线:1 PR 前自证 → 2 AI 审查 → 3 安全审查 → 4 CI
          → 5 风险分级审批 → 6 merge queue / 开关 / 放量 → 7 发现与回滚
        ↓
让 reviewer 变好:事故即 eval / 衡量噪音 / 保证收敛
        ↓
人保留什么 · 团队怎么懂代码 · 文化(谁合入谁负责、方向不对早关)
        ↓
落地清单 · 方向

本笔记按"目标 → 七层 → reviewer 迭代 → 人与文化 → 落地 → 对照我的流程"的顺序组织。


2. 重新定义代码审查

2.1 审查的两个职能

【原文】代码审查一直在做两件事:一是在上线前发现并修掉问题;二是让团队建立对代码的共同理解。文章主要讲前者,后者放到结尾。

【解读】机器接手"读代码"以后,第二个职能会出现空缺,需要单独补(见 6.2)。

2.2 换一个问题问

【原文】"有没有人读过这段代码"只是一个不精确的代理指标。真正该问的是:这个改动可能出什么错?哪一种检查(自动的或人工的)最适合拦住它?

【解读】这样一问,"人逐行读"就从必选项变成了众多检查手段里的一种。类型错误交给类型检查,回归交给测试,两个 PR 合起来冲突交给 merge queue,"该不该做"才交给人。每类风险配一个最便宜又可靠的检查,后面所有设计都是从这个问题推出来的。

2.3 从闸门变成拼写检查

【原文】以前审查是流程末端的一道闸门:写完代码、开 PR、等同事审。现在审查在写代码的过程中一直在跑,像打字时的拼写检查。人被叫来之前,大多数改动已经被 Claude 审过、修过至少一轮,常规错误基本清掉了,人的注意力留给更高层的问题。

2.4 稀缺的是人的时间:目标函数怎么定

【原文】人的时间成本有两部分:花在审查上的时间,和花在修复坏代码造成的问题上的时间。只压低前者没有意义,时间会以返工的形式回来。

内容
优化目标 Claude Code 各仓库(CLI、桌面/Web、移动端)大部分 PR 最多只需要一个人参与,这个人可以是作者本人
人时间的度量 每个 PR 的人工审批数(中位数和 P95)、人工评论数、PR 等人处理的时长
护栏指标 revert 率、事故率。它们一上升,就说明时间没省下来,只是挪了地方

【解读】这和 A/B 实验里"主指标 + 护栏指标"是同一个结构:主指标往一个方向优化,护栏保证没把别的东西搞坏。学习时我对"只看 PR 审批人数会怎样"的回答是:任何领域只盯一个指标都会出结构性问题;只看审批人数、不管上线后的 fix / revert(返工),总时间其实没变。这就是古德哈特定律:一个指标一旦被当成目标,就不再是好指标。评测爬山那篇讲的是同一件事,见自动化评测设计与爬山优化:学习笔记。

2.5 【批判性阅读】两个数字要细看

  1. revert 下降的是占比,不是绝对数。PR 数翻倍多、revert 占比降 40% 多,粗算 2×0.6≈1.22 \times 0.6 \approx 1.2,revert 的绝对次数可能还略有上升,只是增速远低于产量。结论依然成立(产量翻倍、质量没变差),但要清楚说的是哪个数。
  2. 文章定义了"人时间"的三个度量,却没有报告它们的前后变化。给出的结果是 PR 数和 revert 占比,按文章自己的目标函数,"人花的时间确实少了"这一半没有数据直接支撑。

3. 七层防线(上):合入之前

【原文】Fig A 画的是一个典型改动从作者机器(或 Slack 里的 Claude 会话)到用户的路径:大部分路径是自动的,人的注意力放在有风险的地方。

原文 Fig A:一个典型 Claude Code 改动从作者机器到用户经过的九步检查,按合入前、每个 PR、合入、合入后四段排列,只有风险分级审批一步由人决定
原文 Fig A 大部分路径是自动的,人的注意力放在有风险的地方

读图:从上到下分四段,左边的图标说明每一步由谁做:圆圈打勾是自动(automated),橙色菱形是"由人决定,按风险伸缩",回旋箭头是补救(recovery)。

阶段 步骤 做什么
PR 之前 The work is verified(自证) Claude 跑测试、构建、lint;另一个 agent 在全新上下文里审 diff
每个 PR Required CI lint、类型检查、跨平台测试、构建打包,红了就不能合
Claude code review 多个 agent 并行找 bug,verifier 对每条发现投票,留下的按严重程度排序
Security review 单独一遍;两种审查里任一有未关闭的发现,都阻断合入
◆ Risk-based approval 分类器定档:不需要人、一个人、两个人;有些路径永远要人
合入 Merge queue 在已经合上这次改动的最新 main 上重跑检查
合入后 Behind a flag, used internally first 放在开关后面,Anthropic 员工先用
Gradual release 先发 latest 渠道,stable 渠道跟在后面
↻ Watch, fix forward or roll back 作者和值班的人盯着;开关能快速关掉,坏掉的构建直接 revert

【解读】九步里只有一个橙色菱形:人只在"按风险审批"这一处出现,而且出不出现、出几个人,由风险决定。

【原文】Fig B 把同一条路径画成六层检查,下面垫着回滚。

原文 Fig B:六层倾斜的检查面从上到下叠放,每层的洞位置不同;问题从顶部掉下,多数穿过一两个洞后被下一层接住,正中一列六层的洞恰好对齐,问题一路掉进底部的 fast rollback
原文 Fig B 每一层都会抓住一部分问题、漏掉一部分,能穿过所有层的很少,剩下的交给回滚

读图:六层从上到下是 verification(自证)、required CI、AI review、security review、human approval、merge queue + flags。每层都有几个洞,位置各不相同。问题从顶部(problems in)掉下来,多数穿过一两个洞,就落在下一层没有洞的地方被接住。只有正中那一列,六层的洞恰好对齐,问题一路掉到底,落进橙色的 fast rollback(快速回滚)。图里 CI 排在第二层,正文的编号是 1 自证、2 AI 审查、3 安全审查、4 CI,顺序不影响图要说的意思。正文编号 1 到 7,第 7 节就是图里垫在最底下的那层回滚。

【解读】这在安全工程里叫瑞士奶酪模型:每片奶酪都有洞,叠在一起之后,同时穿过所有洞的问题就很少了。

3.1 第 1 层:别人看之前,先自证

【原文】大部分审查现在发生在 PR 出现之前。三个习惯:

  1. 给 Claude 能跑的东西,并要求它贴出输出,而不是只说"做完了"。可以是测试、构建、和设计稿对比的截图。
  2. 用全新上下文的子 agent 审 diff。
  3. 开 PR 前跑几个内部 skill:一个找 bug,一个精简 diff,一个端到端检查行为;UI 改动还要对照设计规范检查。

Claude Code 的作者 Boris Cherny 认为,给 Claude 一个验证自己工作的手段,可能是拿到好结果最重要的一件事,估计能让最终结果好 2~3 倍。验证方式因领域而异:可能是跑一条 bash 命令、跑测试套件,也可能是在浏览器或手机模拟器里试用。

同一个问题被反复指出,就去源头修:改 skill、加 lint 规则,或者更新下面两个文件。

CLAUDE.md REVIEW.md
谁写 项目 这块代码的 owner
写什么 项目是什么:技术栈、规范、目标、非目标 在这块代码里,什么才算出了问题
要点 每次审查都会加载,所以大仓库里要写短,细节链到别的文档;reviewer 会标出违反规范的代码,也会标出让 CLAUDE.md 某句话过时的改动 好的 REVIEW.md 都很短:写清失败是什么样(文档的失败是"写错了",配置的失败是"路由错了");限制 nit 数量;列出不审的路径;要求用文件 + 行号作证据,不能看名字猜;总结开头先报数量,没问题就直说没问题

他们还会单独检查 reviewer 有没有真的按 REVIEW.md 执行。

【解读】为什么要"全新上下文":写代码的 agent 带着自己的推理过程,容易顺着原来的思路替自己找理由;新上下文只看 diff,不受这些影响。

3.2 第 2 层:每个 PR 都过一遍 AI 审查

【原文】

  • reviewer 结合 diff、整个仓库和 PR 写明的意图,判断三件事:解决问题了吗?和现有系统合得来吗?会不会弄坏别处?如果是修 bug,还要找根因。
  • 研究预览上线后,有实质性评论的内部 PR 占比从 16% 升到 54%。1000 行以上的 PR 大多会被审出问题,小 PR 大多没问题。
  • 管理员按仓库选 Quick / Standard / Deep。只有漏一个 bug 的代价超过多花的审查时间时,才值得用 Deep。
  • 核实机制:多个 agent 并行找 bug,再由另一组 agent 投票,足够多的认为是真问题才发出来。只涉及风格、诊断信息或清理的意见直接丢掉。这些过滤规则先在他们自己的 PR 上做过 A/B 测试才定下来。
  • 剩下的意见按严重程度排序,以行内评论发出,每条标 Important / Nit / Pre-existing。Pre-existing 表示这是原来就有的 bug,免得看起来像作者的锅。
  • 每条意见都必须回应:要么修掉,要么写明理由关闭。很多时候 agent 夜里就把修复做了,作者回来看到的是已经又过了一轮的 PR。

【解读】找 bug 的一组负责多找(召回),投票的一组负责去噪(精确率),两件事分开做。

3.3 第 3 层:能阻断合入的安全审查

【原文】安全有单独的自动审查。bug 审查或安全审查只要还有一条意见没关闭,PR 就不能合入。误报写一句理由关掉;后续 commit 修好了问题,意见会自动消失。本地用 /security-review,CI 用开源的 security review GitHub Action。

3.4 第 4 层:会阻断合入的 CI

【原文】lint、类型检查、各平台测试、构建。一个 CLI 的 PR 在人看之前可能要跑几十项检查。PR 模板会问:怎么测试的?怎么撤销? Claude 可以填,但提出这个改动的人要签字确认。

【解读】"怎么撤销"这一栏是在为第 6、7 层铺垫:写 PR 时就要想好退路。

3.5 【批判性阅读】评论变多不等于审查变好

16% → 54% 统计的是"有实质性评论的 PR 占比"。按文章自己在 5.2 节的标准,只看抓到了多少是不够的,还要看噪音。文章讲了噪音怎么测,但没有给出噪音的数字,所以单凭这组数据还看不出审查质量提升了多少。


4. 七层防线(下):审批、合入、回滚

4.1 第 5 层:人工审批,按风险分级

【原文】

风险 例子 需要几个人批准
低 文档、文案、只给内部用的功能、放在开关后面的新增功能 自动审查和安全检查都通过后,不需要人
中 普通产品改动,以及缓存、自动更新器、系统提示词这类敏感区域 1 人
高 风险最高的代码,以及少数规定必须两个人看的路径 2 人

四条规则:

  1. 风险等级由改动本身决定,不由作者决定。一个分类器读完改动来定级;另一个分类器再问"按等级不需要人,但这次改动是不是还是该有人看"。owner 标为敏感的路径和所有 CI workflow 文件,不管等级都要人看。
  2. 轻量审批只给受信任的作者。不在仓库信任名单里(名单主要是负责这个仓库的团队)的人,要再找一个人批准。agent 的批准永远不算人的批准:自动审查可以放行低风险改动,但不能顶替需要人的环节。
  3. 谁给 agent 下的指令,谁就要负责。agent 写的 PR 里,指挥它的人算作必需的审查人之一,和自己亲手写一样。
  4. 批准只对某一个 commit 有效。批准之后又进来系统无法担保的 commit(agent 又推了一次,或有人手动解决了合并冲突),就要重新批准。

【解读】CI workflow 文件一定要人看,是因为它定义的是检查本身,改了它就是改了关卡。agent 遇到检查不过时,最省事的"修法"就是把检查改松。

4.2 第 6 层:merge queue、功能开关、逐步放量

【原文】

  • merge queue 把每个 PR 放到最新的 main 上重跑全部检查,两个各自都通过的 PR 合在一起也不会把 main 弄坏。
  • 新功能通常先藏在开关后面,先给 Anthropic 员工用。他们自己用得很重,回归往往在用户之前就暴露了。
  • 对外发布先进 latest 通道,stable 通道晚一步跟进。

【解读】"各自通过、合起来坏掉"的例子:PR A 把函数 foo 改名为 bar,PR B 新增了一处 foo() 调用。两边各自基于旧 main 测试都是绿的,合到一起 B 调用的函数已经不存在了。

4.3 第 7 层:快速发现、快速回滚

【原文】

  • 还是会有错误漏过去,所以最后一层的目标是尽快发现。开关不需要重新发版就能关。
  • Claude Code 有每周轮换的 on-call。事故复盘会问:这个问题本该被哪一层拦住?
  • 文章承认的缺口:发现坏的合入,目前还是靠作者、on-call 和 revert / 事故看板。他们还没有"熔断器",也就是这些数字变化时自动收紧审批的机制;工程师们希望先有熔断器,再让更多代码零人工合入。
  • 一位同事的看法:放慢确实会降低出错率,但主要是因为什么都做得少了。他们宁愿更快地发现和修复问题。

4.4 【解读】能撤销,本身就能减少审查

第 5 层的表格里,"放在开关后面的新增功能"被归为低风险。说明在他们的定义里,风险不只看改动多大。我把它概括成(个人概括,原文未这样写):

风险 ≈\approx 出错的概率 ×\times 影响范围 ×\times 撤销需要的时间

开关压低了后两项,所以它不只是兜底的保险,而是直接让这个改动需要的人工审查变少。PR 模板里"怎么撤销"那一栏也可以这样用:如果答案是"撤不了,只能等下一版过审",这个改动就要么加开关,要么提高风险等级。


5. 让 reviewer 越来越好

【原文】审查流程本身在一个循环里持续改进。

5.1 把事故变成 eval(管召回)

【原文】团队收集了过去引发事故的 PR,连同后来的修复(fix-forward)和 revert,请熟悉那块代码的工程师逐条确认 bug 是什么、在哪。reviewer 每天在这个集合上跑一遍。

原文 Fig C:上排是事故或 revert 经专家确认后加入集合,下排是改 reviewer、每日评测、分数涨了就保留的循环,两排在每日评测处汇合
原文 Fig C 集合随每次事故增长,reviewer 的任何改动都要在整个集合上重新打分

读图:两条线在橙色的"Daily eval run"(每日评测)处汇合。

  • 上排管集合怎么长:Incident or revert(一次弄坏了东西的改动)→ Expert-checked case(专家确认 bug)→ Added to the set(每次事故加一条);
  • 下排管 reviewer 怎么改:Change the reviewer(改它的 prompt、工具或模型)→ Daily eval run(对集合里的每一条打分)→ Score went up?(涨了保留,没涨丢掉)→ next change,回到下一次改动。

【解读】没有这个集合,调 reviewer 只能凭感觉:改了一句 prompt,不知道是不是因此漏掉了原来能抓到的 bug。而且它是自己真正上线过的 bug,比通用 benchmark 更贴近要防的东西。

下排那一圈就是评测爬山笔记 Fig 6 的循环。不同的是这里只有一份集合,没有留出的测试集。好在集合随事故持续增长,每条新事故对 reviewer 来说都是没见过的题。

5.2 看作者怎么回应,衡量噪音(管精确率)

【原文】

  • 只要不在乎误报,多抓 bug 很容易,所以需要一个对冲的检查。
  • 把 reviewer 的每条意见按作者的回应分类:修了 / 认为意见是错的 / 说明是故意这样写的 / 觉得无所谓。
  • 在已知没问题的 PR 上跑 reviewer,数它有多少次报出会诱导人做出有害改动的问题。
  • 优先级明确写下来:第一是抓真 bug,第二才是减少审查轮次。
  • 作者是 agent 时噪音更贵:人几秒钟就能忽略一条 nit;agent 会去修,于是多一个 commit、多一轮审查。

【解读】第二种测法的重点在"有害改动":关心的不只是误报本身,而是误报被照着改了之后代码变差。只盯召回而不测噪音,又是 2.4 节说的单一指标问题。

5.3 审查必须收敛

【原文】

  • agent 作者和 agent reviewer 可以无限地互相制造工作。一个 CLI 的 PR 一夜之间审了又修、修了又审 50 多轮,最后作者手动叫停。
  • 对 reviewer 反复报的代码做 git blame,约 59% 在 reviewer 第一轮审查时就已经存在。reviewer 很少发现新 bug,而是每轮多挖出几个老 bug。
  • 他们承认是自己让情况更糟:习惯了默认跑好几轮,因为每轮都能多抓一点。高风险改动值得这样做,但不是每个 PR 都值得。
  • 现在把审查轮次和召回一起跟踪,并改了复审规则:
    1. 轮次上限:三轮之后,reviewer 让作者停下来,重新考虑方案;
    2. 前几轮之后只审新增的 diff;
    3. 按 bug 类别报,让作者一次把同类问题全修掉,而不是只改一行;
    4. 已经报过的问题不再重复报。
  • 另外,审查 agent 每天夜里会扫一遍现有代码,老 bug 在别处冒出来之前先抓住。
原文 Fig D:内部 PR 在收敛改动前后的三组对比柱,一轮审查修复后就通过从 65% 升到 78%,需要 5 轮及以上从 8.9% 降到 1.5%,平均审查次数从 2.08 降到 1.32
原文 Fig D 收敛改动之后,每个内部 PR 需要的审查轮次变少了

读图:三组柱子,灰色是改之前,橙色是改之后(after the convergence work):

改之前 改之后
一轮审查和修复后就通过 65% 78%
需要 5 轮及以上 8.9% 1.5%
平均审查次数 2.08 1.32

【解读】

  • 59% 这个数可以这样理解(个人推测,原文未说明):一次 LLM 审查相当于一次抽样,每轮只能看出全部问题的一部分。大 PR 里老问题多,再做一次全量审查就会再抽出几个。所以"每轮都有新发现"既不说明代码越改越差,也不说明应该继续改。
  • 这一节最重要的设计是把两件事分开:这个 PR 引入的问题在 PR 流程里处理,必须收敛;代码库里原有的问题交给夜间扫描,按自己的节奏处理。3.2 的 Pre-existing 标签、这里的只审新 diff 和夜间扫描,都是为这个划分服务的。

5.4 小结

要管的 衡量什么 怎么做
召回 真 bug 抓到了多少 事故做成 eval,reviewer 有任何改动就重跑
噪音 假问题报了多少 记录每条意见的结局;在已知干净的 PR 上测误报
收敛 几轮能通过 轮次上限、只审新 diff、按类别报、不重复报,老 bug 交给夜间扫描

6. 人的角色、团队理解与文化

6.1 人保留的五件事

【原文】把资深工程师从深度工作里拉出来抓一个常规错误,代价远高于跑一遍自动审查。人的工作从"读每一份 diff"转向机器不擅长的决策,五类决策留给人,其余全是检查:

  1. 这个该不该做:方向、品味、范围。有个团队已经宣布审查瓶颈解决了,但外部团队的改动仍然要人审,为的是有人问一句"这事到底该不该做"。
  2. 高风险代码:计费、自动更新器、CI workflow。
  3. 会被别人照抄的架构。
  4. 读证据:测试输出、实际跑的命令和返回、截图、reviewer 的意见。如果说不出这些检查会漏掉什么,就别快速批准,要求认真审一遍。
  5. 对结果负责:每个上线的改动都有一个人负责,不管代码是谁敲的。

6.2 机器读代码以后,人怎么懂代码

【原文】

  • 没有正式流程。团队里没人脑子里装着 Claude Code 的完整结构,他们也认为没人需要。
  • 想了解哪一块,就让 Claude 针对这一块写讲解,几分钟就好,反映的是代码当前的样子。
  • 有人更进一步,让 Claude 做一个小的可视化页面,按自己喜欢的方式讲这块代码。
  • 这比逐行读零碎 diff 划算:一行文案改动几乎学不到东西,你要改的模块的讲解能学到很多。
原文示意图:如果机器读了大部分代码,人怎么懂代码。左边代码库网格里高亮 managed settings 一小块,中间问 Claude 恰好这一块,右边得到讲解文档,或进一步做成可视化小页面;底部对比逐行读零碎 diff 与读要改模块的讲解
原文示意图(未编号) 机器读了大部分代码之后,人怎么懂代码

读图:

  • 左:代码库(The codebase)是一片方格,高亮的几格是 managed settings,即你想弄懂的那一块。图注写着"没人脑子里装着完整的模型";
  • 中:针对恰好这一块问 Claude(Ask Claude about exactly that slice),例子是"Claude Code 怎么读取 managed settings?";
  • 右:实线指向"Claude 写一份讲解",几分钟,反映代码现在的样子;虚线指向"有人更进一步",让 Claude 做一个小页面,可视化这块代码,按这个人喜欢的方式讲;
  • 底部对比:灰底是逐行读零碎 diff,一行文案改动几乎学不到东西;黑底是读你要改的模块的讲解,能学到很多。
  • 团队默认在公开的 Slack 频道里工作,别人在做什么,做的过程中就看得见。

6.3 "谁合入,谁负责"

【原文】中小改动合入后出了问题,团队规矩是一般由你来修。能行得通靠五个前提:

  1. 改动小:容易审、容易查、容易撤;
  2. 都能撤销:开关、revert、分阶段发布,几分钟撤掉大部分错误;
  3. 影响范围决定审查力度:代价高的代码,审的人更多,检查更多;
  4. 回归暴露得快:靠线上监控数据和内部使用;
  5. 不是追责:由你修是因为你上下文最多;事故复盘看的是系统。

责任也是共同承担的:在公开频道里,谁都能看到、接手,或者派一个 Claude 去补缺口,不负责这块代码的人也会给反馈。文章认为这种公开是不对每一行设卡也能保持质量的重要原因。

【解读】这个文化建立在机制上。没有"能撤销"和"暴露得快","谁合入谁负责"就会变成压力,人一有压力就会放慢、加关卡,最后又回到每行都要人审。

6.4 方向不对,早点关

【原文】agent 让开头变得很便宜,所以有些工作本来就不该合入。通常是频道里有人问为什么、指出以前的决策,或者提出更简单的路,作者就关掉或重新调整 PR。代码写得快,关掉也不觉得可惜。关键是换方向的意见要尽早在讨论里提,而不是等 PR 写完了再留一条阻止合入的评论。

【解读】agent 时代最贵的错误往往不是代码写得差,而是把一件不该做的事做得很好。


7. 落地清单与方向

7.1 文章的落地清单

【原文】(按阶段归纳)

PR 之前

  • 给 agent 能跑的检查,写进一份简短的 CLAUDE.md;用 hooks 自动跑格式化和 lint
  • 本地跑 /code-review、/security-review,或者用全新上下文的子 agent 审
  • 加一个 REVIEW.md,把反复出现的审查意见写进去

每个 PR

  • CI 必须通过;PR 模板加上"怎么测的 / 风险 / 怎么撤回"
  • AI reviewer 先在一个仓库上用,调几周 REVIEW.md,再推广
  • 加自动安全审查;所有审查讨论都关闭才能合入
  • 审查力度跟着风险走:常规改动快速过,漏一个 bug 代价大的深审
  • 限制审查轮次;前几轮之后只审新 diff

审批

  • 把风险分级写下来,按改动定级,不按作者;先严,有证据再放松
  • 轻量审批只给信任的作者
  • 用 CODEOWNERS 和分支保护,让认证、计费、CI / 发布流程、安装器必须有人批
  • bot 的批准不能代替必需的人;有新 commit 就作废之前的批准

合入之后

  • merge queue、功能开关、内部先用、分阶段发布、一键回滚
  • 把事故和 revert 做成 eval;reviewer 的 prompt、工具、模型一变就重新打分
  • 衡量人花的时间,把 revert 率和事故率当护栏

【解读】清单里藏着两条落地原则,容易看漏:先严后松,有证据再放松;先一个仓库,调好再推广。整套东西是一步步放开的,不是一次全上。

7.2 文章最后讲的方向

【原文】

  • Code Review 的发布文章说过,批准 PR 仍然是人的决定,产品只发意见不发批准。他们自己的仓库走得更远:由 owner 决定哪些低风险改动只靠自动检查就合入,这条线还在往前移。
  • 审查漏了问题时,他们会问模型当时缺了什么信息,再把补上的东西写进 CLAUDE.md、skill,或者加一项新检查。
  • 目标没变:每个改动花的人时间更少,revert 不比以前多。

【解读】他们把漏检当作上下文缺口,而不是"模型不够聪明"。这决定了补救动作是补信息、补检查,而不是换模型或加人。


8. 延伸思考一:对照我的四道关

8.1 我的流程

我的工作模式和文章很像:agent 产出大量代码,人根本来不及看。现在的流程是:

  1. 测试驱动:先写测试再写代码,测试也由 agent 写。修 bug 时先写一条测试看它变红,修完让它变绿。
  2. Claude 预审:改动面足够大(我主观判断)时,再起一轮多 agent 对抗审查。
  3. 危险功能我自己看 diff 挑刺:比如支付、直接影响机器性能的改动。改动很轻的一般不看。
  4. 移交 QA:专业测试人员把最后一关。

然后上线。

8.2 和文章对照

我的关卡 对应文章 一致的地方 不同的地方
① TDD 第 1 层:PR 前自证 给 agent 能跑的检查 文章还要求贴出运行输出,并补测试覆盖不到的验证(截图、端到端)
② 预审 + 大改动对抗审查 第 1 层子 agent + 第 2 层 AI 审查 多 agent 找 bug,分组投票 文章每个 PR 都审,只是深度按风险调;每条意见都必须回应
③ 危险功能人看 diff 第 5 层:风险分级审批 人力按风险分配 文章由改动本身定级(分类器 + 敏感路径清单);敏感路径改动再小也要人看
④ QA 文章没有专职 QA,对应第 6、7 层 上线前最后一关 文章的重点是出了错能马上撤:内部先用、灰度、一键回滚
— 文章有、我没有 度量(人时间 + 返工率);事故回流成 eval

8.3 三个主要差距

  1. 两个关键判断靠主观:第二道坎的"改动面足够大"和第三道坎的"改动很轻"。文章的原则是风险由改动本身决定,而 diff 大小不等于风险。支付代码里改一行(价格常量、金额取整、订阅状态判断)可能就是最危险的改动。按瑞士奶酪模型看,主观触发等于有时候那片奶酪根本不在。
  2. 四道关全是"防",没有"撤"和"发现":缺第 6、7 层。
  3. 没有度量,也没有回流:没有护栏数据,就不知道哪道关可以放松;QA 抓到的 bug 没有反过来变成前几道关的检查。

8.4 测试:先红再绿之后还有两处漏洞

先红再绿这一步很关键:从没失败过的测试证明不了它能发现问题。还要补两处:

  1. 红要红在断言上。测试变红可能是编译错误、import 写错、环境没起来,而不是测到了 bug,之后"修"绿了什么也没证明。让 agent 贴出变红那次的输出,确认是断言不通过。
  2. 变绿阶段 agent 可能改了测试本身:放宽断言、改期望值、跳过 case。这是 agent 写 TDD 的常见失败。办法是变红后锁定测试文件;变绿阶段的 diff 里改到了测试断言就单独标出来,要求写理由。

另外,测试和代码出自同一个 agent,它对需求理解错了,两者会一起错。成本最低的补法是写代码之前花一两分钟扫一眼测试用例清单:用例清单就是需求说明,比读代码快得多,也是 6.1 说的"读证据"。

8.5 对抗审查:结构一致,第二组问的是另一个问题

我的对抗审查分两组:

  • 第一组找问题,形成候选清单,不直接算 bug;
  • 第二组按我的原则反驳:(a) 有没有必要修,是不是亿万分之一概率才发生(这种不修);(b) 修复有没有破坏现有架构、改动面是不是太大;(c) 改动干不干净;
  • 最后投票选出要修的。

"一组找、一组驳、投票"和文章的做法一致,差别在第二组判断什么:

文章的第二组 我的第二组
判断的问题 这个 bug 是不是真的(真问题还是误报) 这个 bug 值不值得修、怎么修(必要性、对架构的影响、干不干净)

两个问题放在一轮里判,有两个风险:

  • (b) 可能把严重的真 bug 投掉:一个又真又重的 bug,干净的修法要大改,按"改动面太大"会被否掉。这种情况应该交给人,"修一个 bug 要动架构"正是 6.1 里人保留的架构决策。
  • (a) 在支付和安全代码上要换算法:一亿次一次说的是正常使用的随机概率,攻击者不会等随机概率(比如故意重放凭据)。先问"有没有人能故意触发它",能就等于概率接近 100%;不能,也要看概率 ×\times 影响,概率极低但一发生就是资损的照样要修。

改进:

  • 第二组结论从"修 / 不修"改成三种:修 / 不修(写理由)/ 交给人。"写理由"对应文章的"每条意见都必须回应"。
  • 我的这几条原则本身就是一份 REVIEW.md,写成文件,每次审查都加载同一份,再检查 agent 有没有照着执行。
  • 衡量这两组的效果:每条通过投票的意见记录最后结局(5.2 的四类),"认为意见是错的"占比高说明筛得太松;QA 每抓到一个 bug,回查它当时在不在第一组的候选清单里——不在,是"找"漏了;在但被投掉,是"筛"太严。召回就这样拆成找到率和保留率两段,知道该调哪一组。
  • 收敛:设轮次上限,超过就交给我判断;轮次多通常说明改动太大该拆,或者方案本身有问题。发现的老问题打 Pre-existing 标签记进 backlog,不在这个 PR 里修;后几轮只审新 diff。

8.6 第三道坎:从"挑刺"改成"问检查会漏掉什么"

  • 按功能判断"危险",这一点和文章的敏感路径一致;问题在"改动很轻就不看",这是按 diff 大小跳过人工。
  • 列一份敏感路径清单:支付模块、凭据校验接口、权益发放逻辑、性能热点路径、CI / 构建配置。人工投入分三档(结合我的情况改编,非原文):
    • 不用看:没碰敏感路径;
    • 看证据:碰了敏感路径但改动轻,看验证 skill 跑出的 transactionId 和数据库查询结果,一分钟左右;
    • 看 diff + 证据:碰了敏感路径且改动大,或者涉及架构。
  • 改到检查本身的一律要人看:测试文件、CI 配置、REVIEW.md、验证 skill。
  • 4.1 的第 4 条对我很实际:我看完支付 diff 后对抗审查又跑了几轮、agent 又改了代码,我看的就不是最终版了。要么等自动审查收敛了再看,要么看完后只重看新增的 diff。
  • 性能类风险可以挪到第一道坎:光看 diff 很难看出性能会不会变差,跑一次 benchmark 就有数字,我只看数字。
  • 看支付 diff 时,问这四个问题,而不是挑常规错误:
    1. 这次改动可能的失败,REVIEW.md 里写了吗?没写就补;
    2. 验证 skill 覆盖了哪些失败,没覆盖哪些?
    3. 有没有改到检查本身?
    4. 出了问题能不能撤?

8.7 第四道坎和上线后

  • 给 QA 的交接说明(6.2 按需讲解的直接用法):每次提测前让 Claude 生成,写清改了什么、风险等级、重点测哪些失败场景(从 REVIEW.md 取)、自动检查已经覆盖了什么、没覆盖什么。QA 就能把精力放在自动化测不到的地方。
  • iOS 上的对应物:TestFlight 内部测试对应"员工先用";App Store 分阶段发布(7 天内按比例推送自动更新,可暂停)对应 latest → stable;远程开关对应 flag,已有 A/B 实验的远程配置可以顺便当总开关。客户端发出去收不回来,"快速撤销"只能在发版前预先做好。
  • 并行 agent 多了以后,4.2 那种改名冲突迟早出现,用 GitHub merge queue 或 GitLab merge train。
  • 让 agent 把大改动拆成小 PR:文章里 1000 行以上的 PR 大多会审出问题,大 PR 老问题多、更难收敛,拆小后审查轮次、撤销成本、定位难度一起降。
  • 方向检查放在看测试用例清单的时候:我的四道关检查的都是"做得对不对",没有一道检查"该不该做"。写完测试、还没写代码时问"这个该不该做、范围对不对",代价最小。

9. 延伸思考二:苹果支付的验证链

9.1 我的验证 skill

之前做苹果支付自动化时,给 agent 定过一个验证流程:

  • 先检查状态码,并且拿到完整的返回输出,而不只看状态码;
  • 再用只读账号的数据库 CLI 连上开发测试库,按时间戳流判断是不是真的支付成功。

【解读】三点做对了:拿完整返回,就是 3.1 的"拿输出当证据,不听它说做完了";只读账号权限最小,验证时不会误改数据;用数据库时间戳交叉核对,是换一个独立的证据来源。

9.2 验证会怎么骗人:三问

我想到的失败:

  1. 返回的状态码是成功,但可能是脚本误判、误报;
  2. 返回成功了,但没去看数据库,测试实际上并没成功。

第三种当时没想出来。整理成验证链的三问:

  1. 接口说成功了吗?(对应我的第 1 条)
  2. 真的写进数据库了吗?(对应我的第 2 条)
  3. 写进去的是这一笔、只有一笔、而且后续环节都完成了吗?
    • 查到的是旧记录:时间窗口太宽,查到上一次测试留下的成功记录。应该用这次购买的 transactionId 精确匹配,而不是看"最近有没有一条成功的"。
    • 重复处理:同一笔交易被处理两次,比如交易没有 finish,苹果下次会重新推送(待验证:具体重推时机以 StoreKit 文档为准)。要查的是"恰好一条",不是"有没有"。
    • 入库不等于用户拿到权益:订单写进去了,权益没发,或者客户端没解锁。
    • 查错了环境:连的库和服务端实际写入的库不是同一个环境。

9.3 支付代码本身的失败:REVIEW.md 示例

学习时这道题本来问的是"给支付代码写 REVIEW.md,失败是什么样",我回答的是"验证本身会怎么失败"。两件事要分开:9.2 是验证会怎么骗你,属于第 1 层;下面是支付代码本身怎么出事,是 REVIEW.md 要写的。按 3.1 的标准写短:

# REVIEW.md:苹果支付

## 什么算失败
1. 扣了钱,没给权益
2. 没付钱(凭据伪造或被重放),却给了权益
3. 同一笔交易被处理两次(重复入库 / 重复发权益)
4. 退款或过期后,权益没收回

## 证据
- 每条意见给出文件 + 行号,不允许看函数名推测

## 不报
- 纯命名或格式问题,Nit 每次最多 3 条

## 输出
- 开头先写「Important N 条 / Nit M 条」;没有问题就直接写「无需修改」

9.4 成功判据与生产对账

  • 验证 skill 检查的就是 REVIEW.md 里的那些失败。成功判据可以定成:这个 transactionId 在本次运行的时间窗口里恰好一条记录、状态正确、权益已发放。
  • 支付的轻改动不必每次看 diff,但一定看证据:skill 跑出的 transactionId 和查询结果。判断规则用 6.1 那句:说不出检查会漏什么,就认真审。
  • 第 7 层在我这里是空的,支付场景成本最低的补法:把验证 skill 以只读权限拿到生产环境,每天对一次账,比对苹果侧的交易和数据库里的权益记录。"扣了钱没给权益"QA 很难测出来,对账第二天就能发现。

10. 实践检查清单:我的改进方案

10.1 改进后的流程

0 方向     看测试用例清单:这个该不该做?范围对不对?
1 自证     TDD:红在断言上 → 变绿阶段不准改测试 → 性能跑 benchmark → 贴出运行输出
2 自动审查 每次都跑轻量审查;按规则触发对抗审查(行数阈值 / 命中敏感路径)
           第一组找问题 → 第二组判真假 + 值不值得修 → 修 / 不修(写理由)/ 交给人
           最多 3 轮,超过交给人;老问题进 backlog,不在本 PR 修
           加 /security-review
3 人       敏感路径必看:改动轻看证据,改动大看 diff + 证据;问"这些检查会漏掉什么"
4 QA       拿交接说明,重点测自动化没覆盖的部分
5 上线     远程开关 + 分阶段发布
6 发现     每日对账 + 监控;每个漏网 bug 问"本该在哪一层拦住" → 补进 eval / REVIEW.md / 检查

第 0、5、6 层是新增的;第 2、3 层把主观触发改成了规则触发。

10.2 第一批:本周能做,基本只是写文件

这一批专门堵主观判断留下的洞。

  • 写支付的 REVIEW.md;把对抗审查的原则写成文件,(a) 补上"有没有人能故意触发"
  • 第二组结论改成三种:修 / 不修(写理由)/ 交给人;超过 3 轮交给人
  • TDD 两条规矩:红在断言上(贴输出为证);变绿阶段改测试单独标出
  • 敏感路径清单 + 人工三档(不看 / 看证据 / 看 diff),替代"改动轻就不看";改检查本身的一律人看
  • 加 /security-review

10.3 第二批:一两周,补"发现"和"度量"

  • 支付每日对账:验证 skill 只读跑生产库,专抓"扣了钱没给权益"
  • 验证链第三问:按 transactionId 精确匹配,恰好一条、权益已发
  • 记录每条审查意见的结局(四类);QA 每抓一个 bug 回查是"找"漏了还是"筛"掉了
  • 用 10 条 QA bug 和线上 bug 起一个 eval:每条记出 bug 时的 commit、文件和行号、一句话描述
  • QA 交接说明模板

10.4 第三批:要写代码,补"撤"

  • 支付流程、付费墙等高风险客户端功能接远程开关
  • App Store 默认分阶段发布
  • 并行 agent 多了以后上 merge queue / merge train
  • 度量:优化目标是我花在第三道坎上的时间,护栏是 QA 打回率和上线后 hotfix 数。护栏稳住了才放松某一档

先做第一批是因为成本最低、堵的正好是现在最大的洞。最后一条看起来排在最后,其实是之后所有"放松"的前提:没有护栏数据,就不知道哪一档可以松。


11. 术语表

术语 含义
code review(代码审查) 合入前对改动的检查;本文把它扩展成从写代码到上线后的多层检查
revert 把已合入的改动撤回
fix-forward 不撤回,直接再提一个修复往前修
proxy metric(代理指标) 拿来近似真正目标的指标,如"有人读过代码"近似"风险被拦住"
guardrail metric(护栏指标) 优化主指标时必须不变差的指标,本文是 revert 率和事故率
Goodhart's law(古德哈特定律) 一个指标一旦成为目标,就不再是好指标
fresh-context subagent 不带写代码过程上下文、只看 diff 的审查子 agent
CLAUDE.md 告诉 Claude 项目是什么:技术栈、规范、目标、非目标
REVIEW.md 由代码 owner 写,告诉 reviewer 这块代码里什么算失败
finding reviewer 报出的一条意见
Important / Nit / Pre-existing 意见标签:重要问题 / 小问题 / 原来就有、非本 PR 引入
Quick / Standard / Deep Code Review 按仓库设置的三档审查深度
risk tier(风险等级) 低 / 中 / 高,决定需要几个人批准
trusted list(信任名单) 可以走轻量审批的作者名单
CODEOWNERS / branch protection 指定路径必须由谁批准 / 主干分支的合入保护
merge queue / merge train 合入前把改动放到最新主干上重跑检查(GitHub / GitLab 的叫法)
feature flag(功能开关) 不重新发版就能开关某个功能
latest / stable channel 先发的通道 / 晚一步跟进的稳定通道
on-call 轮值负责处理线上问题的人
incident review(事故复盘) 事故后分析原因;本文固定问"本该被哪一层拦住"
circuit breaker(熔断器) 指标变坏时自动收紧审批的机制;文章承认还没有
blast radius(影响范围) 一个改动出错会波及多大范围
eval 一组带已知答案的题目,这里是过去引发事故的 PR
recall(召回) 真 bug 被抓到的比例
precision / noise(精确率 / 噪音) 报出的意见里有多少是真问题 / 假问题
rounds-to-pass(审查轮次) 一个 PR 经过几轮审查和修复才通过
convergence(收敛) 审查和修复的循环能在有限轮内停下
Swiss cheese model(瑞士奶酪模型) 多层各有漏洞的防线叠起来,同时穿过所有层的问题很少
dogfooding(内部先用) 新功能先给自己公司的人用
phased release(分阶段发布) App Store 在 7 天内按比例推送自动更新,可暂停
reconciliation(对账) 比对两边记录是否一致,这里是苹果交易和数据库权益
red-green(先红再绿) TDD 里先看测试失败,再实现让它通过

参考链接

上手:在一个仓库上先跑 /code-review 和 /security-review,加一份 REVIEW.md,按 10.2 的第一批开始。