☰
AI生成代码上生产前的四道检查:编译、逻辑、安全与生产适配
2026/10/2 4:23:18 网站建设 项目流程

1. 先搞清楚“AI 写的代码”到底处在什么位置

“AI 生成的代码敢直接上生产吗?”这个问题,我在团队里被问过不下十次。每次我的回答都一样:敢不敢上生产,跟代码是谁写的没关系,跟代码经过了什么检查有关系。人写的代码照样能把生产库删了,AI 写的代码也有跑得稳稳当当的。真正决定能不能上生产的,是你上线前那几道关卡有没有认真做。

先把一个误区掰开:很多人把“AI 生成代码”当成一个整体来讨论,其实它至少分三种情况,风险等级完全不同。

第一种是辅助补全型,比如你在 IDE 里敲了个方法名,AI 帮你把剩下的循环补完。这种代码你基本是逐行看过的,风险最低,本质上还是你在写,AI 只是个打字加速器。

第二种是整函数/整模块生成型,你给一段注释或者一个需求描述,AI 吐出来几十上百行。这种就需要你完整 review,因为它可能引入你根本没想过的依赖、边界处理或者性能问题。

第三种是Agent 自主执行型,AI 自己读代码库、自己改文件、自己跑测试。这种风险最高,因为它改动的范围可能超出你的预期,而且中间步骤你不一定都看到了。

我这次要聊的,是我自己项目里真实走的一遍流程:一个用 AI 辅助生成的 Java 服务模块,从“AI 吐出来”到“上线生产”,中间我做了四道检查。这四道检查不是什么高深的东西,但每一道都拦下过真实的问题。下面我把每一道拆开讲,包括为什么这么设计、具体怎么操作、以及我在实操中踩过的坑。

提示:本文说的“上生产”,指的是部署到真实用户会访问的环境。测试环境、预发环境不在此列,那些环境本来就该随便折腾。

2. 第一道检查:编译与静态分析,别让低级错误浪费你的时间

2.1 为什么第一道是编译而不是 review

很多人拿到 AI 生成的代码,第一反应是打开逐行读。我建议你先别急,先让它过一遍编译和静态分析。原因很简单:AI 生成的代码里有相当一部分问题是机械性的,比如引用了不存在的包、方法签名对不上、变量名拼错、泛型写错。这些问题你肉眼 review 也能发现,但效率极低,而且容易看漏。

让工具先跑一遍,把机械性问题全部清掉,你再去 review 剩下的逻辑,注意力才能集中在真正需要人判断的地方。这是我一直坚持的顺序:机器能查的交给机器,人只做人才能判断的事。

具体操作上,Java 项目我一般跑这几步:

# 1. 先编译,看有没有语法和类型错误 mvn clean compile # 2. 跑静态分析,我常用 SpotBugs + Checkstyle mvn spotbugs:check mvn checkstyle:check # 3. 如果有 SonarQube,直接跑扫描 mvn sonar:sonar

2.2 静态分析到底在查什么

静态分析工具查的东西,大致分几类,我按对 AI 代码的命中率排个序:

问题类型典型表现AI 代码命中率
空指针风险调用了可能为 null 的返回值高
资源未关闭流、连接没放进 try-with-resources高
异常吞掉catch 块里什么都不做中
并发问题共享变量没加同步中
硬编码密钥、URL 直接写死中
死代码永远走不到的分支低

我实测下来,空指针和资源未关闭是 AI 生成代码的两大高发区。原因也不难理解:AI 是根据上下文概率生成代码的,它倾向于写出“看起来合理”的调用链,但不一定知道你这个方法的返回值在什么情况下会是 null,也不一定记得帮你补上 close。

2.3 一个真实的拦截案例

有一次 AI 帮我生成了一个读取配置文件的方法,大概是这样的:

public String readConfig(String path) { BufferedReader reader = new BufferedReader(new FileReader(path)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); }

逻辑上没问题,编译也过。但静态分析直接报了资源未关闭。这个 reader 如果不在 finally 里关掉,在高频调用场景下会耗尽文件句柄。AI 没写 close,因为它生成的是一段“功能正确”的代码,而不是“生产可用”的代码。这就是为什么第一道检查不能省。

注意:静态分析会有误报,不要看到报错就无脑改。我的做法是先看规则名,再判断这个告警在当前上下文里是否成立。比如某些空指针告警,如果你能证明这个值不可能为 null,加个注解抑制掉就行,不用硬改。

3. 第二道检查:逻辑正确性,AI 最擅长“看起来对”

3.1 编译过了不代表逻辑对

第一道检查能拦下机械性错误,但拦不住逻辑错误。而逻辑错误恰恰是 AI 生成代码最危险的地方,因为它看起来太对了。AI 生成的代码通常结构清晰、命名规范、注释齐全,读起来很舒服,这种“表面质量”很容易让人放松警惕。

