← 全部笔记
工程实践

陌生模块的 Code Review:方法与检查清单

先理解改动要解决的问题,再补最小必要的上下文。用阅读路线、风险检查表、并发案例和 30 分钟流程,完成一次有依据的 Code Review。

Ming约 15 分钟阅读
本文目录

收到一个陌生模块的 PR,最容易陷入的困境是:打开第一个文件,遇到不懂的类就跳转,越看越远,最后仍然不知道这次改动是否可靠。

先把问题缩小:这次改动想解决什么问题?为了判断它是否正确,我最少需要知道什么? 围绕改动补上下文,再用可验证的风险组织 review。

一次 review 的目标:能说明改动前后的行为差异、受影响的调用方,以及关键风险的验证依据。不需要先成为整个模块的专家。

一、先建立变更地图

先读 PR 描述和关联的 Issue、需求单,把下面三件事写清楚。描述不够时,请作者补充问题背景、复现步骤或验收条件,这本身就是有价值的 review 意见。

问题 要得到的信息 示例:给创建订单增加重试
为什么改? 原来的问题、触发条件 下游偶发超时,用户无法确认订单是否创建成功
改了什么? 行为变化、实现边界 超时后使用同一个请求标识重试
预期效果? 可观察的验收标准 重试后能得到确定结果,同一请求最多创建一笔订单

接着扫文件列表、增删行数和新增/删除的文件。文件数量只是线索,改到了什么边界更值得关注:

变更信号 接下来追问
一个小需求跨了很多文件 是否混入重构、格式化或无关修改?能否独立审阅?
改了公共函数、基类或默认配置 哪些调用方会继承新行为?旧用法是否仍然成立?
改了接口、表结构或消息字段 新旧版本共存时会怎样?发布顺序有什么约束?
删除分支、校验或兜底逻辑 当初为何存在?对应的异常场景还会发生吗?

推荐的阅读路线

  1. 01测试作者认为什么行为才算正确?
  2. 02接口与数据契约和影响范围发生了什么变化?
  3. 03核心逻辑实现能否兑现这些行为?
  4. 04外围改动配置、日志与调用方是否同步?

测试能帮助理解作者的预期,但也可能遗漏需求或把错误实现固化下来。要同时核对需求、断言和实现;没有测试时,先写出应该观察到的行为,再去读代码。

二、只补判断所需的上下文

不要无限追调用链。先定位入口、当前函数、直接依赖和结果使用方,能回答“输入从哪里来、结果到哪里去、失败由谁处理”,再决定是否继续深入。

方法 怎么做 希望回答的问题
从入口追踪 Find Usages,追到接口、事件处理器或定时任务 谁调用它?调用时有哪些前置保证?
打开完整文件 读完整函数、相邻分支和清理逻辑 diff 外的约束是否被破坏?
回看历史 查提交、blame 和当时的 PR 看起来奇怪的逻辑,是否是在防一个旧 bug?
本地验证 跑相关测试,断点走主流程和失败路径 实际执行顺序与推断是否一致?
画一张简图 标出数据流、状态迁移或调用时序 哪一步可能重复、乱序或只完成一半?
向人请教 找作者或模块负责人,带着具体路径提问 哪条业务约束没有写在代码里?

查历史时可以从这些命令开始,路径和行号替换成实际目标:

# 先看改动形状;main 替换为该 PR 的目标分支
git diff --stat origin/main...HEAD
git diff --name-status origin/main...HEAD

# 查看文件的演变,追踪可能的重命名
git log --follow -- path/to/file

# 定位某段代码最近的修改,再阅读对应提交
git blame -L 80,130 -- path/to/file
git show <commit>

blame 给出的是最近修改者和提交,不一定是设计理由。格式化、搬迁代码都可能遮住更早的背景,需要继续回看提交说明和关联讨论。

让 AI 做导览,再核实证据

可以请 AI 总结改动意图、列出受影响的模块、提出待验证的问题。给它明确的证据要求,比直接问“有没有 bug”更有用:

请基于 PR 描述、diff 和可读取的相关代码,整理:
1. 改动前后的行为差异。
2. 受影响的入口、调用方、数据契约和测试。
3. 最值得验证的三个风险,并给出文件或函数依据。
4. 缺失的上下文;区分已观察事实与推测。

随后自己打开引用的代码、核对调用关系并运行相关验证。无法确认的业务约束,应转化为具体问题,例如:“这个入口是否保证同一订单串行处理?保证来自哪里?”

