$Wynn5a 技术博客 - AI编程与软件工程实践
~/blog/review-ai-generated-code

如何高效审查 AI 生成的代码

随着 AI 编程助手(如 GitHub Copilot、Cursor 等)的普及,更确切的说是风靡,开发者面临的另外一个新挑战是如何高效且安全地审阅这些 AI 生成的代码。AI 生成代码的特性与人类编写的代码有本质区别:它生成速度极快、表面看似合理且天衣无缝,但可能隐藏着深层的逻辑漏洞、胡乱编造的 API 或安全风险等。

结合我最近的 100% AI 编写代码的实践,总结一下在实际工作中如何高效的对 AI 编写的代码进行 Code review

默认 AI 代码是不可信的#

审阅 AI 代码时,最大的误区是将其等同于需要指导的初级开发者所写的代码。AI 不会像初级开发者那样犯明显的语法错误,它经常将危险或者错误隐藏在看似合理的外衣下,要么逻辑充斥着漏洞跟你需求有偏差,要么依着自己的幻觉编造错误的 API 或者采用过时的 API,要么完全偏离架构设想,用自己的“代码品味”胡作非为。

所以看到 AI 提交的 PR,你必须假设它的实现不可信,多问几个问题

  • 不要问“这看起来合理吗?”而应该问“这段代码做出了哪些假设?这些假设在我们的系统中安全吗?成立吗?”
  • 防范“表面正确性(Plausibility)”,AI 会调用训练数据中存在的库版本或方法,但这些可能在你的实际项目中并不存在。这种错误往往在运行时才会暴露,所以再问问自己,“它的 PR 进过验证么?单元测试或者 API 测试通过了么?”

采用两遍阅读法(Two-Pass Review)#

不要一上来就逐行阅读,这会消耗大量精力且容易迷失在细节中。建议采用分层审阅策略:

第一遍:高维度审查(意图与架构)

  • 审查 Prompt/Spec/Design,而不是仅仅审查输出:理解 AI 是基于什么上下文生成代码的
  • 检查依赖和造轮子行为:AI 往往不知道你项目中已有的内部工具库,倾向于重新实现功能,应该寻找是否存在类似的行为并建议将其替换为项目中现有的代码或依赖
  • 验证架构完整性:这段代码是否符合系统的整体架构设计?是否引入了不必要的复杂性?是否在该引入抽象层级的时候偷懒了

第二遍:低维度审查(执行与安全)

  • 追踪数据流:数据从哪里来,到哪里去?是否有未经验证的输入?
  • 检查边界情况(Edge Cases):AI 倾向于编写“快乐路径(Happy Path)”的代码,经常忽视异常处理和极端情况
  • 代码规范:简单却必须验证的部分就是这个 PR 是否按照项目现有的规范编写,是否符合我们项目的最佳实践,是否保持良好的可维护性等

强化测试驱动与动态验证#

静态阅读 AI 代码往往不够,因为 AI 擅长伪装。

  • 先运行,再阅读(Read before you run, but always run it):不要仅仅在 PR 中静态审阅,必须将其拉取到本地运行,观察其在真实环境中的行为,所以我及其推荐使用 IDE 进行 PR 的审查,这样你可以将这个 PR 跑起来,仔细端详
  • 警惕为通过而通过的测试:AI 在生成测试代码时,为了让测试变绿,可能会过度使用 Mock 或 Stub,导致测试根本没有覆盖真实的业务逻辑,必须严格审查测试代码本身的有效性。
  • 要求可观测性:确保 AI 生成的复杂逻辑中包含足够的日志记录,以便在生产环境中出现问题时可以追踪,这个时候跑一遍看看日志就很有必要

聚焦高风险区域#

AI 生成代码的速度远超人类审查的速度,因此你的时间必须花在刀刃上

  • 身份验证与授权:任何涉及权限控制、用户登录的代码
  • 状态管理与数据持久化:涉及数据库写入或关键状态修改的代码
  • 外部 API 交互:涉及向第三方发送数据或接收不受信任数据的代码
  • 抓大放小:对于低风险的样式调整或简单的纯函数,可以适当降低审查深度,但是确保单元测试覆盖到了这些地方

解决人与流程的问题#

如果团队被大量劣质的 AI 代码淹没,这通常不是工具的问题,而是流程的问题

  • 要求开发者自证其码:提交 PR 的开发者必须能够完全解释 AI 生成的代码。如果你不能解释它,你就不拥有它,如果你不拥有它,你就没办法维护它。
  • 控制单次提交的规模:AI 很容易一次性生成数百行代码,但这超出了人类高效审查的极限,强制要求将大块的 AI 生成代码拆分为小批量、逻辑独立的 PR 进行提交。

采用智能体对抗性审查#

永远不要让同一个 AI Agent 审查它自己生成的代码,如果你让一个 Agent 写了一段函数,紧接着在同一个对话窗口中让它审查这段代码,它几乎总是会认为代码是完美的,因为它的上下文已经被生成该代码的推理过程污染了。

我们可以采用构建者-验证者模式:使用一个 Agent(如 Claude)负责生成代码,将生成的代码交给另一个处于全新上下文的独立 Agent(如 Codex 或另一个 Claude 实例)进行审查。这种“对抗性”的审查能够消除单一会话的累积偏见,让审查 Agent 能够像第三方一样客观地发现漏洞。

同时,需要特别注意的是用 AI Agent 审查代码的时候,注入的上下文必须要正确(即跟生成代码的 Agent 保持一致且符合你的需求),如果能够要求 Agent 按照特定维度进行输出审查结果就更好了,例如:“1. 找出安全漏洞;2. 检查是否有硬编码;3. 评估时间/空间复杂度;4. 检查是否符合 DRY(Don't Repeat Yourself)原则”等。

总结#

世界在发展,未来的代码审查范式将是: AI Agent 作为第一道防线,自动在每个 PR 中提供基础的代码质量和安全审计;而人类高级工程师作为第二道防线,专注于审查业务逻辑的准确性、系统架构的合理性以及理解成本(Understanding Cost)的控制,两者结合才是正道,现在有些极端的组织和团队推崇 0 人工介入的 PR 工作流,最终可能只得到一个“只能不断重新生成”的 AI Slop。