2019 年我在一家做 SaaS 的公司,团队 8 个人,用 GitLab 管代码。有天我收到一个 PR,改了 3200 行,涉及 17 个文件,标题就俩字:「优化」。我花了两个晚上逐行看,最后只提了 3 个注释:两个变量命名,一个拼写错误。合进去之后,第二周线上出了个 nil pointer,就是那个 PR 里的。这事让我开始怀疑:代码审查到底在审什么?如果审 3200 行只能发现拼写错误,那这个流程是不是在浪费生命?
后来我翻了不少资料。Google 2018 年那篇《Modern Code Review: A Case Study at Google》我看了好几遍,他们分析了 900 万条 review,结论是:90% 的 change 修改文件数少于 10 个,中位数是 2 个文件、24 行代码;review 延迟中位数不到 4 小时;35% 的 change 只有 1 个 reviewer。注意,Google 的 review 不是「找 bug」,而是看这个改动合不合理、有没有违反规范、要不要加测试。SmartBear 和 Cisco 做过一个更早的实验:审查 200-400 行代码时,缺陷发现率能达到 70%-90%;一旦超过 400 行,发现率断崖式下跌;超过 60 分钟,人就开始走神。这两个数据放一起,结论很清楚:review 的有效性跟代码量成反比,跟时间也成反比。你让一个人看 2000 行,他能看到的只有格式问题。
后来我换了团队,定了个规矩:PR 超过 400 行,要么拆,要么直接拉个 15 分钟的 pair review,不开评论。一开始有人骂,说拆 PR 浪费时间。但三个月后数据很有意思:平均 PR 大小从 780 行降到 210 行;review 平均响应时间从 26 小时降到 6 小时;线上因为代码问题回滚的次数从每月 3.2 次降到 0.8 次。最反常识的是,我们几乎不再在 review 里吵架了。因为当 PR 小到只有 50 行的时候,你没法写「这个设计有问题」——要么直接说「这里加个判空」,要么就 approve。大 PR 才是争论的温床,因为每个人都能在里面找到自己想反对的东西,而且谁都不用负责。
我现在 review 的顺序是:先看测试,再看接口,最后才看实现。测试没有或者覆盖不够,直接打回,不用往下看。接口看命名、参数、返回值,有没有把内部实现暴露出去。实现部分我只关心三件事:边界条件、错误处理、并发安全。其余的风格问题,交给 ESLint、gofmt、Black 这些工具。人只做机器做不了的事。这个顺序帮我省了至少一半时间。有个同事跟我说,他 review 一个 200 行的 PR 只用了 8 分钟,我说你肯定没看测试。他说测试在 CI 里跑过了。我说 CI 只能证明测试通过,不能证明测试写对了。他后来承认,那个 PR 的测试只测了 happy path。
很多人把 code review 当成质量门禁,我觉得它更像一个「知识广播」机制。你想想,一个模块只有一个人懂,他请假了怎么办?review 就是强迫第二个人看懂这段代码。所以我在 review 里会刻意问一些「蠢问题」:这个函数在什么情况下会被调用?上游是谁?如果这里返回 null 会怎样?这些问题不是为了挑刺,是为了让作者自己讲一遍。讲不清楚,说明代码或者设计有问题。至于 bug,能靠工具发现的就别用人。SonarQube、CodeQL、Semgrep 这些静态分析工具,能扫出 80% 的低级问题。人应该去看那 20%:架构、可维护性、边界。可能有人觉得这是废话,但我确实花了两年才做到。
最后说个坑。不要用 review 来评判人。我见过一个 leader,在周会上说「张三这个月被 review 提了 47 个问题,大家要向他学习」。张三第二个月就离职了。review 的数据一旦跟绩效挂钩,所有人都会开始写 10 行一个 PR,或者干脆不写注释。你得到的不是高质量代码,是高质量表演。所以我的 rule 是:review 数据只看团队趋势,不看个人排名。团队平均 PR 大小在降,review 响应时间在降,回滚次数在降,就够了。个人的数字,谁问都不给。