1. 问题现场:功能全绿,系统却挂了
最近在复盘一个让我印象深刻的线上事故。AI 把需求清单上的功能全部做完了,CR 也过了,测试用例也跑了,结果一上线,线上系统直接告警,核心服务不可用。研发群炸了锅,产品经理一脸懵,AI 助手还在旁边问“还有什么需要我帮忙的吗”。
这个场景现在一点都不陌生。我在多个项目里都见过类似的剧本:AI 代码生成效率极高,完成任务速度惊人,但系统的稳定性、可维护性、扩展性却在肉眼可见地恶化。有人管这叫“技术债加速器”,我觉得更准确的说法是——AI 帮你把功能的砖砌好了,但没告诉你这堵墙的地基已经被挖空了。
这个问题的根源不在 AI 本身,而在我们对“做完”这两个字的理解。AI 理解的“完成”是“需求描述里的功能点实现”,而系统需要的“完成”是“功能可用、性能达标、边界可容错、失败可恢复、演进可持续”。这两者之间的差距,就是事故的温床。
我写这篇文章不是唱衰 AI 编程,恰恰相反,我是 AI 编程的深度用户。但正因为用得深,我才摸清了它会在哪些地方埋雷、为什么埋雷、怎么拆雷。文章会从问题拆解、核心原理、实操流程、排障手册四个维度展开,尽量把你可能踩的坑都趟一遍。
2. 为什么 AI 会“做完功能、改坏系统”
2.1 AI 的上下文窗口与局部视野陷阱
先聊一个很多人忽略的底层原因:AI 的上下文窗口是有限的。不管是 32K、128K 还是 200K,它都只能看到你喂给它的代码和文件,看不到整个系统的运行时状态、调用链路、历史演进逻辑。
这意味着什么?意味着 AI 在改一个函数的时候,它“眼里的世界”就是这个函数、它的直接调用方、被调用的几个工具函数。它看不到这个函数被多少个服务透传调用,看不到下游对超时时间的敏感度,看不到数据量在特定场景下会膨胀到什么量级。于是它就会做出一种非常典型的“局部最优决策”——在当前上下文里最合理,放到整个系统里却是破坏性的改动。
我打一个生活化的比方。你请了个很聪明的实习生帮你整理书柜,他站在书柜前,看到每本书都按开本排列得非常整齐,觉得很满意。但他不知道你的使用习惯是“经常一起用的一组工具书要放在同一格”,他把书按开本一排,视觉上是整齐了,你每天早上拿书的路径却全乱了。AI 改代码就是这个感觉,它在局部的“整洁度”上做优化,却破坏了全局的“结构合理性”。
2.2 需求描述的语义鸿沟
第二个原因是需求天然是模糊的,而 AI 的职责是消除模糊。问题在于,它消除模糊的方式不是问你,而是猜你。
举个真实例子。有个需求写的是“优化用户列表页的加载速度”,AI 可能直接把列表从同步查询改成了全量缓存。单看这个改动,性能确实提升了,功能也“做完”了。但如果这个列表背后关联着权限系统,不同用户看到的数据本就应该不同,全量缓存一上,A 用户就看到了 B 用户的数据。这就是经典的“功能达标,系统越权”。
这种问题的本质是语义鸿沟:人类语言里的“优化速度”包含着隐性约束“不能牺牲正确性”,但 AI 只看到了“速度”,然后把约束丢掉了。我自己在写提示词的时候反复强调约束,但每次也会承认:约束写多了,AI 的理解会打折;约束写少了,它就会自由发挥。这个平衡到现在也没有完美解,只能靠人来兜底。
2.3 测试驱动与回归验证的缺失
业内有个共识被 AI 编程时代放大了:AI 特别擅长写“看起来正确的代码”,但很不擅长做“证明代码正确的验证”。它能给你写出 90% 覆盖率的单测,但这些单测往往是顺着它的实现逻辑来写的,等于自己出题自己答,测不出真实的问题。
更危险的是回归测试。人手写代码的时候,改一个公共模块会下意识地想一想“谁还在用这块逻辑”。AI 不会想这个,它没有这种“调用链全局图”。它改动了一个内部工具函数,把这个函数的返回值语义稍微变了一下(比如从默认空字符串改成了默认 null),然后它自己的测试全过了。而下游对这个返回值做字符串拼接的服务,在了无人知的角落开始报错。
我统计过自己项目里 AI 引入的线上问题,差不多 70% 是这种“改了公共逻辑但没做回归验证”的类型。这不是 AI 能力问题,是它的工作模式决定的。它可以写出考满分的单元测试,但构建不出维护多年的调用关系图谱。没有图谱,它就不知道自己的改动会波及到谁。
2.4 “一切正常”幻觉与自信打分
还有一个很容易被忽略的现象:AI 在回答“这个改动安全吗”的时候,倾向于给出肯定的答案。它没有能力在自己的输出上做独立的否定性验证。
我做过一个实验。让 AI 改一个支付回调的幂等逻辑,改完我问它“存在哪些风险”,它列了三条风险,每一条的触发条件都恰恰是它自己引入的新代码路径。也就是说它知道自己写的代码可能有边界问题,但在输出的那一刻,它默认选择了“这些风险在可控范围内”。这种过度自信在复杂系统里就是隐形杀手。
人类工程师改代码的时候,会有一种基于经验的“不安感”——说不上哪里不对,但就是觉得这里应该再确认一下。这种直觉是 AI 没有的。它的所有评估都基于模式匹配,而模式的覆盖范围永远是有限的。意识到这一点的团队,才会在流程里强制加入“人类不安感检查环节”。
3. 核心风险拆解:AI 改系统到底动到了什么
3.1 接口契约与数据结构的微妙破坏
AI 改坏系统最经典的一种方式,是动接口契约。接口契约可以理解为系统模块之间的“正式合同”,合同里写了双方的权利义务:谁传什么参数、参数什么类型、允许为 null 吗、返回值什么结构、超时多少毫秒。AI 在重构的时候,特别喜欢顺手“优化”这些契约,因为它觉得这就是代码整洁的一部分。
而真实系统里,一份接口合同可能同时被八个调用方遵守着。AI 只看到了那一个被重构的调用方,看不到其他七个。我的建议是:在提示词里让 AI 对任何涉及“接口签名调整”“字段结构调整”“null 语义调整”的操作,先列出影响清单,禁止直接改代码。这条规则能挡掉一大批破坏性改动。
3.2 状态管理与并发安全的隐性风险
另一个重灾区是状态管理和并发。AI 写缓存的时候喜欢用单例全局变量,写计数器的时候喜欢用简单自增,写异步任务的时候喜欢直接整 new Thread。这些代码在单线程演示环境里跑得飞快,一上线遇到真实并发就原形毕露。
我见过一个很典型的案例:AI 给一个订单系统加了本地缓存,用的是一个 HashMap 加定时清理。单测全过,因为单测是串行的。到了线上,多线程同时读写这个 HashMap,直接死循环,CPU 打满。这类问题极其难排查,因为它不是每次必现,而是并发量达到某个阈值才出现。
给 AI 下的指令里,我建议永远带上一句:“涉及共享状态的代码,必须使用线程安全的容器并说明锁的粒度,否则重构为无状态函数。”它不是每次都能做好,但至少会往正确的方向想。
3.3 依赖升级与生态兼容的连锁反应
AI 还有一个隐蔽的危险行为:它会在修改功能时顺手升级依赖。比如你让它修一个日期格式化的 bug,它觉得是库的兼容性问题,直接把 commons-lang3 从 3.9 升到了 3.14。当前功能确实修好了,但系统里其他模块依赖旧版本行为的地方,开始在深夜静默报错。
这不是 AI 调皮,而是它基于训练数据中“升级依赖是常规解法”的经验在做合理推断。解决这个问题并不难,只需要在工程规范里加一条硬性规定——AI 不允许主动升级任何第三方依赖版本。至少在我参与的项目里,这条规定直接消灭了一整类事故。
4. 实操流程:让 AI 干活但别让它掌舵
4.1 任务拆解与提示词护栏设计
既然知道了 AI 会怎么埋雷,我们就要从流程上排雷。第一步是拆任务、设护栏。很多团队直接把整个需求丢给 AI,这是最大错误。正确做法是把需求拆成 10-30 分钟能完成的原子任务,每个任务都给足上下文和边界约束。
我给团队定的模板大致是这样:
- 任务目标:一句话说清楚要做什么,不含模糊词
- 影响范围:明确允许修改哪些文件、禁止修改哪些文件
- 接口约束:列出必须保持不变的签名、字段、返回结构
- 完成标准:列出几条可验证的客观标准,例如“全部单测通过”“回归脚本零失败”
- 深度验证要求:要求 AI 列出三个它认为最可能出问题的场景,然后针对每个场景写验证代码
这套模板跑下来,AI 的破坏率明显下降。原理也不复杂:你把系统里它看不见的那部分约束,用提示词的方式硬塞进了它的上下文。它不知道八个调用方存在,你就告诉它这八个调用方的名字和各自对契约的期望。
4.2 代码审查加设三道人工防线
AI 写完了代码,不能直接合入。我们的流程里设置了三道人工防线,一道都不能省。
第一道防线叫“契约扫描”。人肉检查 AI 改动里所有涉及函数签名、数据结构、常量定义、配置项的部分,拿 diff 和原有调用方逐一对照。这道防线不需要看懂全部逻辑,只需要盯住“接口面”,成本最低,收益最大。
第二道防线叫“逻辑核查”。抽出 AI 生成的核心算法和状态流转部分,拿需求文档对照。核心问题只有一个——这个逻辑处理了业务分支吗?AI 常常只处理主路径,对异常路径能省则省。人肉核查的重点就是这些异常路径。
第三道防线叫“破坏性思维审计”。正常审查是找“哪里不对”,这道防线反着来:假设改动上线了,大胆猜想在什么场景下系统会崩。数据量翻十倍会崩吗?下游超时会崩吗?缓存失效风暴会崩吗?不需要证明真的会崩,只需要把可能的场景列给测试团队定向验证。
4.3 回归验证的最小集设计
有人会说,全量回归测试太慢了,AI 开发速度都被拖垮了。这就需要设计一个最小回归集,用几分钟跑完,覆盖系统里最容易被动到的地方。根据我自己的经验,最小回归集通常包含四类用例:
- 所有涉及公共工具函数和公共组件的用例
- 所有核心用户路径的冒烟用例
- 所有涉及当前改动模块的上下游集成用例
- 所有在最近三个月内修复过 bug 的用例
这四条覆盖的共同逻辑是:AI 动的最多的就是公共逻辑,而公共逻辑的隐性破坏在回归里最容易被发现。我们团队实践下来,最小回归集大概占全量回归的 15%,却能拦截 60% 以上的 AI 引入型回归问题。
4.4 适配不同技术栈的实操清单
不同技术栈,AI 的埋雷点不一样,我列一张实操对照表,方便大家直接参考。这张表来自我的项目实践和同行的经验交流,以常见实践为基准,不同团队可按自身情况调整。
| 技术栈 | 高频雷区 | 防护措施 | 最小回归重点 |
|---|---|---|---|
| Java/Spring | 并发容器误用、Bean 作用域错配 | 强制线程安全容器声明,禁用 static 可变状态 | 事务与幂等场景用例 |
| Python/Flask | 全局变量污染、GIL 误解 | 限制全局状态,强制依赖注入 | 并发请求与资源释放用例 |
| 前端 React | useEffect 依赖缺失、状态更新死循环 | 要求列出 Effect 依赖树,禁止裸写 setState 在渲染层 | 路由切换与状态持久化用例 |
| 数据库 SQL | 隐式类型转换失效、索引选择性下降 | 强制 EXPLAIN 计划输出,禁止改索引结构 | 核心慢查询基线用例 |
| 嵌入式 C | 内存越界、中断上下文危险操作 | 强制静态分析报告,禁止在中断回调做耗时操作 | 中断与实时性基准用例 |
这张表不需要一次做完,但 AI 介入度越高的模块,越应该早日补上对应防护。在实际操作中,我们每引入一个 AI 协作比较频繁的仓库,就按这张表把防护补一轮,踩坑率直线下降。
5. 常见问题与排查技巧实录
5.1 性能劣化的定位思路
AI 改坏系统最常见的第一表现不是报错,而是慢。原来 200 毫秒的接口变成 800 毫秒,线上没有异常,业务还在跑,但用户已经开始不耐烦了。
性能劣化的排查路径,我个人建议按照“从外到内”的顺序。先看网关和中间件的耗时分布,确认问题在应用层;再看应用层的线程栈和慢日志,锁定耗时方法;最后看这个方法里有没有 AI 刚改过的逻辑。我这边有两次定位到的根因都是 AI 在三层循环里插入了一个远程调用,属于典型的“局部正确、整体性能灾难”。定位过程中不急着优化,先恢复原逻辑、再让 AI 带着复杂度约束重写,比直接让它继续调优要稳妥得多。
5.2 数据一致性问题怎么查
数据一致性属于细思极恐的问题类型。表面上是脏数据,实际上是 AI 在某个非原子操作里顺手改动了先后顺序。比如先更新缓存再更新数据库,变成了先更新数据库再更新缓存,某些极端时序下就会出现旧缓存覆盖新数据。
排查这种问题的正确姿势是看操作日志里的时间序列,拿数据和日志对拍。如果 AI 改写了代码顺序,日志时间线会和旧版本有明确差异。发现后修复不难,关键是不要只修数据,要把顺序约束写进提示词,让 AI 在后续任务中默认遵循数据库变更与缓存失效的原子顺序。
5.3 修复时的新坑:AI 二次破坏
这里说一个很多团队后来才意识到的问题。AI 引入的 bug 被发现了,团队把 bug 描述丢回给 AI 让它修。结果 AI 修 bug 的时候又引入新的改动,导致二次破坏。这不是偶然,而是必然——它修复时秉持的理念是“最小修改”,但由于它看不到全局,最小修改也往往只解决了表面症状,留下了深层隐患。
我的建议是:AI 修 bug 的输出,必须严格 diff 审查,只保留与 bug 点直接相关的改动,其他任何“顺手优化”一律 revert。可以设置一个“修复专用分支”,用脚本只允许 AI 触碰指定文件里的指定行段。这个措施确实粗暴,但对付 AI 的无差别修复很有效。
5.4 应急回滚与灰度发布策略
最后聊回滚策略。因为 AI 改坏系统的故事往往以“我飞快地写完了功能并飞快地引发了事故”收场,回滚能力就成了底线能力。我们团队的做法是:每一次 AI 辅助的改动都走独立的 feature 分支,分支上打至少一个稳定性验证通过的 tag,上线走灰度发布。
灰度发布的比例算法我就不展开了,直接给结论:配置管理里先放 1% 流量,观察 15 分钟;稳定后放到 10%,再观察 30 分钟;最后 100% 全量。这套策略配合前面的代码审查和最小回归集,能拦住的不是所有问题,而是大多数会让系统直接挂掉的问题。剩下的少数问题,有了回滚路径也不会造成长时间受灾。
6. AI 辅助开发的协作模式升级
聊完了术的层面,最后说说道的层面。AI 承担的功能开发占比越高,人的角色就越要从“代码生产者”转变成“系统契约守护者”。这里有一个很多人想不通的转变:以前你写代码,你天然知道每一行的意图;现在你审代码,你必须比 AI 更清楚系统为什么是现在这个样子。
这意味着你的工作重心转移到了四个方向:定义边界(哪些模块 AI 不能碰)、描述约束(哪些契约必须守住)、设计验证(怎么证明改动没有破坏原有系统)、控制演进(灰度、回滚、监控的闭环)。这四件事没有一件是写代码直接完成的,但它们决定了 AI 写出来的代码能不能安全地活在生产环境。
协作模式上,我倾向于把 AI 当作一个“快速但不靠谱的高级实习生”,而不是“不知疲倦的架构师”。给它明确任务、清晰边界、完整验收标准,它能在你的掌控下创造出不错的增量价值。让它自己立项、自己评估影响、自己决定改哪些公共层,那你收获的就不是代码,而是事故工单了。
我个人在实际操作中的体验是,AI 编程最大的悖论就在这里——它的上限取决于使用者的系统认知。系统认知强的人,AI 是放大器;系统认知弱的人,AI 是加速器——加速上线,也加速翻车。想用好它,唯一靠谱的路径,就是让自己成为更好的系统设计者和守护者。