代码评审实践
代码评审(Code Review)最大的价值不是抓几个 typo 或风格问题——那些 lint 工具能做。它真正的作用是:让设计决策被更多人理解、把隐式知识显式化、在合并前发现结构和风险层面的问题。评得好,团队整体水平一起涨;评得差,变成拖慢交付的形式主义。
评审看什么
把注意力分配到机器查不出、人最有价值的地方:
| 层次 | 看什么 | 例子 |
|---|---|---|
| 结构 | 设计是否合理,职责是否清晰 | 把业务逻辑塞进工具函数、跨层调用 |
| 边界 | 异常、空值、并发是否处理 | 只考虑正常路径,忘了失败和重试 |
| 风险 | 安全、性能、数据完整性 | 信任外部输入、N+1 查询、缺事务 |
| 命名 | 是否准确传达意图 | 叫 data 的变量、叫 process 的函数 |
| 测试 | 是否覆盖关键行为,是否测了不该测的 | 没有失败路径测试、过度 mock |
风格、格式、import 顺序这类问题,交给 lint 和格式化工具自动处理,不要在评审里反复提。评审者的时间是稀缺资源,要花在机器替代不了的地方。
怎么给反馈
反馈的质量决定评审是建设性的还是对抗性的:
text
差的反馈:
"这样写不好。"
"为什么不用 X?"
好的反馈:
"这里直接读 request.body 没有校验类型,
如果传入数组会让后面的 .trim() 报错。
建议在入口处加一层校验。"几个原则:
- 对事不对人:说"这段代码在并发时可能丢更新",而不是"你怎么又写了并发 bug"。
- 说清为什么:不光指出问题,还解释为什么是问题、影响是什么。
- 给方向而非命令:提建议和理由,而不是强令对方照你的写法改。除非是明确的错误,否则留空间让作者决策。
- 区分必须改和建议:用"必须"和"建议"区分阻塞性问题和可选优化,别让作者猜哪些不改过不了。
- 肯定好的部分:评审不是只找错,看到清晰的设计、好的命名、周全的测试,说出来,强化这些。
作者怎么准备评审
评审效率不只取决于评审者,也取决于提交者准备得怎么样:
- PR 要小:一个 PR 聚焦一件事。几百行的混合改动没人能认真审,最后只能走个过场。
- 写清楚背景:这个改动解决什么问题、采用了什么方案、为什么这么选。评审者没有你的上下文。
- 自己先过一遍:提交前用自己的代码 diff 视角审一遍,常常能发现遗留的调试代码、注释掉的逻辑。
- 标注重点:在复杂或有疑问的地方留注释,引导评审者关注关键部分。
text
好的 PR 描述:
修复用户重复提交导致订单重复创建的问题。
方案:在创建订单前用 Redis SETNX 加幂等锁,
锁 key 用 userId + 请求 traceId,TTL 5 分钟。
请重点看 lockKey 的生成和失败时的降级逻辑。评审的节奏
评审不该成为交付的瓶颈,但也不能无限期搁置:
- 当天响应:收到评审请求尽量当天处理,阻塞的 PR 拖着会迫使作者合并未经审的代码或闲置等待。
- 控制单次评审量:一次审 200-400 行效果最好,超过这个量注意力下降、问题漏看。
- 不要囤积:留一大堆 PR 周五集中审,质量一定差。分散到日常,每次专注。
当意见不一致时
技术分歧是正常的,处理不好会变成僵局或人情债:
- 先理解对方:复述对方的方案和理由,确认你理解对了再反驳。很多"分歧"其实是误会。
- 用事实和权衡:说清两种方案各自 tradeoff,而不是"我觉得这样更好"。
- 小范围验证:有争议时,写个 spike 或跑个数据,用证据说话。
- 知道何时升级:僵持不下时,找资深成员或负责人决策,而不是在评论里无限轮辩。
- 接受合理的不完美:不是每个分歧都要争出最优解。在不破坏核心约束的前提下,接受作者的选择,记录决策原因,比强求一致更健康。
务实的评审清单
- 是否聚焦结构、边界、风险,而不是纠结风格?
- 反馈是否说清了原因、区分了必须改和建议?
- PR 是否足够小、背景是否写清楚?
- 评审是否及时响应,没有成为交付瓶颈?
- 分歧是基于权衡讨论,还是变成了立场之争?
代码评审是团队里最高频的知识传递场景。一次好的评审,让作者知道为什么这么改、让评审者理解这块业务、让后来的读者从讨论记录里看到决策脉络。把它当作"一起把事情做对"的协作,而不是"我审你"的关卡,它的价值才能真正发挥出来。