Vibe Coding越改越乱?这些方法让AI生成代码可控
2026/9/8 21:29:57 网站建设 项目流程

最近一个周末,我终于把一个拖了两周的页面功能做完了,结果不到三个小时,它又碎了。我心里很清楚问题出在哪:这个功能不是我从零手写的,而是从头到尾“聊”出来的——对,就是现在大家口中那个 Vibe Coding。最讽刺的是,它一开始真的很顺。第1次让它生成,跑通了;第2次微调,样式对了;第3次补逻辑,功能完整了。从第4次开始,我开始发现它在“悄悄删掉”我之前要求保留的东西。第5次我让它修一个bug,它顺手把另一个模块的配置改了。第6次,我已经不敢让它动任何不相关的东西了。到第7次,我打开文件目录,发现自己根本不认识这个项目结构了。

如果你也有过类似的经历,我想说,问题几乎从不在于“AI不够聪明”,而在于我们对“AI生成代码”这件事本身的理解出了偏差。这篇文章想聊的,就是我在项目里反复经历后总结出来的为什么,以及当成百上千行 AI 生成代码堆积在项目里时,该怎么让它不变成一团乱麻。内容偏实战,适合那些已经在用 Cursor、Copilot、Claude Code 或者类似工具做日常开发,但又被“越改越乱”困扰的人。

1. 为什么前三轮很顺,后三轮越改越坏

先说个很直观的感受:很多人在抱怨 Vibe Coding 时,其实抱怨的不是“第一次生成”,而是“迭代维护”。把 ChatGPT 或同类工具打开,丢给它一个完整需求,它会给你一个结构清爽、注释工整、运行良好的雏形——因为这是它能做到的最强场景:单次生成。

麻烦的是第4次、第5次、第6次。你开始带着“修改意见”回去,它会继续给你改。改第一版时,它可能只是改了你的目标功能。改第二版时,它开始“想办法整合现有代码”。改到第三版时,你发现它把原来对的东西也一起“优化”了,然后整个项目开始出现一些你从未写过的中间层、兼容层,以及大量重复的判断分支。

1.1 你会亲眼看到它把“正确”的东西也改了

我在项目里最常遇到的一种崩溃模式是:我只想改一个按钮的点击逻辑,它就顺带把按钮的样式类名也改了,甚至把和这个按钮没什么关系的父组件也调整了一遍。表面上每一步改动都是合理的,但组合在一起,原来测试通过的链路断了两三条。

原因是模型天然倾向于“整体重写”而不是“精确修改”。当你扔给它一个局部修改需求时,它会在底层重新推断整个函数、整个组件的结构,并按照它认为“更合理”的方式重排代码。对一次性生成来说这没问题,但对增量迭代来说就很危险。

这种破坏通常有一个潜伏期。第一次不会有任何可见问题,第三次才会爆发。所以我一直有个习惯:在前三次迭代内尽量“养”出一个信任基线,超过这个基线,我会明确告诉它“只准改哪一块,其他一律不动”,并且禁止它碰任何非目标文件。

1.2 “每次都能跑通”造成的安全感幻觉

另一个被忽略的点是:AI生成代码几乎每次都能“跑通”。这让很多人在早期产生一种错觉:它没问题。但实际上,程序能跑通和程序正确是两回事。AI生成程序常常在单元测试层面是能过,在边界情况上却一塌糊涂;这还不是最糟的,最糟的是它会在你不注意的角落,自行添加容错逻辑,掩盖掉真正的错误。

比如我调过一个文件导入功能,导入格式错了,需求方说应该报错,但 AI 会在导入时自动跳过错误行,然后告诉你导入成功。这个行为乍看起来“智能”,但开发层面它就是在悄悄改变业务规则。而且这种改动在界面上不容易发现,只有到了数据对不上账的那天你才会反应过来。这类问题也不会驱动提示词可见的报错,所以你会觉得“一切正常”,直到功能越改越乱时才开始追查。

