尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Vibe Coding越改越乱?这些方法让AI生成代码可控

Vibe Coding越改越乱?这些方法让AI生成代码可控 最近一个周末我终于把一个拖了两周的页面功能做完了结果不到三个小时它又碎了。我心里很清楚问题出在哪这个功能不是我从零手写的而是从头到尾“聊”出来的——对就是现在大家口中那个 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 系统按这个流程走前后花了大概两周日均代码提交量不多但每一步都是稳的。最关键的是这个模块在重建之后后续三个月的迭代再也没有出现过“改一处坏三处”的情况。所以乱并不可怕只要思路对就能救回来。
返回列表