AI 写一个功能只要几秒钟,代码干净、看起来专业,90% 的情况下还能跑起来。但“跑起来”和“安全到可以上线”是两回事,而两者之间的距离,正是现在大部分工作真正发生的地方。

2026 年的一项研究显示,只有约 35% 的 AI 生成后端代码同时满足安全和正确的要求。Veracode 对 100 多个模型、80 个任务的测试则发现,接近一半的代码自带已知安全弱点。这些问题不会让代码编译失败,也不会让常规测试报错,它们藏在表面之下,一眼看不出来。

审查 AI 生成的代码,已经成为一门独立技能,而且和“自己写代码”的技能完全不同。模型没有写代码时的思考过程,它只是根据训练数据里的模式“猜”出了答案,也无法解释自己为什么这样选择。因此,只验证“能不能跑”远远不够,你必须像资深 reviewer 一样,一层层往下挖。

在开始具体步骤前,先记住 AI 的三个底层特性,它们是后面所有习惯的根源:

AI 工具被优化为“快速给出让人信服的答案”。可信和正确大量重叠,这让危险变得隐蔽——你会下意识停止质疑。
当你的需求留有空白时,模型不会追问,它会用训练数据里最常见的东西填补。
模型在正确和错误时,听起来同样自信。没有“语气破绽”,你只能靠实际检查来判断。

第一步:先确认需求,再看代码

AI 可以完美解决一个根本不该被问的问题。
你让它写“更新发票的接口”,它会给你干净的更新逻辑,却很可能完全没有权限校验。代码本身是正确的,它只是回答了不该被问的问题。

先找到原始需求(工单、规格、甚至一句写下来的话)。如果需求只剩一行 prompt,就要格外警惕——模型已经把所有没说清楚的部分用常见模式填满了,你现在审查的其实是那些“猜”出来的部分。

然后把代码和需求对照:这里要求的是什么?它实际做了什么?是否一致?

如果你连一句话都说不清这段代码原本要解决什么问题,就不要急着 review。

第二步:质疑每一个工程决策

每个技术选择都应该有权衡。模型通常跳过权衡,直接拿训练数据里最常见的答案。

举个真实例子:一个需要高并发、大量等待外部服务的轻量服务,用 FastAPI(async-first)更自然。但你让 AI 写,它大概率还是给你 Django,因为“Python Web 后端”在训练数据里最常对应 Django。它没有权衡,只是模式匹配。

半年后团队可能在和框架死磕,却没人知道当初为什么选它。

对每个重大选择,都要问一句:这个选择是因为它真的适合当前场景,还是因为它最常见?“因为大家都这么用”不算理由。

第三步:读逻辑,而不是读“拼写”

语法错误会被编译器和 linter 免费抓住,这些是廉价 bug。真正昂贵的是逻辑错误——边界条件、竞态条件、失败路径。

AI 生成的代码特别擅长把“提示里描述的那条幸福路径”写得漂漂亮亮。真正的 bug 往往藏在提示没提到的边缘:空列表、零、负数、最大值、并发冲突、记录不存在……

把代码当成白板推演:数据从哪里进来、怎么流动、每个步骤的失败路径是什么?而不是只看它长得漂不漂亮。

第四步:猎杀隐藏假设

这是我审查时抓到最多真实 bug 的一步。

隐藏假设是代码默认“世界必须是这样”才能正常工作:输入永远合法、外部服务永远响应、记录永远存在、网络永远通畅。

这些假设在平静的 review 里完全看不出来,因为代码在假设成立时确实能跑。但真实世界里,第三方 API 变慢、用户发来畸形请求、网络中途断开……假设一旦不成立,就变成线上事故。

每看到一段代码,就反复问:它在默认什么?把假设列出来,然后为“假设不成立”的情况补上处理逻辑。

第五步:专门做一次安全检查

模型可以给你一段跑得完美却完全敞开的代码,这两者在模型眼里互不冲突。