三、用四类风险检查表审阅

不熟悉业务时,也能从通用约束切入。按本次改动挑选相关项;每个结论尽量对应到代码、测试、执行结果或作者确认的约束。

正确性:主流程之外会发生什么

检查面 带着这些问题读 可验证的证据
边界条件 空值、空集合、0、负数、上限、超大数据量如何处理? 边界测试、输入约束、上游校验
异常路径 超时、部分成功、异常后如何恢复?是否吞掉异常? 故障注入、错误断言、回滚或补偿路径
并发与幂等 重试、重复提交、并发修改是否重复产生副作用? 交错执行测试、唯一约束、原子操作

影响范围:谁会感受到新行为

检查面 带着这些问题读 可验证的证据
公共代码 其他调用方是否依赖旧行为、旧默认值? 调用方列表、相关回归测试
契约兼容 旧客户端、旧消费者能否读取新格式? 契约测试、新旧版本兼容示例
发布与回退 是否需要迁移、灰度或开关?回退后能否读新数据? 迁移方案、发布顺序、回退演练

数据与安全:不该发生的事能否被阻止

检查面 带着这些问题读 可验证的证据
权限与输入 身份、资源归属、参数边界由哪一层校验? 越权用例、校验代码、接口测试
敏感信息 日志、错误响应是否泄露令牌或个人信息? 实际日志样本、脱敏与错误映射
SQL 与性能 是否引入 N+1、无界查询或不必要的全表扫描? 查询次数、执行计划、代表性数据量
事务边界 哪些操作必须一起成功?远程调用失败时如何收敛? 事务范围、故障恢复测试、补偿机制

可维护性:下次改动能否理解并验证

检查面 带着这些问题读 可验证的证据
复用与一致性 是否已有工具或约定?是否混入无关重构? 现有实现、团队规范、可拆分的提交
意图表达 命名是否解释用途?硬编码是否隐藏业务规则? 命名、常量、说明约束的注释
测试质量 是否覆盖新分支、失败路径和边界?断言验证的是行为吗? 测试输入、断言、失败时能否定位问题

测试全绿说明已有断言通过了。审阅时还要问:需求中的哪条约束没有被断言?哪个调用方没有被覆盖?

四、用一个重试案例练习追问

假设 PR 给创建订单增加重试,并用 requestId 防止重复。下面是简化后的伪代码:

createOrder(requestId, payload):
    existing = findByRequestId(requestId)
    if existing exists:
        return existing

    result = chargeAndCreateOrder(payload)
    saveRequestResult(requestId, result)
    return result

顺序执行时,第二次请求可以读到已有结果。但“先检查、再执行、最后记录”之间存在空隙;两个请求可能同时通过检查。

请求 A 和重试 B 使用同一 requestId,先后查询时都未找到结果,随后分别扣款并创建订单,最后才写入幂等记录,因此副作用已经重复发生。
图 1 · 两次查询都成功,并不意味着整个操作具备幂等性。

此时不必先读完整个订单模块,先验证三个关键问题:

  1. 并发如何被协调? 是否在副作用发生前原子地登记请求、区分处理中与已完成,并处理并发冲突?仅在末尾补唯一索引,仍可能挡不住重复扣款。
  2. 超时意味着什么? 下游可能已经成功,只是响应丢失。重试是否向下游传递稳定的幂等标识?如何查询未知结果并恢复?
  3. 请求标识约束是什么? 是否限定用户或租户?相同标识但不同参数会被拒绝,还是误返回另一笔请求的结果?

原子登记、状态机、数据库事务、下游幂等和补偿分别解决不同问题。具体方案取决于事务边界与下游能力,不能用“加个锁”概括所有路径。

要覆盖的场景 应观察的结果
相同标识顺序重试 返回同一业务结果,不重复创建
相同标识同时到达 最多产生一次副作用,另一个请求得到明确状态
下游已成功但响应超时 能查询或恢复为确定结果,不盲目重复执行
扣款后、保存结果前进程退出 恢复流程能发现并处理未完成状态
相同标识携带不同参数 明确拒绝冲突,不静默复用旧结果

验证并发问题时,要控制两个请求的交错顺序,让它们都经过查询后再继续;只循环调用接口、碰运气复现,难以证明风险已经消除。

五、把观察写成可处理的评论

区分疑问、建议与阻塞

