1. 当AI开始写代码,复杂度为什么反而飙升了
过去两年,我参与过几个从零起步、重度依赖AI辅助编码的项目,也接手过一些“AI参与度很高”的遗留系统。一个越来越明显的感受是:AI编程并没有像很多人想象的那样,把代码库变得更简单、更干净,反而在很多团队里,代码复杂性正在以一种隐蔽的方式悄悄失控。
这个判断听起来有点反直觉。毕竟AI最擅长的事情之一,就是帮你补全样板代码、生成重复逻辑、快速搭出能跑的原型。按道理说,它应该把程序员从繁琐劳动里解放出来,让代码更整洁才对。但实际观察下来,情况恰恰相反——AI让“写出代码”这件事变得极其廉价,而“理解代码”和“维护代码”的成本却在快速上升。
这篇文章想聊的就是这个现象背后的机制。我会从代码churn、技术债、安全漏洞这几个角度,拆解AI编程时代代码复杂性失控的真实原因,并结合我自己踩过的坑,给出一些可落地的应对思路。如果你正在用AI写代码,或者团队里已经全面引入AI辅助开发,那这篇内容应该能帮你提前避开一些雷区。
先说结论:AI不是让代码变复杂的元凶,它只是一个放大器。它把原本就存在的架构问题、规范缺失、评审缺位,以更快的速度、更大的规模暴露出来。理解这一点,是后面所有讨论的基础。
2. 代码churn飙升:AI让“改代码”变得太容易了
2.1 什么是代码churn,为什么它值得警惕
代码churn,简单说就是一段时间内被反复修改、删除、重写的代码比例。一个文件如果今天加、明天删、后天又改回来,它的churn值就很高。高churn通常意味着两件事:要么需求本身不稳定,要么开发者对代码的理解不够深,只能靠反复试错来推进。
在传统开发模式下,churn高往往是因为需求变更频繁,或者团队对某个模块的设计还没想清楚。但在AI编程场景下,我观察到一个新的churn来源:AI生成的代码,开发者自己都没完全读懂,就提交上去了。
我见过一个很典型的例子。一个同事用AI生成了一个数据处理模块,大概两百多行,逻辑看起来没问题,测试也过了。结果两周后另一个需求进来,需要在这个模块里加一个字段。这位同事打开文件,发现自己看不懂AI当时写的抽象层,于是干脆让AI重新生成了一版。新版本和旧版本结构完全不同,旧的那两百多行基本被废弃。这就是一次典型的AI驱动的高churn。
2.2 AI为什么会让churn变高
原因其实不复杂。AI生成代码的速度太快了,快到人类来不及建立对代码的“心智模型”。传统模式下,你一行一行敲出来的代码,哪怕写得慢,但你对每一行的意图是清楚的。AI模式下,你拿到的是一个“黑盒结果”,只要它能跑,你就倾向于直接采用。
这就导致一个恶性循环:
- AI快速生成代码,开发者没有深入理解
- 需求变化时,开发者不敢改,只能让AI重新生成
- 新生成的代码和旧代码风格、结构不一致
- 代码库逐渐变成多个AI“版本”的堆叠
- 后续维护者面对的是一个风格混乱、逻辑重复的代码库
我自己的经验是,AI生成的代码,如果不经过人工“消化”再提交,churn率会比手写代码高出不少。因为手写代码时,你被迫思考每一行的必要性;而AI生成时,你更容易抱着“先跑起来再说”的心态。
2.3 一个可操作的churn控制方法
针对这个问题,我后来总结了一个简单的做法:AI生成的代码,必须经过一次“人工重述”才能提交。
具体来说,就是让开发者用自己的话,把AI生成的这段代码的核心逻辑写下来,可以写在提交信息里,也可以写在代码注释里。如果写不出来,说明还没理解,那就不能提交。这个做法听起来有点笨,但实测下来非常有效。它强迫开发者从“复制粘贴”模式切换到“理解吸收”模式,churn率会明显下降。
另外,我还建议在代码评审环节加一条规则:如果一段代码是AI生成的,评审者要额外关注它的抽象层次是否和现有代码一致。很多AI生成的代码,单看没问题,但放到整个项目里就显得格格不入,这种不一致本身就是未来churn的隐患。
3. 技术债的隐形累积:AI生成的“能跑就行”陷阱
3.1 技术债在AI时代的新形态
技术债这个概念大家都不陌生,通常指为了快速交付而做出的妥协,未来需要额外成本来偿还。在AI编程时代,技术债的累积方式发生了变化。以前的技术债,往往是开发者明知有问题但没时间改;现在的技术债,很多时候是开发者根本没意识到有问题。
我把它叫做“能跑就行”陷阱。AI生成的代码,只要测试通过、功能正常,开发者就倾向于认为任务完成了。但代码的可读性、可扩展性、错误处理、边界条件,这些AI往往处理得不够好,而开发者因为没细看,也就没发现。
举个例子。AI生成的一个API接口,正常流程没问题,但异常处理只写了一个通用的catch,把所有错误都吞掉了。测试用例覆盖的是正常路径,所以测试全绿。上线后,一旦出现异常,日志里什么都看不到,排查起来极其痛苦。这种债,在AI编程之前也有,但AI让它变得更普遍,因为AI倾向于生成“看起来完整”的代码,而开发者容易信任这种完整性。
3.2 为什么AI特别容易埋下技术债
这和AI的工作方式有关。AI是根据大量代码训练出来的,它生成代码时,追求的是“统计上最可能正确”的结果,而不是“在这个具体项目里最合适”的结果。它不知道你的项目有哪些约定、哪些历史包袱、哪些未来规划。
所以AI生成的代码,常常有以下特征:
- 过度抽象:为了通用性,引入不必要的接口和层级
- 重复实现:同一个功能,在不同地方用不同方式实现
- 错误处理缺失:只处理正常路径,异常路径草草了事
- 命名随意:变量名、函数名缺乏项目一致性
- 依赖混乱:引入一些项目里本来没有的库
这些特征单独看都不致命,但累积起来,就是一笔不小的技术债。而且这笔债是“隐形”的,因为它不会立刻导致bug,只会在未来某个时刻,让维护者付出成倍的代价。
3.3 我用来控制技术债的三个习惯
第一个习惯是给AI生成代码设一个“复杂度预算”。比如,一个函数如果AI生成了超过50行,我就会要求拆分;一个模块如果引入了新的依赖,我会先评估这个依赖是否必要。这个预算不是硬性规定,但它能提醒我,AI生成的代码不能无脑接受。
第二个习惯是定期做“AI代码审计”。每隔一段时间,我会专门挑出AI参与度高的模块,重新读一遍,看看有没有明显的技术债迹象。这个审计不需要很正式,哪怕只是花半小时浏览一下,也能发现不少问题。
第三个习惯是在项目里维护一份“AI生成代码清单”。哪些文件是AI生成的,哪些是手写的,大致记录一下。这样在后续维护时,心里有数,知道哪些地方需要格外小心。这个清单不需要很精确,有个大概就行。
4. 安全漏洞的温床:AI代码里的“看起来没问题”
4.1 AI生成代码的安全盲区
安全漏洞是AI编程里最让人担心的问题之一。AI生成的代码,往往在功能层面没问题,但在安全层面存在盲区。这些盲区不是AI故意留下的,而是它训练数据里的常见模式导致的。
比如,AI生成的用户输入处理代码,经常缺少足够的校验。它可能会做一个基本的类型检查,但不会考虑注入攻击、越界访问、资源耗尽这些场景。再比如,AI生成的认证逻辑,有时候会忽略会话管理、令牌过期、权限校验这些细节。
我印象很深的一次,是AI生成的一段文件上传处理代码。功能上完全正常,能上传、能保存、能返回路径。但仔细一看,它没有对文件类型做严格校验,也没有限制文件大小,更没有考虑路径穿越的问题。如果直接上线,这就是一个明显的安全漏洞。而AI生成的时候,这些代码看起来“很完整”,很容易让人放松警惕。
4.2 为什么AI的安全意识不够
这跟AI的训练目标有关。AI被训练成“生成能通过测试的代码”,而不是“生成安全的代码”。在大多数公开代码库里,功能正确的代码远多于安全严谨的代码。AI学到的,自然是前者。
而且,安全漏洞往往需要特定的攻击场景才能触发,普通的测试用例覆盖不到。AI生成的代码,如果只经过功能测试,安全问题是很难暴露的。这就导致一个危险的局面:代码看起来没问题,测试也过了,但安全漏洞已经埋下了。
4.3 针对AI代码的安全检查清单
基于我自己的经验,我整理了一份针对AI生成代码的安全检查清单。每次AI生成涉及用户输入、文件操作、网络请求、认证授权的代码时,我都会对照这份清单过一遍:
| 检查项 | 具体内容 | 常见AI遗漏点 |
|---|---|---|
| 输入校验 | 类型、长度、格式、范围 | 只做类型检查,忽略边界 |
| 注入防护 | SQL、命令、模板注入 | 直接拼接字符串 |
| 文件操作 | 路径校验、类型限制、大小限制 | 缺少路径穿越防护 |
| 认证授权 | 令牌校验、权限检查、会话管理 | 忽略过期和权限 |
| 错误处理 | 不泄露敏感信息、记录日志 | 吞掉异常或暴露堆栈 |
| 依赖安全 | 第三方库版本、已知漏洞 | 引入过时或有漏洞的库 |
这份清单不是万能的,但它能覆盖大部分常见问题。关键是养成习惯,AI生成的代码,尤其是涉及安全边界的代码,一定要对照检查,不能因为“看起来没问题”就放过。
5. 提示词质量如何反向塑造代码结构
5.1 提示词是新的“架构决策”
很多人把AI编程提示词当成一个操作技巧,觉得只要学会几个模板就能用好AI。但我的体会是,提示词的质量,直接决定了AI生成代码的结构质量。你给AI的提示越模糊,它生成的代码就越随意;你给的提示越具体,它生成的代码就越贴近你的预期。
这其实很好理解。AI不知道你的项目背景、代码规范、架构约束,它只能从你的提示里推断。如果你只说“帮我写一个用户登录功能”,AI就会按它训练数据里最常见的模式来写。但如果你说“帮我写一个用户登录功能,项目用的是某某框架,认证走某某方案,错误处理要遵循某某规范”,AI生成的结果就会好很多。
所以,写提示词这件事,本质上是在做架构决策。你在提示词里明确的每一条约束,都会反映到生成的代码里。提示词写得越清楚,代码结构就越可控。
5.2 我常用的提示词结构
经过一段时间的摸索,我形成了一个比较固定的提示词结构,分享出来供参考:
- 背景说明:项目是什么、用什么技术栈、有哪些约定
- 任务描述:具体要做什么,输入输出是什么
- 约束条件:代码规范、错误处理、安全要求、性能要求
- 参考示例:如果有类似的现有代码,贴给AI参考
- 输出要求:文件结构、命名规范、注释要求
这个结构看起来有点繁琐,但它能显著提升AI生成代码的质量。尤其是“参考示例”这一条,非常关键。AI看到你项目里已有的代码风格,会倾向于模仿,这样生成的代码和现有代码库的一致性会好很多。
5.3 提示词里的常见坑
第一个坑是提示词太短。很多人图省事,一句话就让AI生成代码,结果出来的东西完全不能用,还得反复调整,反而更费时间。
第二个坑是提示词里没有约束。不说明技术栈、不说明规范、不说明边界条件,AI就只能靠猜。猜对了是运气,猜错了是常态。
第三个坑是一次让AI做太多事。一个提示词里塞进五六个功能点,AI生成的代码往往顾此失彼。更好的做法是拆分成多个小任务,逐个生成,逐个验证。
第四个坑是不更新提示词。项目在演进,规范在变化,但提示词还是老一套。这会导致AI生成的代码和项目现状脱节。我的做法是,把常用的提示词模板维护起来,随着项目变化定期更新。
6. 多分支并行开发下的复杂度放大效应
6.1 AI编程让分支管理变得更复杂
现在很多团队用多分支并行开发,每个分支可能对应一个功能或一个修复。在AI编程的加持下,分支的创建和合并变得更加频繁,因为AI让“写代码”变快了,分支的生命周期也变短了。
但这里有个问题:AI生成的代码,在不同分支之间的一致性很难保证。同一个功能,在A分支用AI生成了一版,在B分支又用AI生成了一版,两版代码结构可能完全不同。合并的时候,冲突会非常难处理。
我遇到过最夸张的一次,是两个分支同时修改同一个模块,各自用AI生成了实现,合并时发现两版代码几乎没有共同点,最后只能人工重写。这次经历让我意识到,AI编程时代,分支策略需要重新思考。
6.2 用git worktree配合AI编程的实践
说到多分支,顺便提一下git worktree。这个工具允许你在同一个仓库里,同时检出多个分支到不同目录,不用来回切换。在AI编程场景下,这个特性特别有用。
比如,你可以用一个worktree专门跑AI生成的实验性代码,另一个worktree保持主分支干净。AI生成的代码先在实验worktree里验证,没问题再合并回主分支。这样既能利用AI的快速生成能力,又能保持主分支的稳定性。
我自己的做法是,给每个AI辅助开发的任务开一个独立的worktree,任务完成后,把经过验证的代码合并回去,然后删掉worktree。这样每个任务的代码是隔离的,不会互相干扰,合并时的冲突也更容易处理。
6.3 分支策略的调整建议
基于这些经验,我建议在AI编程场景下,分支策略做以下调整:
- 缩短分支生命周期:AI让编码变快,分支也应该更快合并,避免长期存在
- 统一提示词模板:团队共用一套提示词模板,保证AI生成代码风格一致
- 合并前做一致性检查:合并前,检查AI生成的代码是否符合项目规范
- 小步提交:AI生成的代码,拆成小步提交,便于回溯和排查
这些调整的核心思路是:用流程的确定性,来对冲AI生成代码的不确定性。AI本身是概率性的,但我们的工程流程可以是确定性的。把两者结合起来,才能在享受AI效率的同时,控制住代码复杂性。
7. 把AI编程纳入可控工程流程的几个抓手
7.1 建立AI代码的评审标准
AI生成的代码,不能和手写代码用同一套评审标准。手写代码,评审者可以假设作者理解自己的代码;AI代码,评审者需要额外确认作者是否理解。所以我在团队里推动了一套针对AI代码的评审标准:
- 作者能否解释AI生成代码的核心逻辑
- 代码是否符合项目的架构约定
- 错误处理和边界条件是否完整
- 是否有不必要的抽象或重复
- 安全边界是否经过检查
这套标准不是要否定AI代码,而是要让AI代码经过和手写代码同等甚至更严格的审视。毕竟AI生成得快,但出了问题,代价是一样的。
7.2 用工具辅助复杂度监控
人工评审之外,工具也能帮上忙。我常用的几个手段:
- 代码复杂度分析:定期跑一下圈复杂度、认知复杂度,看看有没有异常升高的模块
- 重复代码检测:AI容易生成重复逻辑,用工具扫一遍,能发现不少
- 依赖分析:看看有没有引入不必要的依赖,或者依赖版本混乱
- churn分析:用git历史分析高churn文件,重点审查
这些工具不需要很复杂,关键是定期跑,形成习惯。复杂度失控往往是一个渐进过程,定期监控能让你在问题还小的时候发现它。
7.3 团队层面的规范建设
最后,也是最关键的,是团队层面的规范。AI编程不是一个人的事,如果团队里每个人用AI的方式都不一样,代码库很快就会变成一锅粥。所以需要有一些团队级的约定:
- 统一的提示词模板和规范
- 统一的AI代码评审流程
- 统一的复杂度监控指标
- 定期的AI代码审计
这些规范不需要很复杂,但需要团队达成共识,并且持续执行。我见过太多团队,一开始热情很高,用AI写了很多代码,但因为没有规范,半年后代码库就变得难以维护。
8. 我踩过的几个真实坑和应对心得
8.1 一次AI重构引发的连锁反应
有一次,我让AI帮我重构一个老模块。AI生成的新代码看起来更简洁、更现代,测试也过了。我挺满意,就合并了。结果一周后,另一个依赖这个模块的功能出了问题。排查发现,AI重构时改变了一个内部函数的返回值语义,虽然新模块自己测试没问题,但外部调用方没跟着改。
这个坑让我明白,AI重构不能只看被重构的模块,还要看它的调用方。后来我养成了一个习惯:AI重构后,一定要检查所有调用点,确认接口语义没有变化。如果变了,要么改调用方,要么调整重构方案。
8.2 AI生成的“聪明”代码反而更难维护
还有一次,AI生成了一段非常“聪明”的代码,用了不少高级特性,一行顶十行。当时觉得挺优雅,但三个月后,我自己都看不懂了。更麻烦的是,团队里其他人更看不懂。最后只能重写,用更笨但更清晰的方式实现。
这件事让我反思:代码的优雅,不等于代码的复杂。AI倾向于生成“技术上漂亮”的代码,但工程上,可读性和可维护性往往比技术上的优雅更重要。所以现在,我会主动要求AI生成“直白、易懂”的代码,而不是“聪明、简洁”的代码。
8.3 提示词里加一句“请解释你的实现”
这是我后来养成的一个习惯:在提示词最后加一句“请解释你的实现思路”。AI会输出一段说明,解释它为什么这样写。这段说明对我理解代码非常有帮助,也让我更容易发现潜在问题。
有时候,AI的解释会暴露它的一些假设,这些假设可能和我的预期不符。比如它可能假设输入永远是合法的,或者假设某个依赖总是可用。这些假设如果不检查,就可能变成bug。所以,让AI解释自己的实现,是一个低成本、高回报的习惯。
8.4 定期“断AI”练习
最后一个心得,可能有点反直觉:我会定期做“断AI”练习,也就是刻意不用AI,手写一些代码。这么做有两个目的:一是保持自己的编码能力不退化,二是重新体会手写代码时的那种“逐行思考”的感觉。
AI用久了,容易产生依赖,遇到问题第一反应是问AI,而不是自己思考。这种依赖短期看效率高,长期看会削弱自己的判断力。所以我会刻意留出一些任务,不用AI,自己从头写。写完之后,再和AI生成的版本对比,看看差异在哪里。这个练习对我帮助很大,推荐你也试试。
代码复杂性失控这件事,说到底不是AI的错,而是我们还没学会怎么和AI协作。AI是一个强大的工具,但工具越强大,使用它的纪律就越重要。希望这些经验能帮你少走一些弯路。