当代码不再稀缺 - 05
Code review 没有过时,但我们可能 review 错了对象
AI can produce a diff. Review decides whether the team is willing to own the change.
AI会产出代码,但是团队应该决定是否拥有这个改变
床位系统那个 bug 暴露以后,我反复想过一个问题:
我们明明做了 code review,为什么还是没有发现这个bug?
实现并不离谱。代码在判断查询时刻是否落在预约区间内,局部逻辑说得通,边界也长得像正常的时间处理。
只是业务真正问的是“这一天是否已经被占用”,代码回答的却是“当前这一秒是否落在区间”。
我们不是没看代码。我们看了一个错误问题的正确答案。
这件事让我意识到,AI 时代关于 code review 最危险的讨论,不是“以后还要不要 review”,而是:
我们到底在 review 什么?
旧 Review Contract 有一个隐藏前提
过去的 code review,大多建立在一条没有写出来的 assumption 上:
Author 接到需求
→ Author 理解问题
→ Author 亲手实现
→ Diff 是这段理解的主要表达
→ Reviewer 通过阅读 diff 判断实现是否合理
这条链当然有时也会断。工程师从来不会因为亲手敲键盘就自动获得业务知识。
但至少在大多数情况下,author 在实现过程中被迫经历了一遍系统:查代码、撞 constraint、理解 状态、debug、改 tests。这个过程很慢,却顺便把一些业务知识压进了人的脑子里。
AI 把中间最慢的一段压缩以后,链条变了:
Ticket / prompt
→ Agent 生成 code + tests
→ 工程师选择 candidate
→ PR
→ Reviewer 面对一个完成度很高的 diff
现在,一个 PR 可以在 author 还没有形成完整 mental model 之前就出现。
Diff 看起来更像完成品,背后的理解却未必同比例增加。
如果 reviewer 仍然把任务定义成“把这几百行代码看一遍”,很容易发生两个极端:
- 因为不信任 AI,退回逐行 proofread,review time 暴涨;
- 因为 tests 都绿了,把 PR 当作已经完成,只检查明显问题。
一个看得太细,一个看得太浅。两边都可能漏掉两件件事:这个改动为什么应该存在;团队凭什么相信它。
Review 的对象不是文本,是改动
我现在更愿意这样定义 code review:
Review 是团队决定是否愿意承担一个改动的过程。
代码当然仍然是重要 evidence。很多时候,不读关键路径根本不可能判断风险。
但代码不是 review 的全部对象。真正需要判断的是五件事。
第一件:Intent Delta
先别急着打开 Files changed。
先问:
- 用户或系统行为到底改变什么?
- 原始问题、ticket 和 PR 描述是否一致?
- 这个实现回答的是不是原来的问题?
这一步听起来像 PM 的工作,其实不是。
PM 对问题和优先级负责,工程师对 change 如何进入系统负责。如果 PR author 不能用几句话解释 behavior delta,reviewer 大概率只能从代码反推 intent。
这相当于拿答案猜题目。偶尔能猜中,不适合当 operating model。
第二件:Invariants 和 Assumptions
AI 很擅长把缺口补成一个 plausible answer。
所以 reviewer 需要问:
- 哪些业务规则必须始终成立?
- 哪些 assumption 来自原始需求?
- 哪些是工程师的设计决定?
- 哪些可能只是 agent 为了完成实现而补出来的?
- 时间、权限、状态转换、并发和数据完整性有什么边界?
床位 bug 漏掉的不是一行语法,而是“系统按业务日判断占用”这个 invariant。
如果 invariant 没有进入 PR,逐行读得再认真,也只能检查代码是否忠实执行了一个不完整的问题。
第三件:System Fit
Agent 往往能快速完成局部任务,但它产生的局部答案必须放回整个系统里看。
Reviewer 需要判断:
- Repository 里是否已经有相同能力?
- 是否绕过现有 abstraction 或 contract?
- Change surface 实际有多大?
- 是否引入 migration、兼容性或长期 coupling?
- 这是一个合理增量,还是又在系统边上长了一块新代码?
AI 降低了新增代码的边际成本,没有同步降低未来理解代码的成本。
所以 review 不应该只问“这段能不能工作”,还要问“我们是否愿意以后继续拥有它”。
第四件:Verification Evidence
“Tests passing”经常是最让人安心的绿色。
问题是,code 和 tests 可能来自同一个 ticket、同一个 prompt,甚至同一个 agent。
它们可以完美一致,然后一起偏离业务 intent。
Reviewer 需要继续追问:
- Tests 验证的是业务行为,还是复述 implementation?
- 有没有 negative path 和 boundary case?
- 有没有来自历史 bug、production sample 或 domain expert 的 independent oracle?
- Generated tests 和 generated code 是否共享同一个 assumption?
- 如果判断错了,系统如何发现、隔离和恢复?
多个绿灯不一定是多份 evidence。有时候只是同一个 assumption 换了几个窗口显示。
第五件:Operability
一个 change 能通过 tests,不代表进入 production 后就可观察、可诊断、可恢复。
Reviewer 还需要检查:
- Logs、metrics、trace 是否足以定位问题?
- Dashboard 能否区分 rollout cohort 和 feature version?
- Feature flag 和 rollback 是否可用?
- Telemetry 的数据口径是否可信?
- PM / Ops 上线后能否判断功能真的在工作?
这里的责任边界很重要。
PM / Ops 负责 operational acceptance 和 outcome interpretation;Eng 负责 functional verification,以及让结果可验证的 evidence infrastructure。
Dashboard 不是工程师把责任交出去的仪式。如果埋点错了,PM 只会对一个错误数字进行更加专业的分析。
Reviewer 不应该替 Author 第一次理解 Change
如果 author 提交的只有 diff,reviewer 就被迫做 context archaeology:翻 ticket、找 Slack、猜 prompt、读几百行代码,再问“所以我们为什么改这个?”
这正是 AI slop 对 attention 征税的一种方式。
Author 应该随 PR 一起提交一个最小 Evidence Package:
Intent / Non-goals
Invariants
Assumptions
Change surface
Evidence produced
Evidence missing
Rollout / Rollback
Observability
它不需要是一份长文档。低风险 change 可能只需要 PR template 里的几段话。
重点是:author 是第一层 accountable aggregator。Reviewer 是独立判断,不是替 author 第一次建立 ownership。
先让机器处理机器擅长的部分
Human attention 不应该浪费在稳定可自动化的检查上。
进入 human review 前,可以先完成:
- Formatting、lint 和 types;
- Unit / integration tests;
- Dependency、secret 和基础 security scans;
- Failed gates 汇总;
- Duplicate-code 和 suspicious generated artifact 检查;
- PR size 和 change surface 摘要。
这些 gate 不能证明 change 正确。
它们的价值是减少低价值 attention demand,让 reviewer 把时间留给机器不擅长承担的判断。
Review 不是只有 Approve 和 Request Changes
很多 review comment 都长得像 implementation patch:这里改名,那里抽函数,再补一个 null check。
新 Review Contract 需要允许 reviewer 更准确地说出问题在哪一层:
- Intent unclear;
- Invariant missing;
- Assumption unresolved;
- Evidence insufficient;
- System-fit concern;
- Operability gap;
- Safe to ship。
如果问题是 evidence 不足,就不要伪装成一条代码风格意见。
如果问题是 intent 不清楚,也不要让 author 再生成一版 diff 碰运气。
高风险代码还是得逐行看
对。
安全、账务、权限、数据迁移和关键状态机,很多时候就是需要逐行理解。AI 生成不构成少看代码的理由。
但这并没有推翻前面的论点。
逐行 review 可能是必要条件,但从来不是充分条件。
你可以逐行确认每个时间比较都写对了,仍然漏掉“业务按天,不按秒”的问题。
所以问题不是“还要不要读代码”,而是先根据 risk 决定读到什么粒度,同时确保 intent、invariant、system fit、evidence 和 operability 没有被 diff 遮住。
按风险分配 attention 是第二章的问题。第五章只回答一件事:当 attention 已经投入 review,它应该判断什么。
下周可以做的一个实验
选一个正在进入 review 的 PR。
要求 author 先提交一页以内的 Evidence Package。Reviewer 暂时不要打开 diff,先写下:
- 这个 change 的 intent 是什么;
- 最大 assumption 或 risk 是什么;
- 什么 evidence 足以接受;
- 哪些代码路径需要重点阅读。
Review 结束后,把 comments 粗略分成:
- Implementation;
- Intent / invariant;
- System fit;
- Evidence;
- Operability。
如果绝大多数 comment 都是命名和局部写法,而 escaped defect 经常来自业务语义或系统边界,那不是 reviewer 不认真。
可能是 Review Contract 过时了。
Code review 没有因为 AI 失去价值。恰恰相反,当 authoring 变得廉价,团队更需要一个地方决定哪些 change 值得拥有。
只是这个决定不能只靠读完 diff。