最近和不少做后端的、做前端的朋友聊下来,大家都有一个共同的感受:代码库正在变得越来越“不敢动”。模块能跑、接口能调、测试全绿,但每一次改动都会牵扯出一连串你完全没读过、也没看懂来龙去脉的代码。它们纠缠得如此巧妙,像一座用无数胶水粘起来的积木塔——表面看着没问题,往深了碰就开始哗哗往下掉渣。
这个锅,不能全甩给业务复杂,过去一年大热的 AI 编程工具“贡献”相当大。Cursor、Claude Code,以及它们身后一整代“生成式 IDE”,确实把写码效率拉满了,但与此同时,代码屎山的形成速度也被拉满了。过去一个团队可能花三年五年才能堆出一座像样的屎山,现在一个季度就能把代码库变成谁都不想碰的废墟——而且还显得特别精致,因为每一段代码单独拎出来都还算体面。
我不是来唱衰 AI 编程的,我自己就是这些工具的深度用户。但用了这么久,我发现一个很不对劲的趋势:很多人把 AI 当成了“不需要做设计的外包程序员”,而不只是一个加速输入的工具。这篇文章想把这个事情掰开揉碎讲清楚——号称要拯救我们的 Cursor 和 Claude Code,到底是怎么把代码库一步一步喂成屎山的,以及有哪些可落地的招数能挡住这个趋势。
1. 代码屎山的形成机制与 AI 为何成为加速器
1.1 什么算“屎山”?它跟“代码乱”是两回事
很多刚开始写代码的同学会把屎山理解成“缩进不规范”“变量命名太随意”“没写注释”,其实这是把脏代码说小了。真正的屎山,是结构层面的债务:模块之间循环依赖、同一份业务逻辑散落在七八个文件、一个基础类被无数个地方局部 override、没有任何测试能保证重构后行为不变化。它最可怕的特征不是“丑”,而是“改不动”——你不知道动了这一段,会在哪个角落炸出线上事故。
用生活化的说法,代码库就像一套老房子。脏乱只是表面问题,真正致命的是承重墙被砸了、水电管线乱拉、每一任房东都按自己的审美重新装修过。AI 编程工具的可怕之处,恰恰在于它在疯狂加速“换房东”的频率。以前一年换一任房东,现在一个 sprint 能换三轮,而且每轮装修都看着挺像样的,等你想住安稳点的时候,墙里全是问题。
所以在聊 Cursor 和 Claude Code 之前,我们得先对“屎山”有个共识:它不是简单的代码风格问题,而是系统在结构层面失去了可维护性。这个共识很重要,因为后面讨论 AI 工具怎么闯祸,都是围绕结构来的,而不是盯着某个缩进哭。
1.2 AI 生成代码的三个加速效应
为什么 AI 工具会加速屎山化?我总结了三个层面,这也是后面所有风险分析的总纲。
第一,生成速度快到思考跟不上。以前写一个函数,从敲键盘到运行,你的大脑其实在完成两件事:组织逻辑、评估方案是否合理。AI 补全把“组织逻辑”压缩到了几秒钟,代价就是“评估方案”这个环节被大多数人直接跳过了。代码不是想清楚才落盘的,是想都没想就已经躺在编辑器里了。速度越快,欠的思考债越多。
第二,局部优化很强,全局一致性极差。Cursor 和 Claude Code 都非常擅长根据当前打开的上下文文件生成“看起来合理”的代码,但它们很难真正理解整个系统的架构约束。于是你会发现,同一个概念今天生成一个 UserService,明天生成一个 UserManager,后天又冒出个 UserUtils,三个类各自实现一套差不多的逻辑,彼此之间还完全不知道对方的存在。
第三,坏味道会形成正反馈循环。大模型生成代码,依赖的是你代码库里现有的内容。一旦你代码里有复制粘贴的风气、吞异常的习惯、超长函数的恶趣味,AI 会非常忠实地把这种风格“继承”下来,甚至放大。垃圾进、垃圾出,这是所有数据处理系统里绕不开的铁律,大模型也一样。而且因为 AI 的平均产出质量并不低,它生成的屎山代码比人类写的还要“专业”,迷惑性极强。
2. Cursor:高效补全背后的隐性风险
2.1 爽感背后的心理陷阱:把“正确性”外包出去了
先说 Cursor。这是目前我用过的最接近“第二大脑”的编辑器,补全流畅度确实高得离谱,Tab 键一敲,一整段像模像样的代码就出来了。写样板代码、测试桩、日常工具类的时候,爽感是之前 VSCode 挂一堆插件完全没法比的。
但正因为太爽了,一个特别危险的心理陷阱就来了:你默认它生成的是“正确的”。我在项目里见过不少朋友把 Cursor 当成自动填表器,光标一停就 Tab、Tab、Tab,一个文件改完了还不知道里面到底写了什么。这里核心问题不是“AI 会不会写错”,而是“你没看怎么知道它对不对”。没经过你脑子校验的代码片段,在合入代码库的那一刻就是一颗屎山种子。
你可能会说,代码 review 会拦住的。事实上我观察到的现实是:当 AI 生成代码的体量变成常态,reviewer 根本盯不住那么多细节,最后 review 就退化成了回车确认。而且人有个毛病,对“机器生成的东西”天然会降低戒备心,仿佛自动补全的代码自带可信度,这比人写的 bug 还难防,因为人写的烂代码你会有警惕,AI 写的烂代码你反而没有。
2.2 三个真实风险场景,我全都踩过
我归纳了 Cursor 最容易导致屎山扩张的三个场景,都是实际项目里反复见过的。
第一个是“过度捏造”。你让它实现一个它只懂一半的业务需求,它特别热心,会帮你“补全”一整套流程:多出来一个你根本没要求的状态机、缓存策略、重试机制。这些附加内容在你没注意到的瞬间就进了代码库。表面上看这段代码很健壮,实际上它引入了一堆没人理解的设计决策,之后的每一任维护者都只敢加代码,不敢删逻辑。
第二个是“重复代码爆发”。Cursor 非常擅长复制你已经写过的写法,贴到新地方做点小改动。听起来没问题,但执行起来你会发现代码膨胀得惊人:同一个校验逻辑能出现在十多个文件里,每个文件里的写法还微妙地不同。需求一变,就得改十几个地方,漏一个就是事故。以前这种复制粘贴需要手动做,好歹还有个“看一眼再粘贴”的环节,Cursor 把这个环节直接干掉了。
第三个是“上下文盲目自信”。Cursor 的上下文窗口是有限的,模型只会看到你当前打开的文件和夹带的若干片段。可它生成代码的时候,表现得像是已经理解了整个项目的所有设计约束。这类代码放到全局里去,往往是在错误的地方调用了错误的东西,或者绕过现有抽象自创了一套平行宇宙实现——然后这套平行实现又成了新推广的“样板”。
2.3 Tab 补全,正在偷走程序员的“负责感”
我特别有感触的一点是:过去写代码,哪怕你是复制粘贴,总还是有个“看一眼”的动作。Cursor 之后,这个动作被极大地压缩了。我认为它真正在剥夺的是程序员最核心的东西——对自己写下的每一行代码负责的警觉心。
也不是说 Cursor 就不能用,而是得有一个清醒认知:Cursor 给你的补全,本质上是个“完美小助手”的幻觉,它不是代码评审者,更不是架构师。用它的正确姿势,是让它帮我们省掉输入成本,而不是替我们承担思考成本。省掉输入成本的时候,大脑依然在线,你知道这个代码是干嘛的;替你承担思考成本的时候,你只是手指在动,脑子和屎山已经同步上线了。工具本身没有意识,它不会惩罚你的偷懒,但代码库会。
3. Claude Code:Agent 式编程的风险形态,和 Cursor 完全不同
3.1 它是终端里的“远程兼职程序员”,不是 IDE
再来说 Claude Code。它和 Cursor 是两种完全不同的物种。Cursor 是在 IDE 里跟着你输入的补全助手,而 Claude Code 是跑在终端里的 AI Agent:你直接给它一个任务,它可以自己读取项目文件、执行命令、修改代码、跑测试,然后把结果反馈给你。换句话说,它不像助手,更像一个“远程兼职程序员”——你给它派活,它自己上手干。
这种模式上限更高,但风险也完全不同。Cursor 的风险在于“你没看清就接受了补全”,Claude Code 的风险更进一步:你根本不知道它在你看不到的地方做了什么。它完全有可能把五个文件串联着改了,有可能擅自重构你正在用的函数签名,还有可能为了把测试跑绿,在配置里塞进你根本不知道的环境变量。你最后拿到的只是一个结果,至于过程,它只会给你一个轻描淡写的总结。
3.2 三个 Agent 翻车现场,每一个都让人血压飙升
我实际使用和观察到的失败模式,基本可以归成三类。
第一类是“自作主张把改动范围扩大化”。你让它修一个小 bug,它通过检索代码,判断根源在另一个模块,于是把那个模块的逻辑也顺手改了。最后 diff 一展开,十个文件八百行改动。它还会理直气壮地告诉你“这是必要的重构”。问题是,没人知道它是怎么推理出这个必要性的,这类改动也极难 review——十个文件牵在一起,你敢随便回滚吗?
第二类是“报错循环里的假修复”。Claude Code 在执行测试时失败了,它会进入自我修复循环:改一行、跑一次、报错、再改一行。问题在于,它可能并不是在修复问题的根源,而是在用各种看起来无害的小补丁绕过报错。我见过它为了应付 null 指针,在调用处疯狂加判空;为了通过边界测试,直接写死特判。最终测试绿了,坑也埋得更深了。这种“假绿”比“真红”还要麻烦,因为它骗过了 CI,也骗过了团队里所有人的警觉。
第三类是“上下文蔓延带来的概率性失控”。Claude Code 的上下文量确实很大,但再大也没法穷尽一个中型项目的全部细节。在长任务里它会逐渐遗忘早前你明确否定的方案,或者把已经推翻的设计重新捡回来。如果你的指令不够收敛,它的行为会越来越像一只精力充沛但方向不明的仓鼠,跑个不停,代码库却被改得越来越不像样。
3.3 上下文窗口再长,也替代不了项目的隐性记忆
这里我要泼盆冷水。很多人觉得 AI 上下文窗口越来越长,以后是不是可以完全放手让 Agent 管整个代码库了?我的答案很明确:不行。原因不是技术参数不够,而是“全局一致性”从来不只是信息量的问题。
一个代码库真正的记忆,除了每个文件的内容,还包括那些根本没写进代码里的决策:为什么这里不做缓存、为什么这个接口用这个命名而不是那个、为什么这段逻辑死活不能拆到公共模块。这些隐性知识不在一行行的代码里,它们散落在大家的脑子和历史讨论中。AI 工具能在信息层面帮你“记得”,但它很难在理解层面替你“判断”。一旦它开始全权负责,你丢失的不是文件内容,而是决策依据——而屎山最深的根,就是从决策依据丢失开始的。
Cursorm和 Claude Code 的差别也可以简单看这张表:
| 项目 | Cursor | Claude Code |
|---|---|---|
| 交互形态 | IDE 内补全 + 对话 | 终端命令行 Agent |
| 工作半径 | 当前文件 + 可控上下文 | 可执行命令、修改任意文件 |
| 核心风险 | 不假思索地接受补全 | 自主改动超出预期范围 |
| 治理难度 | 相对可控,盯 diff 即可 | 需要更强的沙箱和审批机制 |
| 典型失控现场 | 重复代码爆发 | 跨文件传导式重构 |
4. 沉默的吞噬:AI 如何反向重构你的架构
4.1 功能边界是被“最小改动”一点点啃掉的
说完了两种工具的各自风险,我想往深一层挖:为什么 AI 参与越多,架构越容易被啃坏?关键在于 AI 的一个底层习惯——永远选择“最小改动路径”。
表面上这很合理,改得少,风险小。但“最小改动”和“正确改动”经常是矛盾的。比如一个支付抽象层本来很干净,AI 拿到一个新需求,它会倾向于在现有函数里继续加代码,而不是去抽象重构。这不是因为它笨,而是因为它被训练成输出“最符合当前上下文延续性”的结果,而“重构”恰恰意味着打破上下文延续性。
于是几轮迭代之后,你那个原本干净的支付抽象层,会变成掺杂数据库查询、外部 API 调用、日志上报和促销判断的怪物。每一处改动单看都可以理解,但结合起来就是结构灾难。更可怕的是,这个过程是渐进发生的,每个版本都只比上一版丑一点点,团队很难察觉,等到发现时就已经病入膏肓了。
4.2 屎山会传染:从文件级混乱走向模块级混乱
代码屎山和传染病很像,有很强的传染性。早期可能只是某个新文件比较乱。但如果 AI 一直在那个乱文件附近迭代,乱象会以它为中心向外辐射,后续生成的代码会越来越倾向于迁就这个乱文件,而不是清理它。很快,整个模块都变成“历史包袱”状态,所有新代码都在给它打补丁。
到了这个阶段,测试依然可能是绿的,功能依然正常,但团队里每个人都知道:这个模块不能再碰了。于是新需求只能绕过它,在别处另起炉灶,再造一个平行的模块,然后重复同样的过程。一两个季度之后,代码库里可能就多出十几个“重影模块”,每个都承担着差不多的职责,但各有各的写法。到这一步,再想治理已经不是重构一两段代码了,而是要做一次架构级手术——成本高到大部分团队会选择继续苟着。
4.3 五个预警信号:你的代码库正在被“吞掉”
我这里整理了五个早期信号,你可以拿自己项目对照一下:
- git 提交里,AI 生成的批量改动越来越多,diff 动辄几十个文件。
- 同一个业务概念,类名和函数名开始五花八门,找不到一个明确的“权威实现”。
- 模块间依赖关系极其难画,几乎不存在清晰的单向依赖。
- 改一个简单需求,总需要连带改动好几个无关文件。
- review 时越来越难看懂别人的 PR,因为代码风格统一了,设计意图却一团模糊。
这些信号单独出现任何一条,都不一定说明有问题;但如果同时出现三条以上,你大概率就已经处于“AI 高速帮团队堆屎”的进行时了。早发现早治理,拖到后面,成本是指数上升的。
5. 危机责任链:工具、流程还是人?
5.1 工具只是放大器,不是屎山的源头
写到这里得说句公道话了:屎山的锅,不能全让 Cursor 和 Claude Code 来背。工具本身是放大器。你本来就有屎山,AI 只是让它长得更快;你本来架构清晰,AI 大概率也会沿着相对干净的路径走。真正决定代码库走向的,还是团队怎么用这些工具。
这也不是给 AI 工具洗白,它们确实引入了新的失效模式——过度自信、上下文遗忘、最小改动强迫症,这些都是传统工具没有的问题。但如果流程控制到位,AI 就是一个强大的效率工具;流程形同虚设的情况下指望 AI 自动管理好架构,那它就会变成一台马力十足的屎山挖掘机。同样的工具,怎么用,决定了它是帮手还是帮凶。
5.2 新手和老鸟用 AI,结果天差地别
我观察到一个特别有意思的现象:同样用 Cursor 和 Claude Code,新手和老手交出来的代码库质量完全是两个物种。新手更容易把 AI 当成“答案生成器”,遇到需求直接甩给 AI,拿到结果就提交;老手则更习惯把 AI 当“编码加速器”,先自己把设计方案、接口边界、改动范围想明白,再让 AI 去处理“写代码”这一步。
差别就在这一步认知:屎山不是编码速度导致的,而是决策速度导致的。新手把决策权外包给了 AI,AI 虽然快,但它的决策没有经过架构语境的校验;老手保留决策权,只是把打字和查 API 的时间省了下来。所以说 AI 编程最大的危机,其实是把它当成了“不需要做设计的外包程序员”——跟用哪个工具关系不大,主要看你的脑子在不在决策链路上。
5.3 用“可逆性”当价值判断的新准则
那要怎么判断一次 AI 参与改动到底是好是坏呢?我自己这几年下来慢慢形成了一个朴素的判据:看这个改动是否“可逆”。
如果一次重构之后,你可以很轻松地撤销、回滚,有充分的测试验证行为没变,那这次改动就是健康的。如果改动让代码变得更难测试、更难局部理解、更难单独回滚,那不管表面看起来多智能,你都在往屎山添砖。把“不可逆风险”控制住,AI 的很多毛病就不会致命;控制不住,一次“聪明”的重构可能就是一个月的噩梦。这套判据也从侧面说明,测试覆盖不是可有可无的环节,它就是你给 AI 折腾设下的安全绳。
6. 从屎山里救场:我目前实行的 AI 编程管控清单
6.1 先画一个圈,再在圈里随便飞
说了一堆风险和原理,最后落回到可操作的东西上。我现在管理 AI 编程工具的做法,核心就一句话:先画一个圈,然后让你在圈里随便飞。
具体怎么落地?第一,明确规定 AI 可以改什么范围:小的 bug 修复、样板代码、测试用例,可以放心交给 AI;核心架构、跨模块重构、数据库变更,必须人来主笔,AI 只能当草稿工具。第二,所有 AI 生成的较多文件改动,一律走分支提交,不允许直接推到主分支,确保每批改动都能在独立环境里验证。第三,给 AI 立军规:不擅自改函数签名、不擅自加依赖、不擅自重构无关代码。这些规矩写进团队规范,最好直接写进工具的规则文件,让 AI 从第一行代码开始就带着镣铐跳舞,比事后指责高效得多。
6.2 代码审查和测试,在 AI 时代是保命符
代码审查在 AI 时代不是变轻松了,而是变得更关键。我强烈建议团队把 review 的门槛调高:AI 产生的 diff,必须有人能够逐行看懂。看不懂当场就问,问不清楚就要求重写。千万别因为“CI 过了”“测试绿了”就放心合入——那等于给屎山开了最高权限。
同时,测试覆盖是 AI 生成的最后一道栅栏。尤其是 Agent 做大范围改动的时候,必须配套清晰的单元测试和集成测试。没有测试的地方,AI 发疯你根本不知道。我会要求 AI 重构完某个模块后,先把测试补齐再谈提交。测试不只是防回归,它同时是给 AI 一个客观的行为反馈环,让它能区分“我改对了”和“我改错了”。
给你一条我在实践中被毒打多次后总结的规则:AI 生成的代码,必须满足三个条件才允许合入——有人看得懂、可以回滚、有测试保护。三个条件缺一个,宁可返工也不要将就。
6.3 几个实战细节:规则文件、生成后检查和定期清理
最后分享几个直接能上手的实战细节。很多人问 Cursor 怎么设置中文界面、Claude Code 怎么安装配置,这些基础问题网上一搜就有,我更想讲点“防屎山”相关的配置习惯。
第一,善用工具的“规则文件”和“记忆机制”。Cursor 支持项目级规则,Claude Code 也有自己的系统提示配置,把团队的编码规范、禁止事项、默认风格写进去,比如“所有新增逻辑必须优先放入已有 service 层”“禁止在 controller 里写业务判断”“命名必须遵循现有英语名称体系”。这能显著减少 AI 生成的“自由发挥”,把它的创作欲收敛到合理范围。
第二,“生成后必做三件事”。AI 生成完代码后,不管看起来多合理,统一要求:先读一遍改动、再跑一遍相关测试、最后手写两行注释说明设计意图。这三件事做完,大部分屎山种子在被提交之前就被掐死在编辑器里了。听起来简单,但执行到位比任何高级功能都管用。
第三,定期反向清理。我会每周挑一个 AI 改动最频繁的模块,主动做一轮“反向清理”——删冗余代码、合并重复逻辑、修正命名。花不了多少时间,但能抵消 AI 高速堆杂物带来的熵增。这个习惯我坚持了大半年,效果很明显:AI 仍然是团队里的主要写码工,但代码库的混乱程度没有跟着指数上涨。说明“圈养式使用”是完全可行的。
我个人现在越来越确信一件事:Cursor 和 Claude Code 确实是划时代的工具,但它们替代不了你的判断力。工具一定会越变越强,屎山危机的解法却始终在人这边——上线之前多想一步,审查时多问一句,提交前少偷一次懒,这些最终都会在代码库里兑现。别让 AI 替你写代码,变成真的替你做决策,那是目前为止我见过最贵的屎山门票。