Code Review 只找真问题

Code Review 做了很多年。AI 参与编码之后,评审对象从纯人写的代码变成人和 AI 混写的代码,量也明显上来了,但怎么审的老原则反而一条条更重要了。这篇写我自己的评审方式,重点有两个:不为凑数量制造意见,核验 AI 意见之后再采信。

先刷新基线

审查从对齐基线开始。先 fetch 远端的目标分支,再审三点 diff(目标分支以 main 为例):

git fetch origin
git diff origin/main...HEAD

三点 diff 给出的是当前分支相对共同祖先的改动。配合刚刷新的远端引用,看到的才是这次评审真正要审的内容;拿陈旧的本地引用直接比对,diff 里会混进目标分支上早已合并的改动,也可能漏掉它最近的变化。基线错了,后面的工作全部白做。所以我把这一步固定成评审的机械动作:不管对仓库多熟,先刷新、再取三点 diff,然后才开始读代码。

这一步在 AI 参与评审时尤其重要。人对着错误的 diff 还可能凭记忆起疑,「这段好像上周就合并了」;Agent 没有这层记忆,会对着错误的范围一路审完。

界定范围

基线之后是范围。我会同时看三样东西:已提交的 diff、工作区里相关的未提交改动、仓库自身的规则和约定。规则和约定指贡献规范、编码约定这类仓库内的既有共识——评审意见与仓库自己的规则冲突时,作者会无所适从,所以先读规则再下判断。三样都看过,明确哪些改动属于本次任务的范围,再开始逐行看。

范围界定同时防两个方向的错误。一个方向是漏:配套的未提交改动里可能藏着问题,只看已提交部分会错过,而它们迟早会跟着下一次提交进仓库。另一个方向是越界:超出本次任务的历史问题可以提,但要标注为范围外,不和本次改动的问题混在一起算账。

只找真问题

我的优先级是固定的:真实的正确性、安全、并发、数据、兼容问题排在前面,这些会造成实际损失;风格和品味类的意见排在最后,多数时候干脆不提。判断一条意见值不值得提,我用的标准是:如果作者不改,会发生什么坏事?答不上来的意见先放下。

不为凑数量制造意见,是我给自己、也给 AI 评审定的规则。一条无关痛痒的风格意见,成本远不止写它的那几秒:作者要阅读、要判断、要回应、要决定改还是不改,几轮往返的时间足够修一个真问题。意见数量从来不代表评审质量,零条意见配一句「未发现问题」是完全合法的产出。

清理 AI slop

AI 参与编码之后,评审要多看一类问题,我把它们统称为 AI slop。slop 这个词近两年常用来指 AI 批量生成的低质量内容,放到代码里,指的是那些单看无害、累积起来让代码库不可信的东西。常见的有四种:

  • 与上下文不一致的解释性注释。注释说的和代码做的对不上,或者在解释一个一眼就能看懂的赋值。
  • 异常防御式的 try-catch。为不可能发生的场景兜底,顺手把真正的错误也吞进空的 catch 里。
  • 只为绕过类型系统的 any 强转。类型对不上,不去修类型定义,用 any 把编译错误压掉。
  • 明显偏离邻近代码的风格。周围是统一的写法,新代码突然换一种范式。

这类问题的麻烦在于单条都「无害」,在评审里很难理直气壮地要求修改。但它们累积起来消耗的是信任:注释不敢信、错误处理不敢信、类型标注不敢信,最后每次读代码都要重新验证一遍。我的做法是把它们明确列为评审目标,发现即提,不因为「小」而放过。提的时候点明类别就够,作者对照类别自查,比逐行争论快得多。

核验 AI 的意见,再采信

AI 评审的产出我从不直接转发,因为它可能把事实搞错。一个真实案例:AI 评审分析一段 Android 权限代码时,把 API 33 引入的权限组行为报成了 API 34 引入。这条意见推理完整、表述自信,如果直接采信,修复方案里的版本条件判断就会错一个版本。

从那以后我固定一条规则:凡是涉及 API level、平台约束、配置语义的意见,先查官方文档,再定结论。核验本身不费事:把意见里的关键断言当关键词去查文档,几分钟就有答案。这类事实性断言正是模型容易出错的地方,也因为它表述得具体、肯定,人最容易放松核验。查证不了、又拿不准的意见,就标注为存疑交给作者判断,好过给出一个错误的肯定结论。AI 评审的价值在覆盖面广、成本低,事实裁决要以官方文档为准。

影响面分析怎么交付

在多端共用的仓库里改公共配置,评审时常要回答「其他端受不受影响」。我要求影响面分析按端逐个写清三件事:是否受影响、理由、当前默认值,最后合成一张表。逐端展开是为了堵住「应该都没问题」这类含糊结论,每个端都必须给出明确判断和依据;记录当前默认值,是因为「不受影响」的结论常常建立在对默认值的假设上,把假设写出来才能被检查;表格让作者一眼看到全貌。

分析结论还要写成代码内的 TODO 注释,放到对应位置。留在评论区的意见容易被后续讨论淹没,写进代码就成了作者能按 diff 逐个处理、逐个消除的事项。

收尾

评审的最后一步是运行与风险相称的检查:小改动跑相关测试和静态检查,高风险改动值得完整构建。报告把三类内容分开写:发现的问题、已经修复的、未覆盖的部分,让读的人清楚哪些结论有验证支撑。未覆盖的部分尤其要写出来,它提醒作者和后续评审者哪里还欠一次验证。最后的总结控制在一到三句,评审的价值在具体意见里,收尾写长了没有人看。

评审输出是给作者消费的。AI 让产出意见变得便宜,一次评审生成几十条意见毫不费力,稀缺的资源变成了作者的注意力。只提真问题,是对这份注意力最基本的尊重。