Claude Code Review
1. 核心理念
1.1 重新理解 code review 的目的
Code review 承担两项职责:
- 在上线前发现并修复问题
- 在团队内建立共享的上下文和代码理解
"有没有人读过这段代码"只是一个粗糙的代理指标。更准确的问题是:
这次改动可能出什么错?哪种检查(自动的或人工的)最适合拦截它?
1.2 从"关卡"变成"持续检查"
| 传统做法 | 新做法 |
|---|---|
| 写完代码 → 开 PR → 等同事看 | 写代码的过程中就持续审查,类似边写边做拼写检查 |
| 人是第一道防线 | 人在 AI 审查和自动检查之后才介入 |
| 逐行阅读每个 diff | 人只看高风险、需要判断的部分 |
1.3 稀缺资源是人的时间和注意力
人工成本包含两部分:
- 审查本身花费的时间
- 坏代码上线后修复所花的时间
只压缩前者而后者上升,并没有省下时间,只是把成本转移了。因此要用回滚率和事故率作为护栏。
目标: 大多数 PR 最多只有一个人参与(可以是作者本人),同时回滚和事故不增加。
2. 一次改动的完整生命周期
提交前 每个 PR 上 合并 合并后
┌──────────┐ ┌────────────────────────────────┐ ┌──────────┐ ┌────────────────┐
│ 自验证 │ → │ 必过 CI → AI 审查 → 安全审查 │ → │ 合并队列 │ → │ Flag / 灰度 │
│ 子代理 │ │ → 按风险分级的人工审批 │ │ │ │ 监控 / 回滚 │
└──────────┘ └────────────────────────────────┘ └──────────┘ └────────────────┘
没有哪一层是完美的。每层各自拦住一部分问题,漏过的由下一层兜底,最后由快速回滚处理剩余部分。
3. 逐层实践
第 1 层:提交前,先验证工作成果
大部分 review 应当在 PR 出现之前完成。
- 让 Claude 实际运行并展示结果 ,而不是只说"完成了":
- 运行测试、构建、lint
- UI 改动对照设计稿截图比对
- 端到端跑一遍真实流程
- 启动一个全新上下文的子代理来审查 diff,避免作者"自己审自己"的盲区。
- 常用的提交前自检动作:
- 查找 bug
- 精简 diff
- 端到端行为检查
- UI 改动对照设计规范
把重复出现的问题固化到源头。 当自动审查反复指出同一类问题,不要每次都在 PR 里改,而是更新:
- lint 规则
- skill
CLAUDE.mdREVIEW.md
CLAUDE.md:告诉 Claude 项目是什么
- 内容:技术栈、约定、目标与非目标
- 每次审查都会加载,因此要保持简短,其他内容用链接指向别的文档
- reviewer 会据此检查代码是否违反约定,以及改动是否让其中某条说明过时
REVIEW.md:告诉 reviewer 什么最重要
由代码 owner 编写,针对各自负责的目录。好的 REVIEW.md 通常:
- 很短
- 定义该仓库中"失败"的样子(例如文档的失败是"说了假话",配置的失败是"路由错误")
- 限制 nit 的数量
- 列出可以跳过的路径
- 要求给出文件和行号作为证据,而不是凭名字猜测
- 要求摘要以数量开头,没有问题时明确说"没有需要修复的内容"
同时,要单独验证 reviewer 是否真的遵循了 REVIEW.md。
第 2 层:每个 PR 都经过 AI 审查
每个 PR 在人看之前都要先经过 Claude 审查。审查应当:
- 对照整个仓库 和 PR 声明的意图阅读 diff
- 判断:这个改动是否解决了它声称的问题?是否契合系统?是否破坏了别的东西?
- 对于 bug 修复,要追到根因,而不是只看表面症状
提高审查质量的机制
- 多个 agent 并行找 bug
- 另一组 agent 对每条发现投票验证,足够多的 agent 认为是真问题才发布
- 过滤掉纯风格、诊断信息、清理类的发现(过滤规则先在自己的 PR 上做 A/B 测试再采用)
- 按严重程度排序 ,并打标签:
Important:重要问题Nit:细枝末节Pre-existing:本 PR 之前就存在的问题(避免让作者背锅)
审查强度按仓库分级
| 档位 | 适用场景 |
|---|---|
| Quick | 常规、低风险的代码 |
| Standard | 大多数仓库的默认选择 |
| Deep | 漏掉一个 bug 的代价高于额外审查时间的地方 |
每条发现必须有回应
作者要么修复 ,要么附上理由关闭该线程。这样 agent 可以在夜间自动接手修复,作者第二天回来时 PR 往往已经过了一轮。
第 3 层:可以阻断合并的安全审查
安全审查是独立的一轮,不与普通 bug 审查混在一起。
- bug 审查或安全审查中任何一条发现未解决,PR 就不能合并
- 清除方式:修复它,或者用简短理由关闭线程(用于处理误报)
- 如果后续 commit 修好了问题,发现会自动消除
第 4 层:自动化检查
这是常规项,但要必须通过才能合并:
- lint
- 类型检查
- 多平台测试
- 构建与打包
PR 模板建议包含:
- Testing:如何测试的
- Risk:风险是什么
- How to revert:如何回滚
Claude 可以帮忙填写,但要由提出需求的人确认签字。
第 5 层:按风险分级的人工审批
文档修改和自动更新器的修改,风险天差地别,所以审批也应该不同。
| 风险 | 典型例子 | 人工审批 |
|---|---|---|
| 低 | 文档、文案、仅内部使用的功能、flag 后的增量改动 | 自动审查和安全检查通过后,无需人工 |
| 中 | 普通产品改动;缓存、自动更新、system prompt 等敏感区域 | 1 人 |
| 高 | 最高风险的代码,以及少数固定必须两人看的路径 | 2 人 |
关键规则
- 由改动决定风险等级,而不是由作者决定。 用分类器读取改动并判定等级;再用第二个分类器判断"即使等级较低,这次改动是否仍需要多一个人看"。
- 有些路径永远需要真人:owner 标记为敏感的内容,以及所有 CI 工作流文件。
- 只有受信任的作者才能走较轻的路径,通常是仓库所属团队的成员,其他人需要另一人批准。
- 机器的批准不能顶替人的批准。 自动审查可以放行低风险改动,但不能填补"需要一个真人"的名额。
- 谁指挥 agent 写代码,谁就是责任人。 对 agent 写的 PR,指挥 agent 的人算作必需的审查者之一,等同于自己亲手写。
- 审批只对特定 commit 有效。 如果之后出现系统无法担保的新 commit(例如 agent 推送、手工解决的合并冲突),需要重新审批。
第 6 层:合并队列、Feature Flag 与灰度发布
- 合并队列:在已应用该改动的最新 main 上重新跑所有检查,防止"两个单独通过的 PR 合在一起把 main 搞坏"。
- Feature Flag:新行为默认放在 flag 后面。
- 内部优先:先让内部员工使用,重度使用往往能赶在用户之前暴露回归问题。
- 灰度发布 :先发
latest渠道,stable渠道稍后跟进。
第 7 层:快速发现与快速回滚
总会有问题漏过前面的层,所以最后一层的关键是快速发现并快速恢复:
- Flag 可以不经发布直接关闭
- 设置每周值班轮换
- 事故复盘时要问:哪一层本该拦住它?
- 发现坏的合并依赖作者、值班人员,以及回滚和事故的仪表盘
- 理想状态:当回滚/事故指标上升时,有自动收紧审批的熔断机制(该团队承认尚未建成,并认为在更多代码走向"零人工合并"之前需要有它)
放慢速度确实会降低错误率,但主要是因为你做得更少了。更好的方向是:更快地发现问题、更快地修复。
4. 如何持续改进 Reviewer
4.1 把事故变成评测集(Eval)
- 收集过去引发事故的 PR,以及对应的修复和回滚
- 请熟悉该代码的工程师确认 bug 以及它所在的位置
- 加入评测集,每个新事故都新增一个用例
- 每天运行 reviewer,对全部用例打分
- 每次修改 reviewer 的 prompt、工具或模型,都用整套评测集重新打分,分数没提高就放弃该改动
4.2 衡量噪音
"多报一些非 bug"很容易让召回率好看,所以必须有对冲指标:
- 按开发者的反应对每条评论分类:
- 已修复
- 说它是错的
- 说这是有意为之
- 认为无所谓
- 在已知干净的 PR 上运行 reviewer,统计它有多少次会给出可能导致有害修改的结论
- 优先级:先抓真 bug,再减少审查轮次
对 agent 作者来说噪音代价更高:人可以几秒钟忽略一个 nit,agent 则会去修,由此产生新的 commit 和新一轮审查。
4.3 让审查收敛
agent 作者和 agent reviewer 可能无休止地互相折腾。文中有一个 CLI PR 一夜之间经历了 50 多轮审查与修复。分析发现,reviewer 反复指出的内容中约 59% 是第一轮审查时就已存在的代码,它并没有在发现新 bug,只是每轮多发现几个旧问题。
收敛措施:
| 措施 | 说明 |
|---|---|
| 轮次上限 | 三轮之后,让作者退一步重新考虑方案 |
| 仅审新增 diff | 前几轮之后,只重审新的改动 |
| 指出整类问题 | 让作者一次性修复所有同类实例,而不是只改一行 |
| 不重复发布 | 已经提出过的发现不再重复 |
效果(内部 PR,改造前 → 改造后):
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 一轮审查修复后通过 | 65% | 78% |
| 需要 5 轮及以上 | 8.9% | 1.5% |
| 平均审查次数 | 2.08 | 1.32 |
此外,每晚让 review agent 巡检已有代码,在旧 bug 在别处爆发之前把它找出来。同时建议把"通过所需轮次"与召回率一起跟踪。
4.4 漏报时的复盘问法
模型当时缺少哪些信息?
然后把答案补进 CLAUDE.md、某个 skill 或一个新的检查里。
5. 人类仍然负责什么
把资深工程师从深度工作中拉出来抓常规错误,成本远高于自动审查。人应当把精力放在机器不擅长的决策上:
- 该不该做这件事? 方向、品味、范围。对来自团队外部的改动,仍保留人工审查,由人来问"我们到底该不该做"。
- 高风险代码:计费、自动更新、CI 工作流等。
- 会被他人效仿的架构:它会成为别人照抄的模板。
- 读证据:测试输出、执行的命令及返回结果、截图、reviewer 的发现。如果说不清这些检查可能漏掉什么,就应该要求真正的审查,而不是草草批准。
- 对结果负责:无论代码是谁写的,每个上线的改动都要有一个人负责。
6. 共享上下文:机器读代码后,人怎么学?
Code review 的第二项职责是传播对代码库的理解。当机器承担了大部分阅读,可以这样做:
- 按需生成讲解:想了解某个模块时,让 Claude 针对那一小块写一份 explainer,几分钟就能生成,且反映的是代码当前的真实状态。
- 生成可视化 artifact:让 Claude 做一个小的可视化页面,用你喜欢的学习方式讲解那部分代码。
- 在开放频道里工作:能看到别人正在做什么,随时参与或指出缺口。
逐行阅读琐碎 diff 学不到什么(一行文案改动几乎没有信息量),而一份针对你即将修改的模块的讲解,价值要高得多。
7. 团队文化:"谁合并,谁负责"
当你合入一个中小型改动并且出了问题,默认由你来修。要让这个规范不变成甩锅,需要:
- 改动要小:易于审查、调试和回滚
- 一切可逆:flag、回滚、分阶段发布能在几分钟内撤销大多数错误
- 爆炸半径决定门槛:代价越高的代码,需要越多人和越多检查
- 回归要快速暴露:依靠遥测和内部使用
- 不是追责:由你来修是因为你上下文最多,事故复盘审视的是系统而不是个人
方向有问题时怎么办
agent 让开始一件事变得很便宜,所以有些工作不该合入。遇到这种情况:
- 尽早在讨论串里提出不同方向,而不是在已完成的 PR 上留一个阻断性评论
- 因为代码写得快,关闭 PR 并不可惜
8. 落地清单
提交 PR 之前
- 给 agent 可运行的检查手段,写进简短的
CLAUDE.md,并用 hook 自动运行格式化和 lint - 本地运行
/code-review与/security-review,或使用新上下文的子代理审查 - 添加
REVIEW.md,把重复出现的审查意见迁移进去
每个 PR 上
- CI 必须通过;PR 模板加入 "Testing"、"Risk"、"How to revert"
- 先在一个仓库接入 AI reviewer,调优
REVIEW.md几周后再扩大范围 - 加入自动安全审查,要求所有审查线程在合并前解决
- 审查强度与风险匹配:常规改动快速过,漏报代价高的地方深度审查
- 限制审查轮次,前几轮之后只重审新增 diff
审批
- 写下风险分级,由改动而不是作者决定;从严格开始,有数据再放宽
- 轻量审批路径只留给受信任的作者
- 用 CODEOWNERS 和分支保护,确保认证、计费、CI 与发布工作流、安装器等始终需要真人
- 机器人的批准绝不能替代必需的人工批准;新 commit 后让旧审批失效
合并与之后
- 使用合并队列、feature flag、内部优先使用、分阶段发布和一步回滚
- 把事故和回滚做成评测集,每次改动 reviewer 的 prompt、工具或模型都重新打分
- 度量人工时间,以回滚率和事故率作为护栏
9. 衡量指标速查
| 类别 | 指标 |
|---|---|
| 人工成本 | 每个 PR 的人工审批数(中位数、P95)、人工评论数、PR 等待人的时长 |
| 护栏 | 回滚率、事故率 |
| Reviewer 质量 | 召回率(基于事故评测集)、评论被采纳比例、在干净 PR 上的误报率 |
| 收敛性 | 通过所需轮次、一轮通过率、5 轮及以上占比 |
10. 小结
- 验证先行:让 agent 自己跑出证据
- 分层防御:没有任何一层是完美的,层与层互相兜底
- 按风险分配人力:低风险自动化,高风险必须有人
- 机器不能顶替必需的人:批准权最终在人手里
- 让审查收敛:设置轮次上限,避免 agent 之间无休止空转
- 一切可逆:把"快速发现、快速回滚"当作最后一道防线
- 事故即评测:每次漏报都让 reviewer 变得更好
最终目标没有变:每个改动消耗更少的人工时间,同时回滚不比以前更多。