
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:sonar2.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 代码的检查清单按优先级排边界条件空集合、null 输入、零值、负数、最大值这些 AI 经常处理不全。循环边界是还是是length还是length - 1差一错误高发。状态变更方法有没有副作用改了哪些共享状态并发下安不安全。异常路径出错时是抛异常还是返回默认值调用方能不能正确处理。业务语义这个逻辑符不符合我们的业务规则AI 不懂你的业务。其中第 5 点是最需要人的。AI 可以写出语法完美、逻辑自洽的代码但它不知道“这个用户状态在业务上不允许被删除”这种规则。这类问题只能靠熟悉业务的人来把关。3.3 用单元测试把逻辑钉死review 完之后我会针对 AI 生成的代码补单元测试。这里有个技巧不要只测正常路径重点测边界和异常路径。正常路径 AI 自己大概率是对的出问题的地方往往是它没想到的那些情况。Test public void testPagination_FirstPage() { // 正常路径AI 一般能过 ListItem result service.query(1, 10); assertEquals(10, result.size()); } Test public void testPagination_SecondPage() { // 边界路径这里曾经抓出过 offset 计算的 bug ListItem result service.query(2, 10); assertEquals(10, result.size()); // 验证第二页数据和第一页不重复 } Test public void testPagination_EmptyResult() { // 空结果AI 经常在这里 NPE ListItem 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 Validationpublic 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 代码行不行”有用得多。