上一篇我们聊了 AI 代码 Bug 怎么排查。但与其等出了 Bug 再修,不如在合并代码之前就把它们找出来。这篇分享我用 AI 做 Code Review 的完整方法——一套提示词模板 + 真实案例对比 + 人工 Review 容易漏什么。
一、为什么让 AI 参与 Code Review?
先说一个扎心的事实:
多数团队的 Code Review 是走形式。
实际工作中 Code Review 的常见状态: 场景 A:PR 太大,Reviewer 花了 30 秒,写了 "LGTM"(Looks Good To Me) 场景 B:Reviewer 只看代码风格——变量命名、缩进、注释 场景 C:Reviewer 认真看了,但漏掉了边界条件、并发问题、安全漏洞 场景 D:Reviewer 自己也在赶需求,根本没时间 Review这些问题不是 Reviewer 不负责,而是人的注意力有限。一段 500 行的 PR,人要逐行检查边界条件、错误处理、并发安全——这本身就是反人类的。
AI 恰好擅长做这件事。它不会累、不会赶时间、不会因为和你是熟人而手下留情。
但直接丢一句「帮我看这段代码有什么问题」,AI 的回复通常是泛泛而谈的「这段代码看起来还不错,可以考虑加一些注释」。
关键不在「用不用 AI」,在于「怎么问」。
二、我实测了 5 套提示词,选出效果最好的 3 套
我拿同一个项目里 5 个真实的 PR 做测试,每套提示词针对不同的 Review 维度。
提示词 A:安全检查 —— 排查安全漏洞和敏感信息
这套提示词专门用来查安全问题——人工 Review 最容易漏的领域。
请对以下代码进行安全审查,重点检查: 1. 硬编码的密钥/Token/密码 - 代码中是否有直接写死的 API Key、数据库密码、JWT Secret? - 环境变量是否被正确引用,而不是直接暴露值? 2. 敏感信息泄露 - 日志中是否打印了用户密码、Token、手机号等敏感信息? - 错误信息返回给前端时,是否暴露了数据库结构或堆栈信息? 3. 注入风险 - SQL 查询是否使用了参数化查询而不是字符串拼接? - 用户输入是否经过了验证和转义? 4. 权限与认证 - 接口是否都有正确的权限校验? - Token 过期处理是否完善? 5. 依赖安全 - 是否引用了已弃用或有已知漏洞的库? 代码: [粘贴代码]真实案例:
用这套提示词审查一个用户管理模块的代码,AI 发现了 3 个安全问题:
发现问题 1:日志中打印了明文密码 console.log('User login:', { username, password }) → 攻击者拿到日志文件就能获取所有用户的密码 发现问题 2:数据库密码硬编码在代码里 const db = new Sequelize('mysql://root:admin123@localhost:3306/mydb') → 应该改成 process.env.DATABASE_URL 发现问题 3:接口没有频率限制 POST /api/login 没有 rate limiting → 可以暴力破解用户密码这三个问题,人工 Review 很可能只看业务逻辑,不会注意到安全细节。
提示词 B:健壮性检查 —— 排查边界条件和异常处理
这套提示词专门排查代码在「非正常情况」下的表现。
请对以下代码进行健壮性审查,假设一切可能出错的地方都会出错: 1. 空值与边界 - 数组为空时,map/filter/reduce 是否会出错? - 对象属性不存在时,是否会导致 undefined 传播? - 数字计算是否会溢出、除零、NaN? 2. 异步与并发 - Promise 是否都有 catch 或 try/catch? - 是否存在竞态条件(多个请求同时修改同一数据)? - 定时器/setInterval 在组件销毁时是否清除? 3. 接口失败处理 - 接口调用失败时,用户看到的是什么?白屏?转圈? - 失败后是否有重试机制?重试几次?间隔多久? - 部分数据加载失败时,已加载的数据是否正常显示? 4. 内存与性能 - 是否存在闭包导致的内存泄漏? - 大列表是否使用了虚拟滚动或分页? - 是否有不必要的重复渲染? 代码: [粘贴代码]真实案例:
审查一个文章列表组件:
// 原始代码constfetchArticles=async()=>{constres=awaitapi.getArticles()articles.value=res.data.map(item=>({...item,createdAt:formatDate(item.createdAt)}))}AI 发现了 4 个隐患:
隐患 1:res.data 可能为 null/undefined → res.data.map() → Uncaught TypeError: Cannot read property 'map' of null 修复:const list = res.data || []; articles.value = list.map(...) 隐患 2:item.createdAt 可能为 null → formatDate(null) 可能返回 'Invalid Date' 或报错 修复:formatDate(item.createdAt || '') 隐患 3:没有 try/catch → 接口失败时用户看到白屏,没有任何提示 修复:加 try/catch + error 提示 隐患 4:没有 loading 状态 → 页面初始状态是空列表,用户不知道数据在加载中 修复:加 loading.value = true/false修复后的代码:
constfetchArticles=async()=>{loading.value=truetry{constres=awaitapi.getArticles()constlist=res?.data??[]articles.value=list.map(item=>({...item,createdAt:formatDate(item.createdAt??'')}))}catch(error){ElMessage.error('获取文章列表失败')}finally{loading.value=false}}提示词 C:可维护性检查 —— 排查代码坏味道
这套提示词关注代码的长期健康度。
请对以下代码进行可维护性审查,从「三个月后的自己还能看懂吗」的角度: 1. 命名 - 变量、函数、组件的命名是否准确表达了它们的用途? - 是否有 data、info、temp 这类模糊命名? - 是否使用了行业通用命名惯例? 2. 职责单一 - 每个函数是否只做一件事? - 是否存在超过 50 行的函数?是否能拆分? - 组件是否承担了过多的职责? 3. 重复代码 - 是否有相同的逻辑出现在多个地方? - 是否可以抽取为共用函数或 composable? 4. 注释与文档 - 复杂的业务逻辑是否有注释解释「为什么这么做」? - 是否有注释在描述「做了什么」(代码本身就能看出来)? 5. 硬编码 - 是否有写死的魔法数字? - 业务常量是否集中管理? 代码: [粘贴代码]真实案例:
审查一段权限校验代码:
// 原始代码if(user.role===1){router.push('/admin')}elseif(user.role===2){router.push('/editor')}elseif(user.role===3){router.push('/viewer')}AI 的建议:
问题:魔法数字 + 无扩展性 三个问题: 1. role === 1 是什么意思?三个月后你还会记得吗? 2. 如果要加第 4 种角色,需要修改这里的 else-if 链 3. 业务逻辑散落在各个文件里,改一处漏一处 建议修改为: // constants/roles.ts export const ROLES = { ADMIN: 1, EDITOR: 2, VIEWER: 3, } as const export const ROLE_ROUTES = { [ROLES.ADMIN]: '/admin', [ROLES.EDITOR]: '/editor', [ROLES.VIEWER]: '/viewer', } // 使用时 import { ROLES, ROLE_ROUTES } from '@/constants/roles' const targetRoute = ROLE_ROUTES[user.role] if (targetRoute) { router.push(targetRoute) }三、AI Review vs 人工 Review:真实对比
我拿同一个 PR(约 450 行代码,包含 3 个功能模块)分别做了一次人工 Review(我自己)和 AI Review(Claude + 上面 3 套提示词),对比结果:
| 维度 | 人工 Review | AI Review |
|---|---|---|
| 耗时 | 约 25 分钟 | 约 3 分钟 |
| 发现 Bug 数 | 2 个 | 5 个 |
| 发现安全问题 | 0 个 | 2 个 |
| 发现边界条件问题 | 1 个 | 6 个 |
| 发现代码坏味道 | 3 个 | 4 个 |
| 发现可维护性问题 | 2 个 | 3 个 |
AI 发现的、人工漏掉的典型问题
问题 1:竞态条件(人工容易忽略)
// 原始代码:用户快速切换标签页watch(tab,async(newTab)=>{constdata=awaitfetchTabData(newTab)tabData.value=data})AI 指出:如果用户快速切换标签页,后发起的请求可能先返回,导致显示的数据和当前标签页不对应。
修复方案:使用请求标记或 AbortController。
问题 2:JSON.parse 没有 try/catch(人工容易忽略)
constconfig=JSON.parse(localStorage.getItem('userConfig'))AI 指出:如果 localStorage 里的数据被手动修改导致格式错误,整个页面会崩溃。
修复方案:
letconfig={}try{config=JSON.parse(localStorage.getItem('userConfig')||'{}')}catch{localStorage.removeItem('userConfig')}问题 3:正则表达式 ReDoS 风险(人工基本不会注意到)
AI 发现了某个表单验证的正则有灾难性回溯风险——输入特定字符串会导致 CPU 100%。
四、我的 Code Review 工作流
经过几个月的实践,我总结出了这个流程:
第 0 步(提交者自己):AI 预审 ├── 提交 PR 之前,先用 3 套提示词过一遍自己的代码 ├── 修掉 AI 找到的明显问题 └── 人工 Reviewer 的时间不要浪费在修低级错误上 第 1 步(AI 自动审查):PR 提交后 ├── 用提示词 A(安全)过一遍 ├── 用提示词 B(健壮性)过一遍 ├── 用提示词 C(可维护性)过一遍 └── 把 AI 的发现整理成评论贴在 PR 里 第 2 步(人工审查):AI 审完之后 ├── 确认 AI 的发现(AI 也会有误报) ├── 关注 AI 不擅长的领域:业务逻辑正确性、架构设计合理性 └── LGTM ✅关键原则:AI 做初筛,人做终审。
AI 擅长找模式匹配的问题(安全漏洞、空值检查、重复代码),但在「这个业务逻辑是否符合需求」这个问题上完全是盲的。
五、AI Code Review 的局限性(必须说清楚)
知道 AI 会在哪里「翻车」,比知道它哪里强更重要:
局限 1:不懂业务逻辑
AI 看到的:if (status === 'pending') { sendEmail() } AI 的评论:代码看起来没问题 实际业务需求:pending 状态下不应该发邮件,要等 confirmed 状态局限 2:过度保守的误报
AI:这个 SQL 没有加 LIMIT,可能导致全表查询 实际:这张表总共就 50 行,加 LIMIT 反而增加了代码复杂度局限 3:不了解团队约定
AI:建议用 async/await 替代 .then() 实际:这个项目的历史代码都用 .then(),保持风格一致更重要局限 4:不看上下文只看代码片段
AI:这个函数可以抽取为公共工具函数 实际:它依赖了当前模块的私有状态,抽取出去反而增加耦合六、不同 AI 做 Code Review 的对比
| AI | 安全检查 | 健壮性检查 | 可维护性 | 误报率 |
|---|---|---|---|---|
| Claude | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 低 |
| ChatGPT | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 中 |
| Copilot | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | 中 |
| 通义灵码 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐ | 高 |
建议:日常用 Claude 或 ChatGPT 做 AI Review。Copilot 更适合写代码时的实时提示,通义适合中文场景但误报偏多。
七、三套提示词速查卡(复制即用)
🔒 安全检查
对以下代码进行安全审查: 1. 硬编码密钥/密码 2. 敏感信息泄露(日志、错误信息) 3. 注入风险(SQL、XSS) 4. 权限校验是否完整 5. 依赖是否有已知漏洞 代码:[粘贴]🛡 健壮性检查
对以下代码进行健壮性审查: 1. 空值与边界条件 2. 异步错误处理与竞态条件 3. 接口失败的用户体验 4. 内存泄漏与性能问题 代码:[粘贴]🧹 可维护性检查
对以下代码进行可维护性审查: 1. 命名是否准确表达意图 2. 是否有超过50行的函数需要拆分 3. 是否有重复代码可以抽取 4. 是否有硬编码的魔法数字 5. 复杂逻辑是否有注释 代码:[粘贴]写在最后
Code Review 这件事,本质不是「找茬」,是「减少线上事故」。
AI 不能替代有经验的 Reviewer 对业务逻辑的判断,但它能让 Reviewer 把精力集中在真正需要人类判断力的地方,而不是瞪大眼睛找变量名拼写错误。
建议从下一个 PR 就开始试:提交前先用 AI 过一遍,看看你会发现什么。
如果这篇文章对你有帮助,欢迎分享给团队里的同事。你试过用 AI 做 Code Review 吗?欢迎留言交流你的体验。