半年前,我接手了一个AI代码比例高得吓人的项目。功能上线速度是真的快,可三个月后,团队里没人敢碰那些代码——改一行可能带崩一片,查问题像是在考古。这个场景我见过不止一次:AI辅助编程让产出暴增,技术债也在以同样的速度悄悄堆积。今天这篇分享不打算讲什么宏大理论,就想踏踏实实聊聊:AI生成代码为什么会变成明天的技术债、怎么提前发现这些债,以及我实际在团队里跑过的一套治理流程。说的都是能直接拿去用的做法,不是概念。无论你是正在让AI帮你写业务的开发者,还是要对代码长期质量负责的技术负责人,都可以对照着调整自己的节奏。
1. 先从一段“白天提速、晚上救火”的日子说起
1.1 一个“两天完成”的模块
某小组接了一个内部工具平台的需求:把三个数据源合并成统一报表。同事A用两天时间让AI完成了其中一个核心模块,代码风格规范,注释齐全,甚至附了测试。而另一个模块按传统方式排期要一周。当时所有人都觉得,AI这条路走对了。
两周后需求调整,状态机需要增加一个流转分支。维护者B接手时发现,状态机的实现散落在三个文件里,每个文件是不同轮次的AI对话生成的,逻辑相似但行为有细微差异。他改了一处,漏了另一处,上线后数据直接错乱。那一周连续做了三次热修复,每次都在重复同一个故事:开头很快,修起来很慢。
真正让我意识到问题的,是新同事C的提问:为什么这里要设置两秒重试?为什么这个异常被吞掉了?没人能完整回答。提交记录里没有设计说明,原始提示词也没有被留存。核心问题随之出现:这个模块变成了“无主代码”——它存在、能跑,但没有人能真正解释它。
1.2 为什么“代码能跑”和“代码可维护”是两回事
这就像一个人办了张信用卡:表面看起来没有欠款,其实一直在透支明天的注意力。技术债最典型的路径,恰恰是越快得到一段代码,就越容易忘记去理解它。AI生成的代码样式往往不难看,甚至命名规范、注释完整,但它依然会成为债务,因为生成时的决策过程没有被记录,维护者无法判断它能承受怎样的改动。
按业界常见的说法,技术债不是“代码写得丑”那么简单,而是“当前选择对未来的一种透支”。人写的烂代码一眼就能被识别,反而不容易长期存留;AI生成的代码看起来太正常了,团队会默认它没有问题,于是债务就藏进了正常的表象里。
其实AI代码完全可以成为正向资产,前提是把它当作“可读的草稿”而不是“可上线的成品”:让AI搭骨架、写重复逻辑、补文档,人负责理解、决策、测试和长期维护。价值会保留,债不会积压。
2. 表面光鲜的AI生成代码,问题藏在四个角落
别急着让AI背锅。AI写出来的代码不一定丑,很多问题恰恰出在它太“正常”了。我观察过大量生成代码,最常见的隐患基本集中在四个角落。
2.1 复读机式冗余
AI没有仓库全局记忆,它基于当前对话的上下文生成代码。于是每接一个新任务,它就很可能把已有逻辑重新写一遍。我在那个项目里见过页面三个文件出现几乎一样的格式化函数,分别由三次不同对话生成,逻辑相近但参数处理略有差异。当时没人觉得有问题,可后来格式规则一变,就需要同时改三处,漏改一处线上数据就错乱。
这类问题用肉眼很难察觉,因为函数名可能完全不一样,但静态扫描会看到很高的相似度。为什么不直接复用?因为你在提示词里没有给出已有函数的签名,AI只能按自己的理解重新发明。重复代码意味着同一业务规则维护了多份副本,任何修改都要同步到所有副本,少一处就是一个隐藏bug。
2.2 自信的幻觉
AI语言模型在生成代码时会有幻觉:在不存在的库上构造调用、编造可选参数、或者使用旧版本的接口写法。最典型的场景是消息队列封装——AI“记得”某个方法在库里存在,实际上当前版本已经改名了。IDE没报错,开发自测也没走到那条路径,生产环境在特殊流量下才触发了异常。
这种问题的可怕之处在于,AI是“自信”地写出这些调用的,它不会告诉你自己不确定。于是在静态检查、编译、开发自测全部通过的情况下,问题被推迟到运行时才暴露。修复成本往往很高,因为你需要先找到那个“看起来存在但实际不存在”的调用点。配合依赖文档、编译期扫描、以及覆盖到真实场景的测试,才能兜住这一类幻觉。
2.3 目光短浅的上下文耦合
AI看不到仓库全貌,只能看到你贴在提示词里的局部代码。为了“完成任务”,它会绕过现有设计直接访问别的模块。举个例子:某个订单状态模块为了标记已处理,AI直接在支付通知代码里改了状态字段,而不是走状态机接口。当时看起来很省事,可后来状态流转增加了新规则,这处直改就成了绕过校验的后门。
背后的问题是职责混乱。表面上看代码在一个模块里,实际上两个模块都在控制同一状态,改动任何一处都可能影响另一处。发生线上问题时,排查的人根本不知道该看哪个模块。这种债需要靠架构约束来防,人在生成之前就要给AI划清楚边界。
2.4 平时不响的防御边界
AI默认按照快乐路径输出。你不让它考虑失败场景,它就不会写异常处理、重试、降级。比如一个导出Excel的功能,在文件目录正常时可顺利跑通,但只要目录变成只读,它就直接抛异常;网络抖动时,远程调用只做一次,没有重试,用户界面转圈到超时。
这类问题的特点是:正常路径上系统很快,故障路径上完全裸奔。测试环境很难覆盖到这些场景,于是它们全部积压到生产环境。要解决,需要在评审时强制追问失败路径,也需要在提示词里明确要求AI补充异常分支。
2.5 为什么“表面质量”反而会骗过评审
最麻烦的地方在于,这些代码的表面质量通常很高。AI生成的命名、格式、注释都很统一,评审者很容易进入“代码这么干净,应该没问题”的状态。人写的糟糕代码反而会唤起警惕,AI写的副本和幻觉却会被顺手放行。
所以治理AI代码的第一课,是把“赏心悦目”和“可靠可用”分开。评审不能只盯着缩进、命名、格式,这些AI自己就能做好;要盯的是意图、边界、失败路径和测试证据。
3. 治债第一步:把模糊的“不放心”变成可量化的信号
不能等上线后才发现债。我在团队里做的第一件事是建立一套可见的预警清单,让AI代码在合入之前就暴露风险。
| 信号编号 | 信号名称 | 检测方法 | 危险级别 |
|---|---|---|---|
| 1 | 相似代码块重复率偏高 | 静态扫描工具对代码块做相似度聚类 | 黄 |
| 2 | 核心模块分支覆盖率不足 | 覆盖率报告检查条件覆盖情况 | 黄 |
| 3 | IO/外部调用附近缺少异常处理 | 规则扫描统计调用点与错误捕获数量 | 黄 |
| 4 | 魔法数字、硬编码配置散落 | 扫描字面量常量,要求抽成配置 | 黄 |
| 5 | 新引入来源不明的依赖 | 依赖锁定文件和变更diff核对 | 红 |
| 6 | 跨模块直接改动或访问全局变量 | 静态分析检查引用范围 | 红 |
| 7 | 同一业务规则出现多个实现 | 代码评审配合语义相似度工具辅助 | 黄 |
| 8 | 注释描述与实际行为不一致 | 抽查评审,对比注释与运行逻辑 | 黄 |
| 9 | 使用了废弃或可疑API | 编译警告与IDE废弃标记检查 | 黄 |
| 10 | 出现明文密钥或凭据 | 正则扫描常见密钥模式 | 红 |
这套清单落地时,我做了三件事。第一,直接做成MR模板里的checklist,每个AI生成片段提交前必须逐项勾选;第二,把能自动化的检查挂进流水线,扫描结果自动标到MR上面;第三,约定触发条件:单次提交命中3个黄灯或1个红灯,直接打回。
有人问,信号这么多,会不会太复杂。我的观点是,技术债最危险的地方恰恰在于不可见。你只有把模糊的“不放心”变成具体数字,才能在合入那一刻拦住它,而不是在半年后让某位同事半夜上线救火。另外我还会统计打回率:如果一个月内AI相关提交的打回率超过20%,说明要求还不明确,或者提示词写得太随意。这时候应该停下来调整约束,而不是继续快速产出代码。
4. 治债第二步:四道关卡把AI生成物挡在主干道之外
有了预警信号,还要有流程。只靠扫描和清单,容易被当成额外负担;真正起作用的,是让AI生成物在合入主分支之前经过四道关卡。
4.1 需求关:先交方案,再交代码
大多数团队让AI直接写代码,这是流程的第一个缺口。AI不是决策者,它只是推断你想要的输出。如果方向本身就错了,生成的代码越完整,返工成本越高。
我现在的习惯是,在生成前用一个固定提示词逼它先输出实现方案,把涉及模块、调用关系、接口改动、失败路径和风险点列清楚。确认方案没问题,再让它写具体代码。这一步看起来多花几分钟,实际能砍掉一半因为方向不对而产生的返工。提示词大概长这样:
请先不要写代码,只输出实现方案: 1. 涉及的模块和调用关系 2. 需要新增或修改的接口 3. 失败路径和异常处理计划 4. 这个方案可能存在的3个风险点 等我确认后再生成具体实现。4.2 架构关:给AI画清楚边界
AI看不到仓库全貌,所以人必须把边界显式告诉它。我在仓库里放了一个提示词约定文件,类似给所有协作者的开发规范,但针对AI生成场景做了更强约束:禁止访问全局状态、禁止直接改写别的模块内部字段、依赖只能从上层指向下层、新增公共函数必须先说明职责。
每次提交AI代码,提示词必须引用相关接口签名,而不是让AI自己猜。这一步容易被忽略,因为很多开发者觉得“把需求描述清楚就够了”。实际上,AI为了完成目标会想办法绕过设计,直接使用全局单例或访问外部模块内部字段。所以架构约束必须配合评审,否则形同虚设。
4.3 评审关:带着生成背景去审代码
AI代码评审最容易被忽略的是意图。只看代码,看不出它背后依据了什么假设、做了什么取舍。于是我在提交模板里加了“生成记录”字段:原始提示词、AI的完整回答、人工修改了哪些地方。
评审者看到的就不只是代码本身,而是这段代码为什么长成这样。审通过的标准也从“代码能跑”变成“我们理解并接受这段代码的决策”。这一步的关键是把决策权留在人手里,AI只是执行者。如果你发现一段AI代码的生成记录缺失,最稳妥的做法是先打回补齐,不要替它脑补理由。
4.4 运行关:没有测试证明的AI代码不允许合入
最后一道关卡最难推行,也最值钱。无论提示词写得有多好,AI代码必须有运行证据才能合入:一个能验证它的自动化测试,最好再补一两个故障场景的测试,比如断网、目录只读、参数非法。
我见过团队跳过这一步,理由是“功能这么简单,不用写测试”。结果两周后那个模块果然出了问题,没人能确定自己改崩了什么。把测试当成买保险而不是额外成本,这可能是AI时代最值得守住的一条底线。
5. 治债第三步:让存量债在流水线上自动还
四道关卡管住了新的生成物,但存量债还在。在接手项目并跑完治理流程之后,还需要让债有出清机制。核心思路是:把质量门槛变成流水线上的自动门禁,而不是依赖某个人的自觉。
5.1 把质量门槛写进流水线
我给CI流水线加了一套门槛规则,示意如下:
pipeline: - lint: warning 以上不允许合入 - duplication: 相似代码块占比不超过 15% - coverage: 核心模块分支覆盖率不低于 80% - dependency: 新增依赖必须经过人工确认 - secret_scan: 出现明文密钥直接终止这些门槛一开始肯定会挡下很多代码,团队也会反弹。所以不要一次拉太高,先挑最顽固的一条规则跑两周,让团队适应,再加下一条。目标不是零债务,而是债务不能无意识增长。跑了一段时间后,重复率和覆盖率的数字会主动说话,比任何人的口头催促都有用。
5.2 用AI反治AI债
AI留下的问题,也可以让AI来帮还,但必须小心。我试过两种行之有效的方式。第一种是让AI做“阐释者”:把一段没人理解的历史AI代码喂给另一轮对话,要求它解释职责、依赖和风险点,然后让维护者复核。理解债是技术债里最贵的部分,这个方法能大幅降低消化成本。
第二种是让AI生成候选重构。把重复代码聚类结果交给AI,让它生成收敛到公共函数的候选补丁。但AI会做重命名、调整副作用,所以必须做行为等价验证,比如录制对比测试或黄金文件,确认前后行为完全一致。
注意:用AI重构AI代码,不等于信任第二份AI输出。AI给的东西永远是候选方案,不是标准答案,人工审阅和测试验证一步都不能少。
5.3 存量债分级处理
不是所有债都值得立刻还,我把存量债按“使用频率”和“风险程度”分成了四档,处理策略完全不同。
| 使用频率 | 风险程度 | 处理策略 |
|---|---|---|
| 高频 | 高 | 优先重构,先补测试再动刀 |
| 高频 | 低 | 收敛入口,统一封装后再替换 |
| 低频 | 高 | 加监控、加隔离层,暂不动结构 |
| 低频 | 低 | 冻结并留文档,标记为技术债 |
不要指望一次清理掉所有债,那既不现实也不必要。治理的目标是把未知的债变成已知的债,把危险的债变成可控的债。
6. 一个季度的数据:提速和质量到底能不能兼得
这套流程跑了一个季度后,我整理了一份真实度量数据,以自己管理的一个业务后端小组为样本。先说结论:速度没有掉下来,质量反而更好了,但过程不是一帆风顺。
| 指标 | 治理前 | 治理后 |
|---|---|---|
| 周均有效交付版本 | 11 | 15 |
| 核心模块代码重复率 | 32% | 14% |
| 核心模块分支覆盖率 | 48% | 76% |
| 变更回退率 | 12% | 6% |
| 生产环境故障平均修复时间 | 4.5小时 | 2.2小时 |
| 新人独立修改核心模块所需时间 | 约3周 | 约10天 |
前两周是最难受的:多了方案确认、生成记录、测试补齐,团队明显感觉变慢了,甚至有人说“不如直接手写”。但从第七周开始,返工少了、线上故障少了、累积效率反而超过了之前。所以如果准备推行这套流程,一定要做好“先慢后快”的心理准备。
我还观察过一个对照组:同时期另一个小组不设门槛,纯粹追求AI生成速度。半年后他们好几个模块没人敢改,最后只能停线重写其中一个模块。这个对比让我认定,AI本质上是放大器——如果流程是健康的,它会放大效率;如果流程本来就失控,它只会放大债务。
7. 我的最终体会:AI是放大器,不是背锅侠
说了这么多流程,最后聊点更个人向的东西。
7.1 哪些地方别直接让AI写
我给自己划了一条线:核心资金计算、权限校验、涉及分布式一致性和不可逆操作的代码,不管提示词写得多好,都不直接采用AI的第一版输出。可以让AI做代码评审、做逻辑解释,但主链路上的关键逻辑必须由人亲手写过并且经过测试。原因很简单,这些逻辑一旦出错,代价不是重构能挽回的,而是线上事故。
其他场景则不必因噎废食。脚手架、CRUD、格式转换、常规增删改查,让AI写完全没问题,只要按前面说的四道关卡走。
7.2 把AI当结对程序员,而不是外包程序员
外包程序员的特征是:你给需求,他交代码,你验收。结对程序员的特征是:你们讨论方案、互相挑战、一起对代码负责。如果只把需求丢给AI,它就是一个交代码特别快的外包;如果你逼它先讲方案、列出风险、解释取舍,它就是能让效率翻倍的搭档。
一个我每次都在用的小技巧:让AI生成了超过20行的代码之后,紧接着问它一句“这段代码可能有哪三个潜在的风险点”。它列出的清单通常不是废话,AI对自己生成代码中最有风险的分支其实是有感知的。你再把它列出来的风险点写进MR描述里,评审效率会明显提升。
7.3 无主代码才是最贵的债务
再回头看开头的那个模块,真正让它变成技术债的,不是用了AI,而是它成了一堆没人能解释的代码。所以我对团队的硬性要求是:AI生成的每一段代码,都必须有一个明确的owner。owner要能讲清楚这段代码为什么存在、边界在哪里、怎么测试它。做到这一点,这段代码就从“AI产出”变成了“团队资产”。
我到现在仍然每天用AI写代码,而且写得越来越多。变化只是:它负责快,我负责懂;它负责产出,我负责把关。这句话是我实践下来最准确的总结,也是我想推荐给所有人的状态。