一次评审吵了两天,落脚点是"这个变量该叫 data 还是 payload"。两个人各自引用风格指南、翻出三个旧文件,把 40 行的改动拖成了跨周任务。问题不在谁对,而在这场争论从一开始就不该以"争"的形式发生。
评审意见不是一类东西。"这里会空指针"和"我不喜欢这个命名"都像建议,但举证方式、可证伪性、该由谁决断完全不同。混进同一个"讨论"里,团队就必然为最便宜的那类意见付最贵的成本。
1. 三类意见与真实占比
| 类别 | 判据 | 可证伪 | 占比量级 | 该由谁决断 |
|---|---|---|---|---|
| 正确性 | 有输入能让它出错 | 是 | 一到两成 | 作者举证,评审者复核 |
| 可维护性 | 半年后还能不能改 | 部分 | 两到三成 | 协商,owner 裁 |
| 偏好 | 无外部判据,只有习惯 | 否 | 五到六成 | 作者,且不阻塞 |
占比是典型量级,不是测量值;分布随语言、工具链、代码年龄移动,但结构稳定:偏好类天然占多数,因为它最容易被说出来。说"这里有竞态"要读代码,说"命名不好"只需看一眼。
三类混在一条时间线里,讨论就会沿最容易表达的那类漂移。两天的争论通常不是有人固执,而是没有机制把可证伪与不可证伪分开。
2. 可证伪性是分水岭
| 主张 | 举证方式 | 争不出结果的原因 |
|---|---|---|
| 并发下计数器会丢更新 | 给出交错顺序、复现步骤 | 一次就能定论 |
| 这层抽象让改动要改三处 | 列出受影响调用点 | 能收敛到"改或不改+代价" |
| 这个命名读起来不顺 | 无 | 换一个仍有异议,无限循环 |
前两类能被外部事实裁决:跑一次、数一下调用点、看一份日志。第三类没有外部事实,只有语感,争论时长不取决于论据质量,只取决于谁先累。
判据:靠"我觉得"支撑的意见,不该拥有阻塞权。
3. "必须改"与"建议改"的定级权
| 表达 | 隐含要求 | 谁定级 | 常见误用 |
|---|---|---|---|
| 必须改 / blocking | 不合并不行 | 评审者,须给判据 | 偏好升格为阻塞 |
| 建议改 / nit | 可选 | 作者 | 降级正确性问题换速度 |
| 我不确定,想确认 | 请求信息 | 双方 | 被读成否决 |
| LGTM,但…… | 批准 + 备注 | 评审者 | 备注里藏真问题 |
定级责任在评审者,约束是:评 blocking 必须同时给判据(复现路径、契约条款、失败场景),否则退回成建议。这条把"我要求"变成"事实要求"。
作者的反向责任是不能把正确性问题标成 nit 换速度。降级要留痕,不是私下让步。
─── 一条合格的阻塞意见
blocking: 重试上限 4 次 × 单次 800ms 超过上游 3s 超时预算
判据: 契约 4.2 节;超时后上游重排,导致重复扣款
复现: 注入 3s 延迟,order 表出现两条 paid 记录
修复建议: 上限降为 2 次,或总预算封顶 1.5s
4. 阻塞项超过三条等于没有优先级
评审里出现五条 blocking,作者的实际行为是:先改最容易的两条,然后申请合并,剩下的"下次一起改"。不是他不负责,是清单没告诉任何人哪条最要紧。
| 阻塞项 | 作者反应 | 后果 |
|---|---|---|
| 1 | 改掉 | 一次收敛 |
| 2–3 | 全改,可能延后一天 | 可控 |
| 4–6 | 改一半,其余转 TODO | 问题沉入技术债 |
| 7 以上 | 重写或换人 | 讨论转移到"流程太重" |
硬约束:只保留最关键的 1–3 条 blocking,其余降为建议或转独立任务。降级不是放弃,是把"不阻塞这次合并"和"必须被处理"分开记账。
5. 该看什么,不该看什么
| 关注点 | 花人的时间吗 | 理由 |
|---|---|---|
| 语义是否正确 | 是 | 工具判断不了意图 |
| 边界(空、越界、并发) | 是 | 漏掉即事故 |
| 失败路径与重试语义 | 是 | 最常被跳过、最贵 |
| 迁移可逆性 | 是 | 不可逆没有第二次机会 |
| 格式、命名、import 顺序 | 否 | 确定性规则,交给 linter |
| 偏好的抽象层次 | 否(除非给出维护场景) | 无外部判据 |
后两行不是不重要,是不该占用人类带宽。它们的边际检查成本近乎零,而人类带宽稀缺。看到格式问题就说出来,等于用最贵的资源做最便宜的事。
6. "我看不懂"是合法的高价值意见
最常见的自我审查是:"我没完全理解,可能是我水平不够,先不说了。" 代价远低于它掩盖的风险:熟悉这块代码的人都读不懂,半年后的维护者只会更读不懂。
表达要可处理,不能只丢一句"看不懂":
─── 读不懂的地方
第 3 步的补偿为什么放在事务外?
我推不出"扣款成功但订单未创建"如何不残留。
─── 我尝试的假设
A:靠下游对账修复(窗口多长?)
B:靠幂等键重放(键在哪生成?)
请指出我漏掉的那层。
它的价值在于不指责代码,只暴露理解断层的位置,几乎总能换来"确实没写清楚"——要么补注释,要么发现真缺陷。
最没价值的是"这不是我写的风格":它不是判据,是身份陈述。团队标准只在三处存在——工具配置、文档、被反复引用的先例。都没有,就说明没有标准。
7. 作者的处置权与举证责任
| 意见类型 | 作者能做什么 | 需举证 | 留痕 |
|---|---|---|---|
| 正确性 blocking | 修或反驳 | 反驳时举证 | 理由进 PR |
| 可维护性建议 | 采纳/部分采纳/拒绝 | 拒绝给一句理由 | 简短即可 |
| 偏好 | 自主决定 | 不需要 | 不需要 |
| 无法理解 | 补注释或改结构 | — | 说明改了什么 |
作者有处置权,没有沉默权。正确性意见被拒时必须给判据:"上游已做幂等"是合格反驳,"我觉得没问题"不是。
值得单独记录的机制:评审者提出、作者拒绝、问题最终发生的意见。不为追责,为校准置信度。
// 拒绝理由的最小记录形态(可检索即可,不必进 PR 正文)
decision: 拒绝
意见: 重试上限应为 2 次
理由: 上游契约已幂等,4 次不会重复扣款(contract.md 4.2)
回访: 若 30 天内出现重复扣款,回归此决策
8. 架构问题不该在代码评审里被发现
| 问题层级 | 成本最低的阶段 | 在代码评审里发现意味着 |
|---|---|---|
| 模块边界、依赖方向 | 设计评审 | 已写了几百行才暴露 |
| 数据模型与一致性 | 设计/方案评审 | 迁移脚本已在排队 |
| 接口契约 | 契约评审 | 上下游已按错的理解实现 |
| 实现细节、边界条件 | 代码评审 | 正常 |
| 命名与格式 | 工具 | 不该出现在这里 |
代码评审的职责是实现是否符合已定设计,不是重做设计。当一个改动被指出"这个服务的职责不该这样切",正确处置通常不是改代码,而是停下来回设计层——否则就是在评审里做了一次没有记录的设计决策。
9. 同一类意见出现三次就该工具化
| 次数 | 处置 | 理由 |
|---|---|---|
| 第 1 次 | 评论说明 | 建立认知 |
| 第 2 次 | 评论 + 记入规则候选 | 确认是模式 |
| 第 3 次 | 写成 lint / 检查 / 模板 | 人不该重复同一判断 |
| 第 4 次以后 | 仍靠人提醒 | 规则化没落地,是流程缺陷 |
产物必须可执行:
// 三次定律的落地形态
// 1) 命名与格式 → .editorconfig + linter
// 2) 依赖方向 → 分层断言(架构测试)
// 3) 错误处理缺失 → 静态检查,命中即 CI 失败
// 4) 迁移缺回滚 → PR 模板必填 + 目录级检查脚本
隐性成本是维护:写死的断言会在架构合法演进时误报。每条规则都要有 owner 和失效条件,否则三年后没人敢删,团队开始绕过它。
10. 度量:四个数
| 指标 | 定义 | 读法 | 误读风险 |
|---|---|---|---|
| 意见分类分布 | 三类各占多少 | 偏好占比高说明工具缺位 | 人工标注有主观性 |
| 被采纳率 | 采纳/提出 | 过低是脱离上下文 | 也受权威压力 |
| 争论时长 | 单线程到收敛 | 长线程多为不可证伪议题 | 快速收敛可能被压制 |
| 规则化转化数 | 转成工具的类数 | 团队学习速度 | 只数不维护会积死规则 |
// 评论粗分类:关键词只是起点,人工复核不可省
correctness : 空指针 / 越界 / 竞态 / 事务 / 幂等 / 超时
maintain : 重复 / 耦合 / 命名误导 / 缺注释 / 难测试
preference : 我觉得 / 习惯 / 风格 / 换一种写法
// preference 长期过半而工具配置没变,问题在工具不在人
11. 排错:五种"评审吵起来"
| 现象 | 真实原因 | 处理 |
|---|---|---|
| 一条线程几十楼 | 议题不可证伪,靠语感拉锯 | 给判据否则降级 |
| 作者全照改但很慢 | 阻塞项过多 | 砍到 1–3 条 |
| 评审者不再提意见 | 提了不被采纳也无反馈 | 记录拒绝理由 |
| 每个 PR 争同一件事 | 该工具化的仍在靠人 | 第三次就写规则 |
| 评审很快但线上返工 | blocking 被降成 nit | 定级归评审者 |
第三条最隐蔽:沉默不是认同,是退出。指标上(意见数降、评审变快)全部向好,实际是判断能力在流失。
12. 边界:什么时候不必较真
| 场景 | 争吗 | 理由 |
|---|---|---|
| 三人小队、作者长期维护同模块 | 不必 | 沟通成本低于规范收益 |
| 已有格式化与强静态检查 | 不必(格式类) | 机器已覆盖 |
| 一次性脚本、demo、临时迁移 | 不必 | 寿命短于维护成本 |
| 相邻风格不一致但无功能影响 | 不必 | 一致性收益低于改动风险 |
| 语义正确性、失败路径、不可逆操作 | 必争 | 后果由用户承担 |
| 同类意见反复出现 | 必争(转规则) | 写一条断言胜过争一次 |
压成一句:争的收益 = 出错后果 × 概率,成本 = 双方时间 + 关系损耗。 后果由用户承担的必须争;只由美观承担的,交给工具或作者。
13. 自测
- 一条没有复现路径也没有契约条款的意见,该有什么级别的权力?
- 五条 blocking 同时出现时,作者的真实行为是什么?
- "我看不懂这段"为什么比"这里命名不好"更值得说?
- 评审里发现"服务职责切错了",改代码还是回设计?
- 同类意见第三次出现,团队该做什么而不是说什么?
- 意见数下降、评审变快、线上返工上升,最可能的解释?