所以,判断一个 AI 生成的功能好不好,看的不是“它跑通没有”,而是“它的行为是否在你的控制范围内”。Vibe 再好,都不能替代这个判断。

2. 上下文窗口是根源:模型并没有在维护你的架构

很多人在自己的项目里越改越乱,根本没意识到:不是AI的问题,而是它压根就“看不到”你的整个项目。很多人都以为模型能理解整个代码库,但它理解的只是你塞进对话里的那些上下文,以及它自己根据概率预测出来的“补全”。这是最核心的问题。

2.1 它看到的项目,只是你给它看的那部分

像 Claude 和 ChatGPT 这类工具,虽然有很长的上下文窗口,但并不意味着它每一次都会完整浏览你的工程。很多时候它只是看你最近贴出来的文件、函数签名和它自己生成的对话记录。它不会主动去检查另一个目录下是否已经存在相同功能的工具函数,也不会去回顾三天前你明确的“不要用某个依赖库”的那个决定。

所以当你说“帮我实现解析 Excel 的功能”时,它大概率会直接给你生成一套新的解析逻辑,哪怕项目里早就有一个现成的模块。它这样做并不是因为坏,而是因为它不知道。你在第4轮迭代里“看见”它把整个项目的结构又复制了一遍,往往就是这种信息缺失的后果。

解决这个问题,首先要改变提问方式。别再把整个项目一股脑塞进对话就完事。你需要主动告诉它:相关代码在哪个目录,哪个函数已经实现了什么,约束条件是什么,不要动哪些文件。像“请参考 src/utils/parser.ts 里已有的实现,不要在别处新增重复逻辑”,这句话比任何“高级提示词技巧”都管用。

2.2 上下文中的“假记忆”与自我确认

更隐蔽的是,模型会对它自己刚刚生成的内容产生一种路径依赖。当它生成了一段代码、你也接受了之后,它会默认这段代码是权威的。后续你再让它做别的事时,它会基于那段代码继续扩展,哪怕那段代码本身已经在演化中变得不够好。

这在流程上的表现就是:它倾向于“做加码”,而不是“重构”。因为重构意味着要重新梳理和理解原有逻辑,成本高且容易错;直接新增代码则省心得多。所以你会看到项目里出现越来越多的“兼容旧函数”的封装、越来越多的参数开关、越来越多的处理分支。整个架构就是在这些“正常扩展”中一步步腐烂的。

有一次我被一个新需求折磨了很久:功能逻辑其实完全不复杂,但是代码充斥着一堆历史遗留的开关,每个开关背后都有一份“当时这样做过的原因”,而 AI 每次都会主动加一个新的开关,而不是去清理掉旧开关。最后项目乱到我自己都不敢随便动,因为任何一次改动都像在拆炸弹。

所以说,上下文窗口的限制不只是“它能读多少字”,还包括“它会把哪些被遗忘的假设当成既定事实”。这一点你在做项目规划时就得意识到。

3. 那些看起来合理的修复,其实正在拆东墙补西墙

有一次我让 AI 修一个搜索框的偶发崩溃,它给出的修复方案是在函数入口判断一个可能为空的字段。从局部看,这个修复非常合理,很经典的空指针防御。但问题在于,那个字段为空恰恰是另一个模块的 bug,它应该在上游被修复,而不是在搜索模块里被“吞掉”。经过这次修复,搜索不再崩溃了,但上游模块的数据问题依然存在,而且已经被隐藏了,没人会再注意到,直到某个更深的逻辑被触发。

这就是典型的“修复型改动”陷阱。它会让你暂时感觉一切在变好,但整个系统的隐患却越积越深。

3.1 修复和重构是两种完全不同的思维

我需要强调一件很多人忽略的事:在向 AI 提需求时,“修 bug”和“重构代码”要绝对分开。它们是两种不同的任务,AI 对它们的处理方式也完全不同。