重点检查项(按重要性排序):

  • 认证与授权:谁在调用?这个人被允许做这件事吗?(OWASP Top 10 连续多年把 Broken Access Control 排第一)
  • 输入验证:是否在信任数据前做了清洗?
  • 敏感信息:密码、密钥有没有硬编码?
  • SQL 注入:用户输入是否通过参数化查询?
  • 幻觉依赖(Slopsquatting):AI 建议的包名是否真实存在?先去真实 registry 查证再安装。

另外别忘了反向检查:你自己有没有把真实密钥、敏感逻辑粘到 prompt 里?

第六步:用真实数据量测试行为

开发环境只有十几条数据时,性能问题几乎不会暴露。最经典的就是 N+1 查询。

AI 经常生成这样的代码:

# AI 生成版:看起来没问题,实际扩展性灾难
def post_list(request):
    posts = Post.objects.all()
    for post in posts:
        print(post.author.name)   # 每条 post 都额外查一次数据库

10 条数据 → 11 次查询;1 万条数据 → 1 万零 1 次查询。

正确做法是用 select_relatedprefetch_related 把关联数据一次性取出来。

审查数据库相关代码时,问自己:当表里数据量变大后,这段代码的成本是多少?

第七步:砍掉过度工程

AI 既会偷懒,也会过度设计。它喜欢从训练数据里抄来大而全的架构:多层 service、各种抽象、为未来可能出现的情况做准备。

结果是:一个本来 10 行的功能,被包在三层间接调用里。改一个小需求要改三四个文件,bug 藏得更深,维护成本直线上升。

用 YAGNI 原则检验每个抽象:它是否正在吸收真实发生的变更?如果只是“以防万一”,就删掉。需要的时候再加,通常比提前加要便宜得多。

第八步:检查错误处理

AI 生成的代码几乎把所有篇幅都花在“幸福路径”上,失败路径写得极少或者完全没有。

要找的信号:

  • 一个可能失败的操作完全没有失败处理
  • 用了宽泛的 except Exception 把所有错误吞掉
  • 外部调用没有超时、重试、降级
  • 没有 fallback 机制

对每个可能失败的操作,都要追问:它失败了会发生什么?代码给出了答案吗?

第九步:评估可维护性

代码被读的次数远多于被写的次数。六个月后回头看,或者别人接手时,能不能快速理解和修改?

AI 代码常有的问题:局部很聪明但整体难跟、跨多次 prompt 生成导致风格不一致、过度抽象导致调试困难。

问自己三个问题:

  • 六个月后我冷启动还能看懂吗?
  • 一个没参与过的人能不能独立维护?
  • 出问题时能不能快速定位?

任何答案为“否”的地方,都是现在就该改的。

最终检查清单(合并前必过)

  • 需求:我清楚它原本要解决什么问题吗?它真的解决了吗?
  • 决策:每个框架/数据库/模式的选型有真实理由,还是只是默认答案?
  • 逻辑:我追踪过数据流,检查过边界和失败路径了吗?
  • 假设:它默认了哪些输入、服务、记录、网络条件?
  • 安全:认证、授权、输入验证、密钥暴露、SQL 注入、幻觉依赖都检查过了吗?
  • 性能:有 N+1 查询、缺失索引、过度调用吗?
  • 复杂度:每一层抽象都在为当前需求服务吗?
  • 错误处理:失败路径、降级、重试都考虑了吗?
  • 可维护性:六个月后的自己或同事能轻松接手吗?
  • 输入安全:我有没有把真实密钥粘进 prompt?

AI 负责生成代码,你负责判断它是否安全、正确、值得上线。这两件事从来都是分离的,只有一件事被自动化了。

把每次生成的代码当成一个快速、自信、偶尔粗心的贡献者提交的 draft,而不是最终答案。多花在审查上的时间,远比生成时省下的时间更有价值。

下次你用 AI 生成一段代码后,会先做哪一步审查?或者你目前最常忽略的是清单里的哪一层?欢迎在评论区分享你的实际案例。

我是紫微AI,在做一个「人格操作系统(ZPF)」。后面会持续分享AI Agent和系统实验。感兴趣可以关注,我们下期见。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