我踩过的一个坑:AI 帮我写了一个分页查询的方法,参数是 pageNum 和 pageSize,它内部算 offset 的时候写的是pageNum * pageSize。看起来没问题对吧?但我们的接口约定 pageNum 从 1 开始,所以正确的 offset 应该是(pageNum - 1) * pageSize。这个 bug 编译能过、静态分析查不出、单元测试如果只测第一页也发现不了。它是上线后用户翻到第二页发现数据重复才暴露的。

3.2 我 review AI 代码时重点看什么

经过多次踩坑,我总结了一个 review AI 代码的检查清单,按优先级排:

  1. 边界条件:空集合、null 输入、零值、负数、最大值,这些 AI 经常处理不全。
  2. 循环边界:是<还是<=,是length还是length - 1,差一错误高发。
  3. 状态变更:方法有没有副作用,改了哪些共享状态,并发下安不安全。
  4. 异常路径:出错时是抛异常还是返回默认值,调用方能不能正确处理。
  5. 业务语义:这个逻辑符不符合我们的业务规则,AI 不懂你的业务。

其中第 5 点是最需要人的。AI 可以写出语法完美、逻辑自洽的代码,但它不知道“这个用户状态在业务上不允许被删除”这种规则。这类问题只能靠熟悉业务的人来把关。

3.3 用单元测试把逻辑钉死

review 完之后,我会针对 AI 生成的代码补单元测试。这里有个技巧:不要只测正常路径,重点测边界和异常路径。正常路径 AI 自己大概率是对的,出问题的地方往往是它没想到的那些情况。

