Skip to content

代码评审看什么 ​

一个合并请求收到了二十多条评论,全部关于命名、空行和导入顺序。合并上线后,一个循环里的数据库查询在批量导入时被执行了十万次,服务被拖垮。事后翻评审记录,那个循环在代码里清清楚楚,只是没有人看它。

代码评审的注意力是有限的。评审者在一个合并请求上花的时间大致固定,看了格式就少看了逻辑。更麻烦的是,最容易发现的问题(命名、格式)往往代价最小,最难发现的问题(失败路径、批量成本、契约变化)代价最大。不刻意安排,评审就会自然地落到容易的那一层。

这一篇讲评审的顺序、改动的大小和评论的分级。它是方法文,没有配套实验。

1. 失败路径与数据一致性异常、超时、重试、部分成功时数据会处于什么状态?回滚与幂等是否成立?2. 契约与兼容对外的字段、接口、配置、存储格式有没有变?旧数据和旧调用方怎么办?3. 整体成本循环里有没有查询或远程调用?批量时的 I/O 次数与数据量是多少?4. 可读性与结构名字、职责、重复、测试的可读性。重要但通常可以在后续改一次评审的改动量越小,越能看到第 1、2 层;几千行的合并请求,大多只剩第 4 层的意见
图 1 · 示意:评审的注意力有限,先看错了代价最大的那一层。格式与命名可以交给工具和一句建议;失败路径、跨调用的成本与契约变化一旦漏掉,就是线上事故。评论也按这个顺序分级:哪些必须改才能合并,哪些是建议。

一、先看错了代价最大的那一层 ​

1. 失败路径与数据一致性。 正常路径作者已经测过了,评审最有价值的地方是没有被测过的路径:

  • 这一步失败时,前面已经做的事怎么办?数据是否处于一个业务上不存在的状态?
  • 超时之后会重试吗?重试时对方可能已经执行过一次了吗?
  • 异常是否被吞掉了,或者被另一个异常盖住了?
  • 并发时两个请求交错执行,结果是否还对?

失败路径上的常见问题,见出了错,日志里却没有可用的信息与每层都重试三次,最底下的服务收到二十七倍请求。

2. 契约与兼容。 这次改动有没有改变别人依赖的东西:

  • 对外的字段名、接口签名、错误码、配置键、消息格式、数据库结构。
  • 旧数据(队列里积压的消息、缓存里的对象、数据库里的历史行)还能被正确读取吗?
  • 回滚到上一个版本时,新版本写入的数据旧版本能读吗?

改名、加字段、换枚举都可能是契约变化,见名字说的是什么与上游加了一个字段,下游全部反序列化失败。

3. 整体成本。 局部看起来整洁的代码,整体上可能很昂贵:

  • 循环里有没有数据库查询、远程调用、文件读写?批量执行时会被调用多少次?
  • 一个方法提取得很干净,但它被调用了三次,每次都查一遍数据库。
  • 数据量从测试时的十条变成生产的一百万条,内存和时间会怎样?

把方法拆小会让这类问题更难看出来:重复的 I/O 分散在几个名字很好的小方法里。评审时要沿着一次请求的完整调用路径看,而不是只看改动的那几行。

4. 可读性与结构。 名字、职责、重复、测试是否清楚。这一层很重要,它决定了下一个人修改时的成本,但它的问题通常可以在后续修复,不必阻塞合并。格式和导入顺序交给自动化工具,不占用评审者的注意力。

二、改动越小,越能看到前两层 ​

几十行的改动,评审者可以在脑子里把每条路径都走一遍;几千行的改动,只能扫一遍看有没有明显的问题,落到第 4 层。改动的大小决定了评审能达到的深度。

几个让改动变小的做法:

  • 重构与行为变更分开提交。 先提交一个纯重构(改名、提取、移动),评审者只需要确认行为没变;再提交行为变更,这时候改动集中在少数几处。
  • 按可独立验证的结果切分。 一个大功能拆成几个可以单独合并、单独验证的部分,而不是按技术层(先合并所有数据库改动,再合并所有接口)。
  • 按业务结果拆分需求。 一个需求按纵向切片拆开,每个合并请求自然就小,而且每个都能单独演示和验收,见把需求切成可验证的小块。
  • 缩短等待。 合并请求等评审的时间越长,作者越倾向于把更多改动攒在一起。及时响应比一次评审得很仔细更能控制改动的大小。

三、评论要分级 ​

二十条评论混在一起,作者分不清哪些必须改。评论应当标明它的性质:

级别含义例子
阻塞不改不能合并失败时数据不一致;契约变化没有兼容;明显的性能问题
建议评审者认为更好,作者可以不采纳换一个更清楚的名字;提取一个方法
问题评审者没看懂,需要作者解释这里为什么要先查再写?
记录不在本次处理,另开任务这一块的结构可以重构,但与本次改动无关

「问题」这一类特别有价值:评审者看不懂的地方,下一个维护者大概率也看不懂。作者的回答应当变成代码里的改进(更好的名字、一条说明为什么的注释),而不只是在评审对话里解释一遍。

有分歧时,回到具体的证据:一个会失败的输入、一个测量结果、一条规范的原文。「我觉得这样更好」与「我觉得那样更好」之间无法收敛;一个具体的反例可以。

四、评审之后 ​

  • 阻塞意见修改之后要再看一次。 改动可能引入了新问题,或者没有真正解决原问题。
  • 记录类的意见要有去处。 写进任务列表,否则「以后再改」就是永远不改。
  • 反复出现的问题,用工具或约定解决。 同一类问题在每次评审里都要指出,说明它应该变成一条自动化检查、一个代码模板,或者一份团队约定。
  • AI 生成的代码同样要过这几层。 生成的代码通常格式整洁、命名规范,第 4 层很少有问题,这恰恰会让评审者放松对前三层的检查。见 AI 辅助编码的工程纪律。

五、检查清单 ​

  • 先看失败路径与数据一致性,再看契约与兼容、整体成本,最后看可读性。
  • 沿一次请求的完整调用路径看 I/O 次数,不只看改动的几行。
  • 重构与行为变更分开提交;改动尽量小。
  • 格式交给工具。
  • 评论标明级别:阻塞、建议、问题、记录。
  • 有分歧时用具体的反例或测量结果讨论。
  • 阻塞意见改完再看一次;记录类意见有去处。

参考资料

文章以 CC BY-NC-SA 4.0 授权 · 代码片段以 MIT 授权