AI 做 Code Review 靠谱吗?它能抓的 5 类问题和抓不到的 3 类

0 阅读9分钟

@TOC


让 AI 审查代码,最容易得到的是一堆正确的废话:

「建议添加错误处理」 「变量命名可以更清晰」 「建议补充单元测试」

这些话对任何一段代码都成立,等于没说。

用了一段时间之后我发现,问题不在于 AI 不会 review,而在于大多数人给它的信息不足以做出有价值的判断。它只看到了你改动的那几十行,看不到这些行在项目里意味着什么。

这篇说清楚三件事:它实际能抓到哪几类问题、哪几类根本抓不到、以及怎么问才能拿到有用的结果。


一、它确实能抓到的 5 类

第一类:改动的影响面遗漏

这是我认为最有价值的一类,前提是工具能看到调用关系。

典型场景:你改了一个函数的返回值语义,本文件内所有调用点都更新了,但另一个包里通过接口间接调用的实现没动。编译能过(接口签名没变),测试也过(那条路径没覆盖)。

这类问题人工 review 很难发现,因为它不在 diff 里。reviewer 看到的是你改的那几个文件,那个漏掉的实现根本不在视野内。

能不能抓到这类问题,取决于工具是否掌握调用关系。如果它只能看到 diff 文本,那和人工 review 的视野是一样的,自然也发现不了。

我现在提交前会做一次自检:

@git:changes 这次改动有没有遗漏的调用方?
特别检查通过接口间接调用的地方。

wescode 能看到 diff,也能看到这些改动涉及的调用关系,所以这个问题它答得比较实。抓到过两次真问题:一次是改了函数签名漏了接口实现,一次是改了返回值语义但没更新依赖这个顺序的下游。

第二类:和项目既有写法不一致

错误处理方式、命名规范、分层约束这些。比如全仓都用 errors.Wrap,你这次用了 fmt.Errorf;handler 都是 Handle 开头,你写了个 processXxx。

这类问题的特点是:单看这段代码完全正确,放在项目里才不对。所以 AI 必须知道项目的既有写法,光看 diff 判断不了。

我在 wescode 里做这步不用额外交代规范——它会对照项目里已有的写法,而不是通用的「最佳实践」。这个区别挺重要:通用最佳实践有时候和项目现状是冲突的,按前者改反而制造了新的不一致。

第三类:机械性的疏漏

这类它抓得又快又准:

  • 新增的错误分支没有对应的测试
  • 改了函数签名但注释还是旧的
  • 加了配置项但文档没更新
  • 日志里打了不该打的字段
  • 资源申请了没有对应的释放

这些都是「有模式可循」的,不需要理解业务就能发现。人工 review 也能抓,但容易累了就漏,AI 不会累。

第四类:边界条件没处理

空值、空集合、零、负数、越界、并发写。AI 对这类情况相当敏感,因为训练数据里这类 bug 太多了。

我会专门问一句:

这次改动新引入的分支,有哪些输入会走到没处理的路径?
只列你确信没处理的,不要罗列所有可能性。

最后那句很重要。不加的话它会把所有理论可能性都列一遍,一半是噪音。

第五类:安全上的低级错误

SQL 拼接、硬编码密钥、日志打印敏感信息、路径拼接没校验。这几类它识别率很高。

不过要注意,这只覆盖了「模式明显」的部分。复杂的权限逻辑漏洞它看不出来,下面会说。


二、它基本抓不到的 3 类

这部分比上面更重要。知道边界在哪,才不会把 AI review 当成免检通行证。

第一类:业务逻辑对不对

这是最根本的限制。

比如折扣计算,代码写的是先打折再加运费。AI 看不出这有什么问题——从代码角度它完全合理。但如果业务规则是运费不参与折扣、且应该先加运费再整体打折,那这段就是错的。

AI 只能从代码推断意图,而代码可能正好把意图写错了。 这类问题只有懂业务的人能发现,或者靠明确的需求文档和测试用例。

我的做法是:涉及核心业务规则的改动,AI review 只当辅助,人工 review 不能省。

第二类:这个改动该不该做

AI 会告诉你这段代码写得怎么样,不会告诉你这段代码不该存在。

比如为了一个边缘场景加了一层抽象,代码质量没问题,但引入的复杂度不值得。或者这个功能其实和已有模块重复了,应该复用而不是新写。这类判断需要对项目演进方向的理解,AI 给不出来。