@Test public void testPagination_FirstPage() { // 正常路径,AI 一般能过 List<Item> result = service.query(1, 10); assertEquals(10, result.size()); } @Test public void testPagination_SecondPage() { // 边界路径,这里曾经抓出过 offset 计算的 bug List<Item> result = service.query(2, 10); assertEquals(10, result.size()); // 验证第二页数据和第一页不重复 } @Test public void testPagination_EmptyResult() { // 空结果,AI 经常在这里 NPE List<Item> result = service.query(999, 10); assertTrue(result.isEmpty()); }

我一般要求 AI 生成的代码,单元测试覆盖率至少覆盖到所有分支。不是追求那个百分比数字,而是通过写测试的过程,逼自己把每个分支都想一遍。

4. 第三道检查:安全与依赖,AI 不知道你的安全底线

4.1 AI 生成代码的安全盲区

安全这块是 AI 生成代码的重灾区,因为 AI 的训练目标是“生成能跑的代码”,不是“生成安全的代码”。它不知道你的安全规范,也不知道当前最新的漏洞库。我遇到过几类典型问题:

第一类是硬编码敏感信息。AI 有时候会把示例里的密钥、token 直接写进代码,因为它觉得这样“完整”。这个必须拦,生产代码里绝对不能出现任何明文密钥。

第二类是引入了有漏洞的依赖版本。AI 生成 pom.xml 或者 build.gradle 的时候,用的可能是它训练数据里的旧版本,那个版本可能已经有已知漏洞了。

第三类是输入校验缺失。AI 生成的接口方法,经常直接拿参数就用,不做长度校验、格式校验、范围校验。SQL 注入、路径穿越这类问题往往就从这里来。

4.2 依赖检查怎么做

依赖检查我用两个工具配合:OWASP Dependency-Check 查已知漏洞,mvn dependency:tree 看依赖树有没有冲突。

# 查依赖漏洞 mvn org.owasp:dependency-check-maven:check # 看依赖树,排查版本冲突 mvn dependency:tree -Dverbose

依赖冲突这个问题在 AI 生成的代码里特别隐蔽。因为 AI 可能给你引入了一个新库,而这个库又传递依赖了某个你已经在用的库的不同版本,导致运行时行为和你预期的不一样。这种问题编译期发现不了,跑起来才炸。

我一般的做法是:AI 每引入一个新依赖,我都要在依赖树里确认它没有和现有依赖打架。如果打架,用<dependencyManagement>统一版本,别让它自己选。

4.3 输入校验不能省

对于 AI 生成的对外接口,我会强制加一层校验。Java 里我常用 Bean Validation:

public class QueryRequest { @NotNull @Min(1) private Integer pageNum; @NotNull @Min(1) @Max(100) private Integer pageSize; @Size(max = 50) private String keyword; }

然后在 Controller 上加@Valid。这层校验看起来啰嗦,但它能挡掉大量脏输入。AI 生成代码时不会主动加这些,你得自己补。

提示:安全检查和依赖检查最好做成 CI 流水线的一环,每次提交自动跑。靠人记着做,迟早会漏。

5. 第四道检查:生产环境适配,本地跑通不等于线上能跑

5.1 环境差异是最大的隐形杀手

前三道检查都在代码层面,第四道检查是环境层面。这是最容易被忽略、但上线后最容易出事的地方。AI 生成的代码在本地跑得好好的,一上生产就各种问题,原因基本都是环境差异。

我列一下常见的环境差异点:

差异点本地环境生产环境可能引发的问题
数据库单机、小数据量集群、大数据量慢查询、连接池耗尽
配置硬编码或本地文件配置中心配置读取失败
并发单线程测试高并发线程安全问题
网络直连经过网关、负载均衡超时、连接重置
资源充足受限OOM、CPU 打满

5.2 配置外置是底线

AI 生成的代码经常把配置写死在代码里,比如数据库地址、超时时间、线程池大小。这些必须外置到配置中心或者环境变量。我一般会检查这几类配置有没有外置:

  • 数据库连接信息
  • 第三方服务的地址和密钥
  • 超时时间、重试次数
  • 线程池、连接池的大小
  • 开关类的功能标志
# 正确做法:配置外置 spring: datasource: url: ${DB_URL} username: ${DB_USER} password: ${DB_PASSWORD} redis: timeout: ${REDIS_TIMEOUT:2000}

冒号后面的值是默认值,这样即使配置中心没配,也有个兜底。

5.3 并发和资源问题要专门测

AI 生成的代码在单线程下跑通很容易,但生产环境是多线程的。我一般会针对 AI 生成的核心方法做一轮并发测试,看看有没有共享状态被并发修改的问题。

@Test public void testConcurrentAccess() throws Exception { int threadCount = 50; ExecutorService executor = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { executor.submit(() -> { try { service.process(sharedData); } finally { latch.countDown(); } }); } latch.await(); // 验证结果是否符合预期 }

这个测试不追求覆盖率,就是专门用来暴露并发问题的。AI 生成的代码如果用了非线程安全的集合、或者有竞态条件,这个测试大概率能抓出来。

5.4 上线前的最后一道人工确认

四道检查都过了之后,我还会做一件事:把 AI 生成的代码和人工写的代码分开标记,上线后重点观察。具体做法是在日志里加上标记,或者在监控里单独建一个看板。

这样做的目的是:如果上线后真出了问题,我能快速定位是不是 AI 生成的代码引起的。这不是不信任 AI,而是给自己留一条快速回滚和排查的路。实测下来,这个习惯帮我省过好几次排查时间。

6. 四道检查之外,我踩过的那些坑

6.1 过度信任 AI 的“自信语气”

AI 生成代码的时候,注释和命名都特别自信,读起来像是经过深思熟虑的。但实际上它可能只是概率上觉得这样写合理。我踩过最坑的一次,是 AI 生成了一段加密逻辑,注释写着“使用 AES-256 加密”,实际代码里 key 的长度根本不够 256 位。这种问题你不逐行核对参数是发现不了的。

所以我的经验是:AI 生成的代码,注释可以看,但不能信。注释说是什么,你得自己验证是不是真的。

6.2 忽略了 AI 的“版本幻觉”

AI 有时候会调用一些不存在的方法,或者用了某个库在新版本里已经废弃的 API。这在编译期能发现一部分,但有些是运行时才报错的。我的做法是:AI 生成的代码里,凡是调用第三方库的地方,我都去官方文档核对一遍方法签名和版本。麻烦是麻烦,但比上线后炸了强。

6.3 忘了检查日志和监控埋点

AI 生成代码时不会主动加日志和监控埋点,它只管功能实现。但生产环境没有日志和监控,出了问题就是两眼一抹黑。我一般会在 review 的时候专门检查:关键路径有没有日志、异常有没有记录、核心指标有没有埋点。这块 AI 帮不上忙,得自己补。

6.4 测试数据和生产数据不是一回事

AI 生成的代码在测试数据上跑得好,不代表在生产数据上跑得好。生产数据可能有脏数据、有极端值、有历史遗留的特殊格式。我一般会从生产环境脱敏拉一批真实数据,在预发环境跑一遍,看看有没有意外情况。这一步能抓出不少边界问题。

7. 我的最终结论:不是敢不敢,是流程到不到位

回到最开始的问题:AI 生成的代码敢直接上生产吗?

我的答案是:经过完整检查流程的 AI 代码,可以上生产;没经过检查的,不管是谁写的,都不该上。这四道检查——编译与静态分析、逻辑正确性、安全与依赖、生产环境适配——不是专门为 AI 代码设计的,它们本来就是任何代码上生产前都该做的。只不过 AI 代码在某些环节的出错率更高,所以这些检查更不能省。

我自己的实践是,AI 生成的代码大概能帮我省掉 60% 到 70% 的敲键盘时间,但 review 和检查的时间省不掉,甚至因为要额外核对一些东西,某些环节比纯手写还慢一点。但整体算下来还是划算的,因为 AI 帮我跳过了那些机械性的、不需要思考的部分,让我能把精力集中在真正需要判断的地方。

最后分享一个我一直在用的小习惯:每次 AI 生成的代码上线后,我都会记录一下它有没有出问题、出了什么问题。攒了一段时间之后,你会发现 AI 在某些类型的代码上特别容易出错,在另一些类型上则很稳。有了这个数据,你就能更精准地决定哪些代码可以让 AI 多写、哪些必须自己来。这比笼统地说“AI 代码行不行”有用得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询