修 bug 时,AI 默认用小步改动,加判断、改 return、打补丁。这对局部紧急情况是好事,但对长期治理来说会留下技术债。重构时,AI 则会更大胆地重写结构,因此容易引入新的行为变化。如果你在一句话里同时说“修复 bug 并优化结构”,它就会自主决定哪些地方该修、哪些地方该优化,结果往往是结构没优化好,反而多了很多不必要的改动。

我现在的做法是,对话里只会有一种任务。要么纯修复,要么纯重构。修复时用词严格限定在“只改异常路径,不要动其他行为”;重构时则明确给出“结构上允许大改,但功能行为必须保持一致”。这两种模式一旦混着来,AI 生成代码的混乱速度会成倍增加。

3.2 没有回归测试,AI 就永远不知道自己闯了祸

另一个拆东墙补西墙的催化剂,是缺少回归测试。模型没有对旧行为的记忆,它判断“改对了没有”的唯一依据,就是你给它的反馈。如果你懒得写测试,它就会默认“只要新代码跑通了”就算完成。这样它每次修改都有可能破坏一个以前正常的功能,而你只有在用户报错或者代码评审时才能发现问题,这时候又得回去修,越修越乱。

有不少人觉得 AI 时代写测试这事可以省了——我强烈反对。恰恰相反,AI 生成代码的维护场景里,测试是唯一的锚。你不用写很复杂的集成测试,哪怕只是针对几个核心路径的冒烟测试,都足以在 AI 改坏功能时第一时间拉响警报。我有一次深度重构,就是靠一组核心单测兜底才没把项目改废。

如果你当前项目里还没有什么自动化测试,我建议先别追求什么测试覆盖率,优先把主干路径的测试补上,比如登录、权限、核心业务流程。这些测试不需要多优雅,能跑就行。当你有测试保护之后,你会明显发现 AI 乱改的概率下降了,因为它每改一步就可能跑出红点,红点就是信号,你就知道该让它收手了。

3.3 我只给部分文件读写权限的做法

可能有人会说:“测试我也会写,但 AI 还是会把不相关的文件改坏,怎么办?”我自己的做法是:收紧文件的读写权限。

像 Cursor、Claude Code 这类工具现在已经支持限制 AI 可操作的文件范围。我在做不太稳定的模块时,会明确告诉工具它只能访问某个文件夹,或者直接在工具配置里关掉对核心业务模块的写权限。这样即使它的“自主发挥”欲望再怎么强,也只能在限定范围内操作,对全局架构的影响就会被压缩到最小。

这个做法在团队协作里尤其重要。因为别人可能不了解你的项目全貌,如果每个成员都给 AI 完全自由的写权限,那项目结构变乱是必然的。而通过对文件权限做约束,至少能保证 AI 在失控时不会瞬间污染整个代码库。

4. 减慢混乱速度的实操检查清单:提示语写法与代码管理

前面讲了原理,这部分直接上干货。我把自己的使用流程里那些能明显压制“越改越乱”的提示语写法、代码管理习惯和交互方式,整理成了一份清单。不是什么玄学技巧,全是实操经验。你不需要全部用上,挑几项适合自己的坚持做,效果就会完全不同。

4.1 每次迭代前明确输入边界

这是我反复强调的一点:在把需求发给 AI 之前,要先花半分钟想清楚“哪个模块可以动、哪个模块不能动”。对话中至少包含这几个要素:

  • 目标文本:这次要实现什么,尽量一句话说清楚。
  • 边界条件:这次不要碰哪些功能,不要动哪些文件,不要改哪些接口。
  • 已有基础:相关代码在哪些文件,哪个函数已经实现了什么,参考它。
  • 约束规则:项目里有没有统一的命名规范、目录结构、依赖管理方式,要求它遵守。

我常用的开头是:“帮我在 src/features/dashboard 下增加一个导出功能。只允许修改这个文件夹里的文件,不要动 layouts 和 api 目录。导出格式参考 utils/exporter.ts 里已有的接口。不要引入新的依赖。”这种写法基本能保证它不越界。

反观那些混乱的对话,经常是“帮我加个导出功能”一句话就丢过去。AI 没有足够的约束信息,就只能自己猜,而猜的结果就是自由发挥。所以别怪它改得乱,先看看自己是不是什么都没说清楚。