第三类:历史原因造成的约束

那段看起来多余的判断,可能是三年前踩过坑加的补丁。AI 看到的是「这个 if 永远为真,建议删除」,但删了线上就炸。

这类信息在 git blame 和当年的 issue 里,不在代码结构里。所以 AI 建议删代码的时候,我一般会先看一眼 blame。

wescode 能告诉我这段代码被谁调用、改了会影响什么,但回答不了「当年为什么要加这个判断」。结构问它,历史问 git,这个分工得拎清楚。


三、怎么问才有用

给 diff,不要给整个文件

review 的对象是改动,不是全部代码。给整个文件,它会把无关的既有代码也评论一遍,噪音很大。

@git:changes 帮我 review 这次改动

这样它聚焦在变化上。改动分散在多个文件时,这个方式比一个个贴文件方便得多。

明确要求「不确定就说不确定」

这条能大幅减少废话:

review 这次改动,要求:
- 只报你确信有问题的地方,不确定的标注「需人工确认」
- 不要提「建议加注释」「建议补测试」这类通用建议
- 每个问题说明:在哪、为什么是问题、怎么改
- 如果没发现问题,就直接说没发现

最后一句很关键。不加的话它总要挤出几条建议来,哪怕代码没问题——模型有「必须给出有用回答」的倾向,你得明确告诉它「没问题」也是合格答案。

分轮次,别一次问全部

一次让它同时看逻辑、性能、安全、风格,每样都浅尝辄止。分开问效果好得多:

第一轮:这次改动有没有遗漏的调用方或影响面
第二轮:新引入的分支有哪些边界情况没处理
第三轮:和项目既有写法有没有不一致的地方

三轮下来比一轮问全面得多,成本也没高多少。


四、放进流程的两个位置

位置一:提交前自检

这是我用得最多的。写完 git add 之前,在 wescode 的 Chat 里让它扫一遍改动。

好处是这时候还没推上去,发现问题改起来没有心理负担。抓到的多是机械性疏漏——漏更新的注释、忘了的测试、不一致的写法。

位置二:review 别人的 PR 之前

拿到一个大 PR,先让 AI 过一遍,把机械性问题列出来。然后我自己的注意力就可以集中在业务逻辑和设计上——那正好是 AI 抓不到的部分。

选中代码、发起审查、查看建议、应用修改

这个分工我觉得是对的:AI 负责「有模式可循」的部分,人负责「需要判断」的部分。

反过来用就危险了:让 AI 判断业务逻辑对不对,自己只看格式,那是把两边的长处都浪费了。


五、几个实际的坑

它倾向于挑出问题,哪怕没有

前面提过,模型有给出「有用回答」的倾向。如果代码确实没问题,它可能会编几条无关痛痒的建议。

所以看到「建议优化变量命名」这类时,直接忽略就行,别真去改。判断标准是:这条建议有没有说清楚「为什么现在这样是问题」。说不清楚的基本都是凑数的。

大改动会漏

一次改了二十个文件,让它一次 review 完,后面的会明显变敷衍。改动大的话拆开分批看。

它不知道你的测试覆盖情况

它说「这个分支需要测试」,但可能已经有测试了,只是在另一个文件里。这类建议要自己核实一下再动手。


六、什么情况下不用它

改动特别小的时候。 改一行配置,自己看一眼比走一轮流程快。

纯业务规则的改动。 前面说了,这是它的盲区,找产品或者业务方确认更靠谱。

已经有完善 lint 和 CI 的项目。 机械性问题 lint 已经拦住了,AI 再扫一遍收益不大。这种情况下它的价值主要在影响面分析上。


小结

AI 做 code review 的实际价值,我觉得可以概括成一句:它替你完成「需要耐心但不需要判断」的那部分。

漏掉的调用方、不一致的写法、没处理的边界、忘了更新的注释——这些人工也能发现,但需要逐行盯着看,累了就会漏。AI 不累,这部分交给它很划算。

但涉及判断的部分——业务对不对、这个设计该不该做、这行代码为什么当年要这么写——它给不出可靠答案。这些仍然是人的工作,而且应该是 review 时真正投入注意力的地方。

把 AI review 当成一道前置过滤,而不是终审,这个定位比较合适。

文中用到的 diff 引用和调用关系检查都是在 wescode 里做的,官网是 weisyn.com。你们把 AI 放进 review 流程的哪个环节,效果如何,欢迎评论区交流。