代码评审看什么
一个合并请求收到了二十多条评论,全部关于命名、空行和导入顺序。合并上线后,一个循环里的数据库查询在批量导入时被执行了十万次,服务被拖垮。事后翻评审记录,那个循环在代码里清清楚楚,只是没有人看它。
代码评审的注意力是有限的。评审者在一个合并请求上花的时间大致固定,看了格式就少看了逻辑。更麻烦的是,最容易发现的问题(命名、格式)往往代价最小,最难发现的问题(失败路径、批量成本、契约变化)代价最大。不刻意安排,评审就会自然地落到容易的那一层。
这一篇讲评审的顺序、改动的大小和评论的分级。它是方法文,没有配套实验。
一、先看错了代价最大的那一层
1. 失败路径与数据一致性。 正常路径作者已经测过了,评审最有价值的地方是没有被测过的路径:
- 这一步失败时,前面已经做的事怎么办?数据是否处于一个业务上不存在的状态?
- 超时之后会重试吗?重试时对方可能已经执行过一次了吗?
- 异常是否被吞掉了,或者被另一个异常盖住了?
- 并发时两个请求交错执行,结果是否还对?
失败路径上的常见问题,见出了错,日志里却没有可用的信息与每层都重试三次,最底下的服务收到二十七倍请求。
2. 契约与兼容。 这次改动有没有改变别人依赖的东西:
- 对外的字段名、接口签名、错误码、配置键、消息格式、数据库结构。
- 旧数据(队列里积压的消息、缓存里的对象、数据库里的历史行)还能被正确读取吗?
- 回滚到上一个版本时,新版本写入的数据旧版本能读吗?
改名、加字段、换枚举都可能是契约变化,见名字说的是什么与上游加了一个字段,下游全部反序列化失败。
3. 整体成本。 局部看起来整洁的代码,整体上可能很昂贵:
- 循环里有没有数据库查询、远程调用、文件读写?批量执行时会被调用多少次?
- 一个方法提取得很干净,但它被调用了三次,每次都查一遍数据库。
- 数据量从测试时的十条变成生产的一百万条,内存和时间会怎样?
把方法拆小会让这类问题更难看出来:重复的 I/O 分散在几个名字很好的小方法里。评审时要沿着一次请求的完整调用路径看,而不是只看改动的那几行。
4. 可读性与结构。 名字、职责、重复、测试是否清楚。这一层很重要,它决定了下一个人修改时的成本,但它的问题通常可以在后续修复,不必阻塞合并。格式和导入顺序交给自动化工具,不占用评审者的注意力。
二、改动越小,越能看到前两层
几十行的改动,评审者可以在脑子里把每条路径都走一遍;几千行的改动,只能扫一遍看有没有明显的问题,落到第 4 层。改动的大小决定了评审能达到的深度。
几个让改动变小的做法:
- 重构与行为变更分开提交。 先提交一个纯重构(改名、提取、移动),评审者只需要确认行为没变;再提交行为变更,这时候改动集中在少数几处。
- 按可独立验证的结果切分。 一个大功能拆成几个可以单独合并、单独验证的部分,而不是按技术层(先合并所有数据库改动,再合并所有接口)。
- 按业务结果拆分需求。 一个需求按纵向切片拆开,每个合并请求自然就小,而且每个都能单独演示和验收,见把需求切成可验证的小块。
- 缩短等待。 合并请求等评审的时间越长,作者越倾向于把更多改动攒在一起。及时响应比一次评审得很仔细更能控制改动的大小。
三、评论要分级
二十条评论混在一起,作者分不清哪些必须改。评论应当标明它的性质:
| 级别 | 含义 | 例子 |
|---|---|---|
| 阻塞 | 不改不能合并 | 失败时数据不一致;契约变化没有兼容;明显的性能问题 |
| 建议 | 评审者认为更好,作者可以不采纳 | 换一个更清楚的名字;提取一个方法 |
| 问题 | 评审者没看懂,需要作者解释 | 这里为什么要先查再写? |
| 记录 | 不在本次处理,另开任务 | 这一块的结构可以重构,但与本次改动无关 |
「问题」这一类特别有价值:评审者看不懂的地方,下一个维护者大概率也看不懂。作者的回答应当变成代码里的改进(更好的名字、一条说明为什么的注释),而不只是在评审对话里解释一遍。
有分歧时,回到具体的证据:一个会失败的输入、一个测量结果、一条规范的原文。「我觉得这样更好」与「我觉得那样更好」之间无法收敛;一个具体的反例可以。
四、评审之后
- 阻塞意见修改之后要再看一次。 改动可能引入了新问题,或者没有真正解决原问题。
- 记录类的意见要有去处。 写进任务列表,否则「以后再改」就是永远不改。
- 反复出现的问题,用工具或约定解决。 同一类问题在每次评审里都要指出,说明它应该变成一条自动化检查、一个代码模板,或者一份团队约定。
- AI 生成的代码同样要过这几层。 生成的代码通常格式整洁、命名规范,第 4 层很少有问题,这恰恰会让评审者放松对前三层的检查。见 AI 辅助编码的工程纪律。
五、检查清单
- 先看失败路径与数据一致性,再看契约与兼容、整体成本,最后看可读性。
- 沿一次请求的完整调用路径看 I/O 次数,不只看改动的几行。
- 重构与行为变更分开提交;改动尽量小。
- 格式交给工具。
- 评论标明级别:阻塞、建议、问题、记录。
- 有分歧时用具体的反例或测量结果讨论。
- 阻塞意见改完再看一次;记录类意见有去处。
参考资料