AI生成的代码能直接上生产吗?上线前必做的四道检查
1. 先搞清楚AI 写的代码到底能不能上生产“AI 生成的代码敢直接上生产吗”这个问题我在团队里被问过不下十次。每次新项目赶工期总有人提议让 AI 先把增删改查和接口骨架撸出来省掉重复劳动。我的态度一直很明确AI 生成的代码可以进生产但绝不能“直接”进生产。这两者之间的差别就是我今天要聊的四道检查。先把结论摆在这里AI 写代码的本质是基于海量代码语料做概率预测它擅长的是“看起来对”而不是“确实对”。它不知道你的业务约束、不知道你的数据库索引长什么样、不知道你们团队对异常处理有什么约定。所以它产出的代码更像是一个干活很快但不太靠谱的实习生交上来的初稿——结构可能没问题细节处处是坑。这篇文章适合三类人看一是正在用 AI 辅助写 Java 后端、准备把代码合并进主干的开发二是负责代码评审、需要判断 AI 代码质量的技术负责人三是对 AI 编程好奇、想知道实际落地边界在哪的同行。我会围绕代码检查这条主线把上线前必须做的四道关卡拆开讲清楚每一道都配上我实际踩过的坑和可复现的操作方法。需要提前说明的是下面提到的工具和流程都是基于常见 Java 工程实践做的合理补充不是某个特定平台的专属方案你完全可以按自己团队的情况调整。2. 第一道检查语法与编译层面的硬性拦截2.1 为什么编译通过不等于没问题很多人觉得AI 生成的代码只要mvn compile能过就万事大吉了。我早期也这么想过直到有一次 AI 给我生成了一段用了 Java 17 新特性的代码而我们的生产环境还跑在 Java 11 上。编译在本地过了打包上服务器直接UnsupportedClassVersionError整个发布流程卡住。这就是第一道检查要解决的核心问题AI 不知道你的运行环境边界。它训练数据里混着各个 Java 版本的写法生成时不会主动问你“你们用 JDK 几”。所以第一道检查不是简单跑个编译而是要确认三件事语言版本匹配、依赖坐标真实存在、语法糖没有超出目标环境支持范围。2.2 具体怎么操作我通常的做法是分三步走。第一步把 AI 生成的代码单独放到一个隔离模块里用项目实际的pom.xml或build.gradle去编译而不是随手新建一个工程。这一步能暴露出依赖缺失和版本冲突。第二步检查 AI 引入的第三方库重点看 groupId 和 artifactId 是否真实存在。AI 编造依赖坐标是家常便饭比如把commons-lang3写成commons-lang4或者虚构一个根本不存在的工具包。第三步用jdeps或者 IDE 的字节码版本检查功能确认编译产物能在目标 JDK 上运行。# 检查编译产物的字节码版本 javap -verbose YourClass.class | grep major version # Java 8 对应 52Java 11 对应 55Java 17 对应 61注意AI 特别喜欢用var关键字和 Stream API 的链式调用这些在 Java 8 环境下部分不可用。如果你的生产环境是 Java 8务必让 AI 在提示词里明确“使用 Java 8 兼容语法”。2.3 我踩过的坑有一次 AI 生成了一段 JSON 处理代码引入了com.google.gson的一个高版本而我们项目里已经用 Jackson 做了全局配置。结果两个库的日期序列化行为不一致接口返回的时间格式在测试环境看着正常上了生产因为时区问题全乱了。后来我养成了一个习惯AI 生成的代码里每出现一个新依赖都要问一句“项目里有没有现成的同类库”。能复用就复用不能复用再引入并且要在依赖管理文件里显式锁定版本。3. 第二道检查逻辑正确性与边界条件3.1 AI 最擅长写“快乐路径”编译过了接下来才是真正的硬仗。AI 生成的代码有一个非常明显的特征主流程写得漂漂亮亮边界条件一塌糊涂。你让它写一个根据用户 ID 查订单的方法它会给你一个完美的select * from order where user_id ?但它不会考虑用户 ID 为空怎么办、订单不存在怎么办、分页参数越界怎么办。我做过一个统计在我们团队最近三个月 AI 辅助生成的代码里逻辑缺陷有七成集中在边界处理上。空指针、数组越界、除零、并发修改这些经典问题 AI 一个不落。所以第二道检查的核心就是把 AI 当成一个只会写正常流程的新人你来负责补全所有异常分支。3.2 边界检查清单我整理了一份针对 Java 后端代码的边界检查清单每次评审 AI 代码时逐条过检查项常见问题检查方法空值处理方法入参未判空直接调用方法看每个 public 方法的参数校验集合操作对可能为空的 List 直接 for 循环检查集合来源和判空逻辑数值计算除法未判零大数溢出审查所有算术运算字符串处理未判空直接 substring 或 split检查字符串来源并发安全共享变量未加锁HashMap 多线程写看是否有线程共享状态资源释放流、连接未关闭检查 try-with-resources这张表看着基础但 AI 生成的代码几乎每条都会中招。特别是空值处理AI 默认假设所有输入都是合法的这在真实业务里根本不可能。3.3 用单元测试反向验证光靠人眼看还不够我习惯让 AI 自己生成单元测试然后我来补充边界用例。具体做法是先让 AI 为它生成的业务方法写测试通常它会写三五个正常场景的用例。然后我手动加上空参数、极值、异常输入这些用例跑一遍看会不会挂。挂了就说明逻辑有问题让 AI 修修完再跑。// AI 生成的测试通常长这样 Test public void testGetOrder() { Order order service.getOrder(1L); assertNotNull(order); } // 我会补充的边界用例 Test public void testGetOrderWithNullId() { assertThrows(IllegalArgumentException.class, () - service.getOrder(null)); } Test public void testGetOrderWithNegativeId() { assertThrows(IllegalArgumentException.class, () - service.getOrder(-1L)); }提示让 AI 修 bug 的时候一定要把失败的测试用例一起贴给它并且明确说“这个用例必须通过”。否则它可能改了一个地方又引入另一个问题。3.4 一个真实的翻车案例去年做一个优惠券核销功能AI 生成了一段计算折扣的代码逻辑是“满减金额不能超过订单金额”。测试环境跑得好好的上线第二天财务发现有一笔订单的实付金额变成了负数。排查下来是 AI 写的判断用了而不是当满减金额恰好等于订单金额时实付算出来是零但后续还有一个叠加折扣的逻辑直接把金额减成了负数。这个 bug 在正常测试数据下根本触发不了因为测试订单金额都是整数且大于满减额。边界值差一个等号生产环境就能让你赔钱。4. 第三道检查代码规范与可维护性4.1 规范不只是面子问题有人觉得代码规范是形式主义能跑就行。但 AI 生成的代码如果不做规范检查三个月后你自己都看不懂。AI 写代码没有统一的风格同一个项目里它可能这段用驼峰命名那段用下划线这个类里注入依赖用构造器那个类里用字段注入。更麻烦的是注释AI 要么不写注释要么写一堆废话注释比如// 设置名称这种把方法名翻译一遍的。第三道检查的目标是让 AI 代码看起来像是团队里一个正常成员写的。这不仅是审美问题更直接影响后续维护成本。一个命名混乱、注释缺失、圈复杂度爆表的类改起来的时间够你重写三遍。4.2 静态检查工具链Java 生态里做代码规范检查的工具很成熟我常用的组合是 Checkstyle SpotBugs PMD。Checkstyle 管格式和命名SpotBugs 管潜在 bugPMD 管代码坏味道。把这三个工具配到 CI 流程里AI 代码提交时自动跑一遍不通过就打回。!-- Checkstyle 配置片段强制要求方法参数判空 -- module nameRequireThis/ module nameMissingSwitchDefault/ module nameIllegalThrows/配置的时候有个技巧针对 AI 代码把圈复杂度阈值调低。正常人手写的代码圈复杂度超过 10 我们会警告AI 代码超过 8 我就建议拆分了。因为 AI 特别喜欢把一堆逻辑塞进一个方法里它不会主动做方法抽取。4.3 命名和注释的专项检查命名这块AI 生成的变量名经常是data、result、temp、list这种毫无信息量的词。我的做法是在评审时把所有这类命名标出来让 AI 重命名并且要求“变量名要能体现业务含义”。比如data改成userOrderListresult改成discountAmount。注释方面我要求 AI 生成的每个 public 方法必须有 Javadoc说明参数含义、返回值、可能抛出的异常。但 AI 写的 Javadoc 经常是方法签名的复读比如/** * 获取用户 * param userId 用户ID * return 用户 */ public User getUser(Long userId) { ... }这种注释等于没写。我会让 AI 补充“为什么需要这个方法”“什么场景下调用”“userId 为空时行为是什么”。注释要解释意图而不是翻译代码。4.4 可维护性的隐藏指标除了工具能查的还有一些 AI 代码特有的坏味道需要人工识别。比如 AI 喜欢生成超长的 if-else 链明明可以用策略模式或者枚举它偏要写十几个分支。再比如 AI 生成的工具类方法参数列表经常超过五个调用的时候根本记不住顺序。这些在静态检查里不一定报错但会严重拖慢后续迭代速度。我的经验是AI 代码合并前至少让两个不同的人看过。一个人看逻辑一个人看规范。AI 自己评审自己没意义它看不出自己的问题。5. 第四道检查安全与性能的底线验证5.1 安全漏洞是 AI 代码的重灾区AI 生成代码时脑子里没有“攻击者”这个概念。它不会主动考虑 SQL 注入、XSS、越权访问这些安全问题。我见过 AI 生成的查询代码直接拼接字符串也见过它把用户输入直接塞进Runtime.exec()。这些代码在功能测试时完全正常但一旦上线就是敞开的门。第四道检查必须包含安全扫描。Java 项目我推荐用 OWASP Dependency-Check 扫依赖漏洞用 SonarQube 的 Security Hotspots 扫代码里的危险模式。重点看几类问题SQL 拼接、命令执行、文件路径拼接、反序列化、敏感信息硬编码。// AI 可能生成的危险代码 String sql select * from user where name name ; // 必须改成参数化查询 String sql select * from user where name ?; PreparedStatement ps connection.prepareStatement(sql); ps.setString(1, name);注意AI 有时候会用 ORM 框架的“原生查询”功能然后在里面拼字符串。这种最隐蔽因为表面上看是在用框架实际上还是注入了。评审时要特别留意createNativeQuery、createSQLQuery这类调用。5.2 性能问题往往藏在细节里AI 生成的代码在性能上通常有两个极端要么过度优化写一堆看不懂的位运算要么完全不考虑性能在循环里查数据库。后者更常见。我见过 AI 写的一个批量处理逻辑在 for 循环里逐条调用selectById一万条数据查了一万次数据库。测试环境数据量小跑得挺快生产环境直接超时。性能检查我主要看几个点循环内是否有数据库调用或远程调用、是否有 N1 查询、集合初始化容量是否合理、字符串拼接是否在循环里用。这些用 SonarQube 的性能规则能扫出一部分但更关键的还是人工评审时多问一句“这段代码在数据量放大一百倍后会怎样”。5.3 安全检查的实操流程我把安全检查分成三步。第一步依赖扫描用工具自动跑高危漏洞直接阻断合并。第二步代码模式扫描重点看输入输出相关的代码所有外部输入必须经过校验和转义。第三步人工渗透测试针对 AI 生成的核心接口手动构造异常请求看会不会泄露信息或者越权。检查类型工具阻断标准依赖漏洞OWASP Dependency-CheckCVSS 评分 7 以上阻断代码模式SonarQube所有 Blocker 和 Critical 阻断输入校验人工评审任何未校验的外部输入阻断权限控制人工评审任何未鉴权的接口阻断5.4 一个差点上线的安全事件有一次 AI 生成了一个文件下载接口逻辑是根据前端传的文件名去服务器目录找文件返回。功能测试完全正常下载速度也快。我在安全检查时发现它没有对文件名做任何过滤如果传入../../etc/passwd这种路径就能下载服务器上的任意文件。这个漏洞如果上了生产后果不堪设想。后来我让 AI 修它第一次修只是简单替换了..还是能被绕过。最后我手动写了路径规范化校验确保文件只能从指定目录读取。这件事给我的教训是安全问题上不能完全信任 AI 的修复能力。它可以帮你发现一些问题但修复方案必须由人来把关。6. 四道检查之后AI 代码怎么管6.1 建立 AI 代码的准入标准四道检查跑完并不意味着 AI 代码就能随便用了。我们团队后来定了一个规矩AI 生成的代码必须经过“人工重写”才能合并。不是说不信任 AI而是 AI 代码的逻辑需要被真正理解一遍。我的做法是把 AI 代码当成一个需求描述自己照着重新实现一遍实现过程中自然会发现 AI 没考虑到的地方。这个做法听起来费时间但实际上比反复修 AI 的 bug 要快。AI 生成的代码平均要改三到五轮才能达到可合并状态而自己重写一遍通常一轮就过了。AI 的价值在于给你一个起点和思路而不是终点。6.2 把检查流程自动化人工检查很累能自动化的尽量自动化。我们在 CI 里配了这样一条流水线代码提交后自动跑编译检查、静态扫描、单元测试、安全扫描任何一项不通过就阻断合并。只有全部通过后才进入人工评审环节。这样人工只需要关注逻辑和设计不用再操心格式和基础漏洞。# CI 流水线示意 stages: - compile - static-check - test - security-scan - manual-review6.3 我个人的使用心得用了大半年 AI 辅助编程我最大的体会是AI 适合做“从零到一”的草稿不适合做“从一到一百”的打磨。它能在几秒钟内给你一个能跑的骨架但把这个骨架变成生产级代码需要的时间可能比你自己写还多。所以我现在只在两种场景下用 AI一是写那些我完全不想手写的样板代码比如 DTO 转换、配置文件二是遇到不熟悉的库或 API让 AI 给个示例我照着改。至于“AI 生成的代码敢直接上生产吗”这个问题我的答案始终是敢用但不敢直接上。四道检查一道都不能少而且检查的人必须对生产环境有敬畏心。AI 不会为线上事故负责负责的是你。