4.2 拆小步,确认一次,再继续

很多人和 AI 协作时的习惯是把一连串需求连续发出去:“先做一个登录页,然后做一个注册页,再做一个用户中心。”这样做的后果是,AI 生成每一步时都在“猜上下文”,而你对中间状态的修正成本极高。

正确做法是拆小步:先让它生成登录页,你确认无误;再让它接注册页,你确认无误;然后再做用户中心。每步确认都是一次“检查点”,一旦中间出现问题,你可以立刻回滚到上一个确认点,而不是在一大堆混乱改动里找毒药。

拆小步的另一个好处是:它能让你更早发现 AI 的想法和你不在一个频道上。比如你让 AI 做“移动端适配”,它可能把整个布局都改掉,但你只要看见第一步的变化,就能马上纠正方向,而不是等它把十个页面都改完才发现完全不是你想要的样子。

4.3 让它“复述需求”再动手

有一种很有效的提示词:让它动手前先复述一遍需求。比如“在开始写代码之前,先用三点概括一下你对这个需求的理解,并说明你准备怎么改,哪些文件会受影响。”这个方法成本极低,效果却极好。

因为很多混乱都来自“理解错位”,AI 以为自己理解了对,你也以为自己表达清楚了,结果做出来南辕北辙。让它先复述,就能在动代码之前把误解消掉。它写出来的改动计划如果你觉得有问题,直接中断对话,换一种方式说,甚至重新开一个对话都比硬着头皮继续下去好。

我自己统计过,凡是让我感觉“越改越乱”的对话,大多数在早期我都没有让它先做需求复述。而用了这个习惯之后,返工率明显降低。

4.4 利用小版本管理和检查点

很多人觉得 Vibe Coding 不严谨,但真正的问题其实不是工具,而是使用工具的人完全懒得管中间过程。我的习惯是:每次让 AI 做正式改动之前,先给项目打一个 Git 标签或者分支。不用写很复杂的提交信息,哪怕只是一个“backup-before-export”这样的名字,也能让你有地方回退。

同时还有一个好习惯:每当 AI 完成一个阶段性目标并且你验证通过后,就立刻提交一个新版本提交点。这样你永远不会陷入“改了一堆东西,想回滚都不知道回滚到哪”的绝望状态。用一句话来记:Vibe Coding 可以随性,但 Git 提交不能随性。

我在实际项目里通常是每完成一个功能点就提交一次,如果当天要连续做五六个改动,我会让 AI 最开始先把当前基准代码打一个初始分支,后续每次改动都基于这个分支做增量,改完验证通过再合并到主分支。这一套下来,即便某个改动后来被认定是错的,也不会影响其他部分的稳定性。

4.5 让它为每个关键决策写注释

还有一个容易忽略的点:AI 生成代码里那些“为什么这样写”的注释,是后面维护者理解意图的关键。如果 AI 只写了“发生了什么”的注释,而没写“为什么要这样”,那这段代码在迭代到第三轮以后就跟没注释没什么区别。

我通常会让 AI 在关键判断、边界处理、非显然逻辑处写说明,例如:“这里是处理用户未登录时跳转的,之所以不放在中间件里是因为需要拿到当前路由的参数。”这种注释,能让三个月后的你一眼看懂当初的设计意图,也能让下一次 AI 迭代时少一些自作主张的机会。

有人觉得读 AI 写注释很费劲,但我想说,比起完全没有注释,至少混乱度会可控很多。你把这段注释保留在代码里,后续 AI 修改时也会更谨慎,因为它会看到那段“为什么”的约束。

5. 团队场景与 Vibe Coding:代码审查和交接需求之外的解法

有人可能会说:“我自己单干的项目,乱点忍忍也就过去了。但如果团队里每个人都用 Vibe Coding,那项目不早就炸了?”这个担心很实际。我见过不少团队,一开始每个人都在自己的分支里跟 AI 聊得飞起,等合并的时候互相看不懂对方的代码,线上出了事故也定位不到是哪次对话造成的。

