← 返回教程库

AI 代码质量与安全 review——怎么审 AI 写的代码

最后更新 2026-06-25
你将学到
  • 认清 AI 写的代码"能跑"和"能上线"之间有多大差距,建立主动 review 的意识
  • 掌握审 AI 代码的四个维度:功能正确性、安全风险、性能陷阱、可维护性
  • 学会高效 review 的组合策略:AI 自审 + lint 扫描 + 人盯安全 + 交叉审查
  • 拿到一张可落地的 AI 代码 review 清单,并知道怎么用 hook/CI 把它变成强制流程

AI 写的代码能跑,不等于能上线。不审就用,等于埋雷。

这句话不是危言耸听。你让 AI 帮你写了一个「查用户订单」的接口,它给你的代码功能是对的,测试也过了。但你仔细看一眼:它把 user_id 直接拼进了 SQL 字符串、API 密钥硬编码在文件里、接口里没校验这条订单是不是当前用户的。每一项单独拿出来,都是真实事故的经典前情。

这就是 AI 代码和「你自己写的代码」之间最关键的区别:AI 优化的目标是「让功能跑通」,不是「让代码安全、可维护、没性能坑」。它会把注意力放在你说的需求上,把你没提的那些默默忽略。不审,就是默认接受了这些被忽略的部分。


为什么 AI 代码必须审——它会埋什么雷

先把几类最典型的雷具体化,让你有直觉判断力:

功能理解偏了

你说「查最近的订单」,AI 可能理解成「最近 10 条」,而你的业务要求是「最近 30 天内的」。差一个字,逻辑完全不同。它不问你,直接写——功能测试能过,因为测试数据没有刚好踩这条边界。

这类问题最隐蔽:代码是「正确实现了它理解的需求」,但不是你真正要的需求。

安全:AI 不主动加防线

几个典型场景:

  • 硬编码密钥:你让它「接上这个 API」,它把 key 直接写进文件,能跑,但密钥跟着代码进了 git。
  • SQL 注入:你让它「查这个用户的数据」,它可能把变量直接拼进 SQL 字符串——"SELECT * FROM orders WHERE user_id = " + userId。功能没问题,但这是教科书级别的注入口子。
  • 越权:查订单的接口,它只校验了「你有没有登录」,没有校验「这条订单是不是你的」。换个 order_id,你能看到别人的单。
  • 输入没校验:用户传来的参数直接用,没做类型校验、长度限制、格式验证。

AI 不是不懂安全,是它默认「你已经在别处处理了这些」。你不明确要求,它就不写。

性能:局部正确,整体跑不动

N+1 查询是最常见的:你让 AI 写「展示所有用户及其最新订单」,它可能写一个循环,每个用户单独查一次订单——100 个用户就是 101 条 SQL。在测试数据库里无感,到了生产数据量就直接拖垮。

还有无谓的全量加载:「查一下这个表」,它直接 SELECT *,把整张表的字段全取回来,其中你只用了 3 个。

这些在本地小数据量下完全看不出来,上线后才会被数据量打脸。

可维护性:技术债积累

AI 容易产生:

  • 命名混乱:一会儿 userData,一会儿 userInfo,一会儿 user,指的是同一个东西
  • 魔法数字if (status === 3) 里那个 3 是什么意思?只有它知道
  • 过度设计:你让它「写个简单的配置读取」,它给你一个工厂模式加策略模式的五层抽象
  • 重复代码:同一段逻辑在三个地方各写了一遍,因为它每次都是「就这个需求」独立写,不记得之前写过什么

审 AI 代码的四个维度

一、功能对不对

不要光测「正常路径」,要想「边界在哪」「异常时怎样」:

  • 空值传进来会怎样?
  • 并发两个请求会不会打架?
  • 超大数据量时行为一致吗?
  • 你说的「最近」,它理解的和你要的一样吗?

这一步的关键不是测代码,是回到原始需求,把你说的每个隐含假设都拿出来对照一遍。AI 很容易「正确实现了一个错的理解」。

二、安全(最不能省的一环)

盯死这几个位置:

密钥和敏感信息:全局搜一遍 keysecrettokenpassword——看有没有硬编码在代码里的,看有没有可能出现在前端 bundle 里的。

数据库查询:找所有 SQL 相关的地方,看有没有字符串拼接。凡是 "... WHERE id = " + someVar 这种写法,就是注入风险,改成参数化查询。

接口权限:每个需要登录的接口,除了验「有没有登录」,还要验「这条资源是不是他的」。去翻每个涉及「查/改/删具体数据」的接口,问自己:如果我把请求里的 ID 换一个,我能看到或操作别人的数据吗?