评论写清楚位置 → 触发条件 → 后果 → 验证或修改建议。不理解业务约束时,明确提问;发现确定风险时,说明依据。

级别 适用情况 评论示例
blocker 必须解决 已确认的正确性、安全或兼容问题 两个相同标识的请求可同时通过查询,并分别扣款。请在副作用前处理并发,并增加交错执行测试。
suggestion 建议 有明确收益,但不阻塞合入 这里与现有重试工具职责接近,建议复用,避免两套退避策略分别维护。
question 疑问 缺少背景,暂时不能判断 这个入口是否保证请求串行?如果没有,上面的查询与写入之间可能存在竞态。
nit 小细节 命名、格式等轻微问题 变量名可改为 retryCount,更容易区分次数与时间。

级别应遵守团队约定。question 暴露了关键未知条件时,仍需要在合入前得到答案;标成问题,不代表风险可以忽略。格式和排版尽量交给自动化工具处理。

特别关注没改的地方

  • 新增枚举值:所有 switch、映射、序列化与展示文案是否跟上?
  • 修改字段:旧读取方、缓存键、消息消费者是否仍按旧含义使用?
  • 改变状态迁移:超时任务、补偿流程、指标和告警是否认识新状态?
  • 删除校验:原先依赖这层防线的其他入口,是否有等价保护?

PR 太大时先缩小审阅单元

按业务行为拆分,尽量让重构、格式化和功能变化独立呈现。数百行改动可以作为提醒,但行数没有统一的质量分界线;生成文件、复杂度和跨模块耦合都会影响审阅成本。

判断标准是:能否清楚描述每部分的目的,独立验证行为,并评估回退影响。无法做到时,请作者补变更地图、拆 PR 或分轮审阅。

六、一次 30 分钟的首轮 Review

下面按 25 分钟检查、5 分钟整理意见安排首轮审阅。小改动可以压缩到 15 分钟左右;复杂 PR 需要继续深入,时间用完不是批准合入的条件。

  1. 00–03 分
    读描述与需求产出:一句话说明问题、行为变化与验收标准。
  2. 03–05 分
    扫文件与边界产出:受影响的入口、公共代码和数据契约。
  3. 05–10 分
    先读测试产出:已有行为断言,以及明显缺失的场景。
  4. 10–20 分
    读核心改动,补上下文产出:主流程、失败路径和关键调用关系。
  5. 20–25 分
    按风险验证产出:边界、异常、并发与兼容性的验证依据。
  6. 25–30 分
    分级整理意见产出:明确的阻塞项、建议、待确认问题和下一步。

时间到后,如果关键业务约束仍不明确、相关测试无法运行,或发布与回退风险没有得到解释,应把缺口和需要谁补充写清楚,再安排下一轮。

可直接复制的审阅记录

## 改动理解
- 问题与触发条件:
- 改动前 → 改动后:
- 影响的入口、调用方与数据契约:

## 验证依据
- [ ] 需求与测试断言一致
- [ ] 已检查相关边界、异常、并发/重试路径
- [ ] 已核对调用方与新旧版本兼容
- [ ] 已检查相关权限、数据与性能风险
- [ ] 需要时已确认迁移、灰度与回退方案
- 实际运行的测试/命令与结果:
- 未执行、不适用或仍缺失的验证及原因:

## 审阅意见
- blocker:
- suggestion:
- question(确认人/所需证据):
- nit:

七、让每次 Review 留下一点模块知识

每次审阅后记下三个东西:一个关键概念、一条核心流程、一个容易踩坑的约束。下一次碰到同一模块,就有了可以复用的起点。

积累来源 值得留下的内容
本次陌生模块 入口、状态、负责人、隐藏前提和关键测试位置
线上故障复盘 触发条件、为何漏检、什么测试或监控能提前发现
同事的 review 对方追问的约束、如何构造反例、如何说明影响
自己的审阅复盘 哪个判断缺证据、漏了哪条路径、下次先查什么

检查清单也要维护:把真实漏检的问题补进去,把长期无关的项目移走。让它逐渐贴近团队的系统和故障模式,而不是越写越长。

下次收到陌生模块的 PR,先试着填完这句话:“这次把____行为改成了____,我需要验证____,依据是____。” 填不出的部分,就是下一步最值得阅读、运行或提问的地方。

把知识带回代码里,用一次实验验证理解。

← 返回全部笔记