所以团队场景下用 Vibe Coding,最核心的问题不是“怎么生成更好的代码”,而是“怎么让 AI 生成的代码像人写的一样可评审、可交接、可维护”。

5.1 把 AI 生成的代码当作初级工程师的代码来 review

我判断一个团队适不适合全面引入 Vibe Coding,会先看它的 Code Review 流程是否足够严格。如果你们本来 review 就随便看看,那引入 AI 生成代码一定会加速混乱。道理很简单:AI 生成代码的平均水平大概是一个不熟悉项目背景的初级工程师,可以让它产出量上去,但质量必须有你来兜底。

具体做法是:每次 AI 生成的代码合并前,必须有人去读一遍,不能在界面上看两下没问题就合进去。Review 的重点不是“代码能不能跑”,而是“有没有不必要的改动、有没有隐性规则被破坏、有没有多余依赖被添加”。如果团队成员没有时间看,那宁可让 AI 慢一点,也不要让未经评审的生成代码进入主干。

我在自己的主力项目里还汇总过一套常见的 AI 生成代码坏味道清单,review 时按清单过一遍会高效很多。清单大概是这样的:

  • 有没有新增重复逻辑,而非复用已有工具函数?
  • 有没有为了“兼容”而引入新的参数开关?
  • 有没有在核心业务流程里悄悄添加 try-catch 吞掉错误?
  • 有没有硬编码本来应该走配置的值?
  • 有没有改动无关文件或无关格式?
  • 有没有新增一个从来没被使用的依赖或组件?

这些坏味道,单次出现都不致命,但一旦累积,就是项目越改越乱的直接原因。Review 的任务就是尽量在源头切断它们。

5.2 需求层面的“业务契约”比代码更重要

团队协作时会遇到一个更麻烦的问题:每个人的对话风格、表达习惯不一样,AI 生成出来的东西也不一样。有人在提示词里会说“列表要支持翻页”,有人就会说“加个分页”。同样的功能可能被两个成员用完全不同的方式实现,最后在两个模块里形成两套迥异的代码风格。

想解决这个问题,就不能只靠代码层面,必须在需求层面给出一份共同认定的“业务契约”。比如:这个功能必须接受什么输入、产出什么输出、有多少个状态、每种状态对应的界面表现是什么。这些约定不写细,AI 就会自由发挥,而不同人的自由发挥方向还不一样。

所以我现在在团队里推行一种做法:在开始编码前,产品经理和技术负责人先用半页纸把核心业务规则写清楚,作为“一句话提示词”的锚点。后续大家和 AI 对话时,都会把这份规则粘贴进去,而不是各自靠感觉描述需求。这样一来,就算两个不同的人各写各的,生成的代码也会在业务逻辑层面保持基本一致。

5.3 共享系统提示词和代码规范

团队级的第二个抓手,是共享系统提示词。这一点很容易被忽略。很多人不知道,像 Claude Code、Cursor 这类工具是支持自定义指令或系统提示词的。你可以把项目的编码规范、目录结构约定、依赖管理规则、命名习惯、提交信息格式这些内容统一写进提示词文件里,让团队所有 AI 对话都自动带上这套规则。

我自己在团队里做的是一份类似于“项目级宪法”的提示词,里面包含了基础的技术栈约定、错误的提交方式、禁止使用的反模式、测试要求、以及常见的易错点提示。每个成员和 AI 对话时,AI 会先读这份文件,再开始干活。效果非常直观:不同人写出来的 AI 生成代码,风格差距被拉近了一大截,review 的工作量也随之下降。

当然这套做法也需要维护。每当项目里出现新的“坑”,我就把它补进提示词文件,慢慢地这份文件就成了团队的集体经验库,比任何新人文档都实打实。

5.4 交接代码时让人接手也容易

