用 AI 做代码审查:提示词怎么写,它能查出什么、查不出什么
让 AI 看代码这事,效果差别极大。甩一句"帮我看看这段代码"过去,它通常会夸你一通再提两条不痛不痒的建议。换成具体的提示词,能查出来的东西多得多。
一、提示词怎么写
把四个要素给全:代码 + 上下文 + 关注点 + 输出格式。
一个我常用的模板:
下面是 [语言/框架] 的代码,用途是 [一句话说明干什么],
运行环境 [版本/依赖],会被 [调用方/并发量/输入来源] 调用。
请从以下几个角度审查:
1. 空指针和未判空的访问
2. 边界条件(空数组、0、负数、超长输入、null)
3. 异常被吞掉或者资源没关闭
4. 命名和可读性
5. 重复代码
对每个问题,按下面格式输出,不要修改代码之外的东西:
- 位置:函数名 + 行号
- 问题:一句话
- 影响:什么情况下会触发
- 建议:怎么改
按严重程度排序,只报告你有把握的问题,不确定的单独列在"存疑"里。
几个让效果变好的细节:
- 限定关注点。一次只让它查 3 到 5 类问题,查得比你列二十条要深。
- 要求给位置和触发条件。不带位置的建议没法验证,等于没说。
- 明确要求它区分"确定"和"存疑"。这条很关键,能过滤掉大量幻觉。
- 给配套信息。把相关的接口定义、数据结构、测试用例一并贴进去,它才有判断依据。
- 小批量提交。一次喂 200 到 500 行效果最好,整个文件几千行丢过去,它会漏。
二、它能稳定查出的问题
这些是我实测命中率比较高的:
- 空指针和未判空。链式调用、可选字段、map 取值,基本都能点出来。
- 边界条件。空数组、只有一个元素、0 和负数、超长字符串、临界值。它列得比人全。
- 异常被吞掉。
catch里什么都不做、只打日志不处理、吞掉原始堆栈。 - 资源没关闭。文件句柄、数据库连接、流没关,或者没用 try-with-resources。
- 命名和可读性。变量名单字母、函数名和干的事不符、布尔命名不带 is/has。
- 重复代码。几处几乎一样的逻辑,它会直接指出来并建议抽函数。
- 硬编码。魔法数字、写死的路径、明文密钥和 token。这一条值得单独跑一次。
- 明显的 N+1 查询。循环里查数据库,一眼就能看出来。
- 线程不安全的容器。在并发场景用了普通 HashMap、ArrayList。
- 拼写错误导致的 bug。变量名打错、赋值写成了比较,人眼很容易漏,它反而能抓住。
三、它查不出来的东西
这部分更值得记,因为它会给你虚假的安全感。
- 业务逻辑错误。扣款算错了、状态流转缺了一步,它不知道你的业务应该是什么样。
- 架构和分层问题。职责划分、模块边界、依赖方向,需要看整个项目才判断得了。
- 需求理解偏差。代码写得完美,但做的不是产品要的东西。
- 并发时序问题。真正的竞态、死锁、可见性问题,需要运行时分析。
- 性能问题的真实归因。它能看出"这里可能慢",但定位不到真正的瓶颈。
- 数据一致性。跨表、跨服务的一致性问题,看不到全局就判断不了。
- 安全漏洞的业务语境。越权访问这类问题,它不知道"什么数据该对谁可见"。
- 历史包袱相关的坑。某段看起来多余的代码是为了绕开一个三年前的问题,它会建议你删掉。
四、怎么嵌进日常流程
我现在的用法:
- 提交前自查:写完一个函数,先让它审一遍,重点看边界和空指针。这一步能挡掉大部分低级问题。
- PR 前第二遍:把 diff 喂给它,让它从 reviewer 视角提问题,同时自己再读一遍。
- 接手陌生代码时:让它解释一段代码的意图和副作用,比自己硬啃快。
- 专项扫描:定期只查一类问题,比如"把所有硬编码和密钥找出来",命中率比混合审查高。
五、最后一条提醒
AI 审过的代码,人还是要看一遍。它给的建议,每一条你都得能判断对不对,判断不了就先别改。
把 AI 当成第一道筛子,不是最后一道。它能帮你省掉读代码的体力活,省不掉"这段代码对不对"这个判断,那部分是你的活。