输入校验:用户输入、请求参数——类型、长度、格式,后端有没有验?前端验了不算,因为前端随时可以被绕过。

三、性能

重点看这几个模式:

  • 循环里有查询吗?一个 forforEach 里有数据库操作,大概率是 N+1。改成一次批量查询。
  • 全表扫描吗SELECT * 很方便,但生产环境代价大。只取你要的字段。
  • 有没有该加 index 但没加的地方?根据你的查询条件,对应的列有没有索引?
  • 重复计算?一个值在循环里算了很多次,其实只需要算一次放在外面。

性能问题不用每行都查,重点看跟数据量成正比的地方——循环、查询、数据聚合。

四、可维护性

这个维度不紧急但重要,因为烂的可维护性会让你每次改一个小东西都要花大量时间搞清楚代码在干什么:

  • 命名能不能一眼看懂?不能的话,让 AI 重命名。
  • 有没有同一段逻辑写了好几遍?提取成函数。
  • 复杂逻辑有没有注释?
  • 有没有「为了以后扩展」但现在根本用不上的抽象?能删的删,不要提前设计。

怎么高效审

四个层次配合用,不是每次都要全部跑一遍——根据代码风险程度调整力度:

第一层:让 AI 先自审

写完代码别急着用,把代码贴回去,明确让它审安全

这是你刚才写的用户订单查询接口,请逐条检查:
1. 有没有 SQL 注入风险?
2. 有没有越权风险(用户能看别人的订单)?
3. 有没有硬编码密钥或敏感信息?
4. 输入校验完整吗?

AI 对自己写的代码做 review,往往能发现一批明显问题。这一步几乎零成本,先跑一遍再说。

但有个坑:它会「找到问题然后说改了」——你要确认它真的改了,而不是只在回复里说「这里可能有问题」然后给你一份改了一半的代码。每次让它修改后,要确认改动是否真的落地。

第二层:lint 和静态分析工具

工具能机械地扫出很多人工容易漏掉的东西,而且快:

  • ESLint / Pylint 等 linter:命名规范、未使用变量、潜在空指针
  • 安全专项扫描:Semgrep 可以扫常见安全漏洞模式,有免费规则集;npm audit / pip-audit 扫依赖漏洞
  • 类型检查:TypeScript 的 tsc --noEmit、Python 的 mypy——很多运行时才会爆的错误提前在编译阶段拦住

这些工具配在 CI 流程 里自动跑,每次推代码都过一遍,不需要人工触发。

第三层:人重点盯安全和业务逻辑

工具扫完,人要看的是工具看不了的东西

  • 业务逻辑:这段代码实现的,和你真正要的是同一件事吗?
  • 权限边界:每个接口的权限假设你是第一次看到,重新想一遍「有没有绕过的方法」
  • 密钥处理:全局搜一遍敏感词,不信工具,亲自确认
  • 错误处理:出错时代码会泄露什么信息?错误栈有没有可能直接展示给用户?

这一层不需要把每行都看,集中精力在「会有安全后果的地方」——认证、授权、数据库、外部 API 调用、用户输入处理。

第四层:用另一个 AI 交叉审

如果条件允许,把代码拿到另一个 AI 工具里让它审——它没有参与写代码,没有「理所当然」的先入为主,往往能发现第一个 AI 自审时遗漏的东西。

这不是每次都要做的,而是在改动比较大、风险比较高的时候用:新的认证流程、支付相关逻辑、涉及用户隐私的功能。


AI 代码 review 清单

每次 AI 帮你写完一段代码,对着这张清单过一遍。分级打勾——核心项每次都要,扩展项按风险程度来:

【核心·每次必过】

  • 功能理解:AI 实现的,和我真正要的需求一致?边界条件想过了吗?
  • 密钥安全:有没有硬编码密钥、token、密码?有没有可能进前端 bundle?
  • SQL/注入:数据库查询有没有字符串拼接?全部改成参数化查询了吗?
  • 越权检查:涉及具体资源的接口,有没有校验「资源归属」而不只是「是否登录」?
  • 输入校验:用户输入或外部参数,后端有没有做类型/长度/格式校验?
  • 错误处理:报错信息会不会把敏感信息(堆栈、内部路径)暴露给用户?