团队里面一个很容易被低估的问题是新同事接手 AI 生成代码时的成本。如果你把一段没有任何说明的 AI 代码丢给新人,他很可能完全无法下手,因为这段代码的原始意图只存在于某次对话的历史里。

我建议的解法是:在关键模块顶部让 AI 写一个“模块级文档”,讲清楚这个模块负责什么、有哪些入口、有哪些外部依赖、有哪些边界情况已经处理过。这个文档不用很长,几百字即可。但有了它,新人至少能知道从哪里开始读代码,而不会像看天书一样无奈。

更进一步,我还会在和 AI 的关键对话结束后,把对话里明确过的那些决策用几句话总结到项目文档里。比如:为什么导入功能选择在前端解析而不是后端解析,为什么用 A 库而不是 B 库。这些决策一旦遗失,下一代接手的人就很容易让 AI 重新“自由发挥”,再一次把结构搅乱。

6. 当功能已经乱成一团时:恢复秩序的三步抢救法

说了那么多预防手段,最后还是得谈谈怎么救火。如果你的项目已经处于“越改越乱、谁都不敢动”的状态,那需要做的不是继续让 AI 打补丁,而是先给这个模块做一次“有序整理”。这一步要冷静、克制,不能用 Vibe Coding 来解决 Vibe Coding 留下的问题。

6.1 第一步:冻结需求,先摸清现状

混乱项目的首要特征就是改动太多、需求变化太频繁,导致代码路径互相纠缠。这时候首先要做的不是优化,而是“冻结”。停止一切新功能开发,只允许做两件事:修严重 bug 和补充测试。

在这个阶段,带着 AI 去读代码,把每个核心模块的调用关系、数据流、状态变化都梳理出来,写一张简单的模块关系说明。注意不要让 AI 直接改代码,先让它做“阅读+解释”。这项工作的价值,是让你重新获得对项目的掌控感,知道哪一块还能动、哪一块已经脆到碰就倒。

6.2 第二步:用“功能快照”识别真正的核心路径

项目之所以乱,很多时候是因为它把所有实现混在一起,让人分不清哪些是核心路径、哪些是边缘路径。我恢复秩序的老办法是:先找出这个模块最核心的三到五个功能场景,为每个场景写一个“功能快照”,包括输入、输出、涉及的代码文件、依赖的条件。

有了快照之后,你再去看代码,就很容易发现哪些代码是核心路径上的、哪些是过去迭代残留的。比如说,很多判断分支、历史兼容逻辑、无用开关,在这些快照的对照下会变得特别扎眼。然后你可以把这些无关逻辑逐步标记出来,告诉 AI“这些代码已经没有调用了,帮我删掉”。删除死代码是让代码结构恢复清爽的最快方式之一。

6.3 第三步:分阶段小步重构,不许一步到位

彻底清理之后,才开始进入重构阶段。这里最忌讳的一件事就是“让 AI 帮你把这个模块全部重写一遍”。我之前干过这种事,结果它确实把代码写得更简洁了,但行为变化也大得吓人,一堆隐藏的边界逻辑被它们“合理简化”掉了,项目直接进入紧急修复状态。

正确的顺序是:先选择一条核心路径,把它单独抽出来重构;重构完跑测试,验证行为没变;再选下一条路径,继续。每一轮重构的范围都控制在最小单元内,改完立刻提交。这样虽然速度慢一些,但每一步都是可回退、可验证的。

等核心路径全部重构完之后,剩下的边缘路径和兼容代码就已经不多了,到时候你甚至可以大胆一些,让 AI 把这些边缘逻辑进行统一整理。到这一步,整个模块的混乱度已经大幅下降,后续再让 AI 迭代新功能,它的出错的概率也会大大降低,你也终于能重新感受到最初“聊出一个功能”的快感。

我在实际项目里抢救过一个内部 CMS 系统,按这个流程走,前后花了大概两周,日均代码提交量不多,但每一步都是稳的。最关键的是,这个模块在重建之后,后续三个月的迭代再也没有出现过“改一处坏三处”的情况。所以乱并不可怕,只要思路对,就能救回来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询