【安全扩展·涉及认证/支付/用户数据时必过】

  • 依赖安全:新引入的包有没有已知漏洞?(npm audit / pip-audit
  • 敏感数据处理:日志里有没有打出密码、token、用户隐私字段?
  • HTTPS:敏感接口走的是 HTTPS 而不是明文 HTTP?
  • 限流防刷:关键接口(发短信、调付费 API)有没有限流?

【性能·上线前或数据量大时过】

  • N+1 查询:循环里有没有数据库操作?能不能改成批量查询?
  • 字段精简:有没有 SELECT * 但其实只用了几个字段的查询?
  • 索引:查询条件用到的列,数据库里有没有对应索引?

【可维护性·重构或长期维护时过】

  • 命名清晰:变量/函数名一眼能看出意思?
  • 无重复:同一段逻辑有没有在多处复制?
  • 无魔法数字:if (status === 3) 里那个 3,有没有给它一个有意义的常量名?
  • 无过度设计:有没有「为了以后扩展」但现在没人用的抽象?

用 hook 和 CI 把 review 变成强制流程

依赖人「记得」去 review 是靠不住的。累的时候、赶进度的时候,这步最容易被跳过。解决办法是把 review 嵌进流程,让跳过变得困难

Claude Code Hooks

Claude Code 的 Hook 机制 允许你在 AI 写完代码后、提交前自动触发脚本。可以用来:

  • 写完代码自动跑 lint
  • 提交前自动扫安全(Semgrep)
  • 检测到新的 .env 相关改动时发出提醒

一个简单的配置思路:在 PostToolUse hook 里,每次文件改动后自动跑 eslint 和安全扫描,发现问题就在终端里提示,而不是等你手动记得去跑。

具体配置方法见 hooks 完整指南

CI 流水线

本地 hook 是第一道防线,CI 是第二道——即使本地没跑,推到远端时自动触发:

# 一个简单的 GitHub Actions 安全检查示意
- name: Lint
  run: npm run lint

- name: Security audit
  run: npm audit --audit-level=high

- name: Type check
  run: npx tsc --noEmit

这几步加进 CI,任何推代码都会自动跑。没过就不让合并。

关键心态:这些自动化不是为了代替人的判断,是为了把低价值的机械检查外包给工具,让人的精力集中在真正需要判断的地方——安全边界、业务逻辑、权限设计。


常见问题

Q:AI 自审有效果吗?它会不会只说「代码没问题」?

有效果,但得问对。笼统问「这段代码有问题吗」,它倾向于说没问题。具体问「这里有没有 SQL 注入风险?找所有把变量拼进查询的地方」,命中率高很多。把要审的维度一条条列出来,让它逐条回答。审完之后,要求它「如果有问题,直接给我改好的版本」而不是只说「可能有问题」。

Q:lint 工具能替代人工 review 吗?

不能。lint 和静态分析擅长机械规则:命名规范、潜在空指针、已知的漏洞模式。但它看不了业务逻辑——接口权限设计对不对、这段代码实现的是不是你真正要的需求、越权漏洞的背后逻辑是否有问题。工具是过滤器,人是判断者。两者都要,不能互相替代。

Q:项目小、代码量不多,也要这么认真 review 吗?

安全漏洞不挑项目大小。一个 50 行的 API 接口,如果有越权漏洞,一样会出事。你可以根据风险程度缩短 review 的深度——比如一个纯静态展示页面,安全风险极低,可以快速过;涉及用户数据和支付的接口,不管代码量多少,核心项必须认真过一遍。

Q:我怎么知道 AI 有没有「假改」——回复里说改了但代码没变?

两个方法:一是要求它输出完整的改后代码,不要只说「把第 X 行改成……」;二是养成用 git diff 确认改动的习惯——改完就 diff 一遍,亲眼确认改动内容。测试驱动的工作流里也提到这点:跑测试是确认功能的方式,diff 是确认改动的方式。

Q:安全 review 上线前做一次就够了吗?

不够。安全是个持续过程,不是一次性的。上线前做一次完整 review,之后每次新功能上线前做针对性 review,依赖库定期 audit 一遍。另外,上线安全与合规自查里有一张上线前的完整 checklist,可以配合本文的 review 清单一起用。


上线之前,花半小时对着 review 清单过一遍,比上线之后花三天处理安全事故要划算得多。把 lint 和安全扫描接进 CI,让跳过 review 变得麻烦——这不是对 AI 不信任,是工程化的基本素养。

想系统搭好整个工程化工作流,AI 编程教程大全 里的工程化专题(L4)有完整路线;上线安全与合规自查 是这篇的自然延伸,对照着把上线前的安全门槛也过一遍。

👉 看看我们的 AI 编程实战体系课,或逛 AI 编程教程大全 把基本功打扎实。

📄 来源 / 自校链接

本文为学习整理,关键步骤与代码请结合下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为学习与落地整理,AI 工具与平台更新较快,关键步骤请结合官方最新资料验证。见免责声明