Vibe Coding 实战:大模型协作与全局 md 文档指南
Vibe Coding这个词我第一次刷到的时候心里第一反应是这不就是甩手掌柜式写代码吗。后来真的拿它做了一个内部小工具从零到能跑起来大概两周我改口了——它压根不是偷懒而是把开发者的注意力从一个地方整体搬到了另一个地方。简单说Vibe Coding是一种以自然语言为主要输入方式、由大模型负责产出代码、开发者以验收结果而非逐行审阅为主的开发模式。它的关键词是氛围和感觉你说清楚想要什么、大概长什么样、边界在哪剩下的交给模型去填。它解决的核心问题不是写得更快而是把想法变成可运行东西的门槛被压得极低。适合谁适合已经有一个明确小目标、能自己跑起来验证结果、并且愿意为最终质量兜底的人不适合完全没有工程常识、指望一句话变出生产级系统的人。这篇就把我这两周的真实操作、踩的坑、以及那套被反复提到的全局 md 文档到底怎么写一次讲透。1. 先把话说清楚Vibe Coding 到底是什么又不是什么1.1 一句话定义与三个可观察的特征如果非要压成一句话Vibe Coding 是一种描述意图—生成实现—观察结果—继续描述的闭环开发者主要工作在闭环的两端描述和验收而不是中间那段敲键盘的过程。它有三个特别明显的可观察特征你可以拿它对照自己是不是在这么干。第一个特征是不看 diff 也能推进。传统开发里你 review 每一行改动是默认动作Vibe Coding 里你更多是跑一遍、看报错、看输出、看界面用结果说话。第二个特征是提示词会不断迭代你可能会为同一个功能改五六遍描述直到模型给出的东西接近你要的形态。第三个特征是回滚是常态你不太在乎某一次尝试失败因为重来的成本很低。这三个特征合在一起就带来一个很关键的推论Vibe Coding 的成本结构被重新分配了。写代码的时间大幅下降但定义清楚自己要什么和验证结果对不对的时间大幅上升。很多人第一次尝试觉得不好用就是因为只看到了前半句没准备好承担后半句。我自己的体感是一个中等复杂度的小功能过去我可能花两小时写、二十分钟测现在我可能花四十分钟反复描述和调整、一小时验证和修边角。总时间差不多但过程的心理负担小很多——因为不需要一直保持那种每一行都得想清楚的高度专注状态。1.2 它和用 AI 补全代码根本不是一回事这一点特别容易被混为一谈。代码补全包括各种编辑器里的行级、块级建议本质是加速你原有的工作流你还在主导结构模型帮你省键盘。而 Vibe Coding 是替换了主导权结构由模型提你负责判断和纠偏。差别体现在几个具体的地方。补全模式下你会下意识保持代码风格一致因为它只是接你的下文Vibe Coding 模式下模型会按它自己的偏好组织代码如果你不提前约束一个文件里可能同时出现三种命名风格。补全模式下你对每一处改动有天然的这是我写的的责任感Vibe Coding 下代码是它写的责任感的建立需要额外机制比如测试、比如强制自己过一遍关键路径。还有一个隐蔽的差别在错误处理方式上。补全时如果模型给的建议不对你按一下 Esc 就完事了成本接近零Vibe Coding 时如果它生成的方向错了你可能要花十几分钟才意识到方向错在哪。所以 Vibe Coding 特别吃前期把话说清楚这个能力这一点的权重被放大了好几倍。我见过不少人抱怨AI 写的代码一塌糊涂追根究底问题往往不在模型而在于他们把 Vibe Coding 当成了代码补全来用——只给一句模糊的需求然后期待一个完整可用的结果。这个期待本身就是错配的。1.3 三个最容易掉进去的认知误区误区一以为不用懂代码了。恰恰相反Vibe Coding 对能不能一眼看出这份代码有病的要求更高。你不需要逐行写但你需要能在几秒钟内判断这个函数是不是在做三件事、这个循环是不是可能死、这个接口是不是没做校验。看不懂就验收不了验收不了就只能在坑里越陷越深。误区二以为生成的代码可以直接上生产。我做的第一个功能就是踩了这个坑。当时生成的东西跑起来完全正常我就直接合了。结果三天后一个边界输入把它打穿了。事后复盘问题特别典型模型写的是快乐路径异常分支要么没有要么只写了throw new Error(unexpected)这种等于没写的兜底。误区三以为上下文越多越好。这也是我一开始的想法把所有相关文件一股脑丢进去。结果是模型被大量无关信息干扰反而抓不住重点。后来我才想明白上下文的关键不是多而是准和分层——这也是下一节要重点讲的全局文档存在的意义。2. 我为什么在真实项目里开始用这套玩法2.1 从逐行审查切换到审意图的心理转变说实话最开始那几天我是抗拒的。作为一个写了十年代码的人看着屏幕上哗啦啦冒出一大段自己没写过的逻辑那种失控感非常真实。我试过逐行读读了两百行就放弃了——不是因为读不懂而是因为读得越细越想去改一改就变成了传统开发Vibe Coding 的效率优势荡然无存。真正的转折点是我给自己定了一条规矩只审三件事——输入输出的契约、错误处理的完整性、以及是否有明显的资源泄漏或死循环风险。剩下的实现细节交给测试去兜。这条规矩一立节奏立刻顺了。我不再纠结为什么它用了 map 而不是 for因为这不影响正确性我把注意力全放在这个函数收到空数组会怎样这个请求超时了会怎样这个循环的终止条件是什么。这三个问题问下来八成的问题都能提前拦住。有一点要提醒这个转变不是让你放弃把关而是让你把把关的精度放在最值钱的地方。代码风格可以后面统一格式化命名可以全局替换但逻辑漏洞和边界缺失是后面极难补救的。2.2 哪些项目类型天然适合放手让它写经过这段时间的实践我总结出一张适配度清单判断标准主要看两点错了会怎样以及验证成本高不高。项目类型适配度主要原因一次性脚本、数据处理工具极高跑一次就丢验证就是看输出对不对内部后台、管理界面高用户少、容错空间大、界面错误容易发现原型与可行性验证极高目的就是快速确认想法能不能成立有完整测试覆盖的业务模块中高测试网兜住了大部分回归风险对外 API 的核心服务中需要额外补契约测试与压测涉及资金、权限、隐私的模块低一旦出错代价不可逆底层驱动、并发密集的逻辑低错误难以复现调试成本极高我自己现在的习惯是原型、脚本、内部工具直接放手核心链路让它先写一版然后我自己重写关键部分。2.3 有几个场景我是明确禁止开启这个模式的第一个是涉及权限判断的地方。模型的直觉在权限逻辑上特别容易出问题比如把校验写在了数据返回之后或者漏掉某个角色的边界。这类代码我坚持自己写或者至少自己逐行过一遍。第二个是数据库迁移与数据删除。任何会改写存量数据、删除记录的操作我不接受看起来对这个标准。这类操作必须有明确的回滚预案而且我会把生成的语句人工审一遍再执行。第三个是加密与凭据处理。这不是不信任模型而是这类代码的正确性很难通过跑一遍来验证需要专门的测试向量和静态分析普通验证手段根本覆盖不到。第四个是性能敏感的循环。模型生成的实现往往可读性优先可能藏着一个嵌套遍历或者每次都重新建连接的写法。这类代码我会单独用 profiling 工具过一遍而不是靠肉眼。3. 全局 md 文档让 AI 不再每次从零猜你的项目3.1 为什么单次对话的上下文撑不住一个真实项目这是很多人卡住的地方。你开一个新会话模型对项目一无所知你花五分钟把项目背景、技术栈、目录结构讲一遍然后开始干活。中途聊到第十轮早期说的那些约定已经被挤出上下文窗口了模型开始按照自己的默认习惯写代码——于是你发现它突然换了命名风格、突然用了另一个 HTTP 客户端、突然把配置文件放到了别的地方。全局 md 文档解决的正是这个问题。它的本质是把那些每次都要重复交代的稳定信息落成一个物理文件让工具每次自动读取。这样你省下了开场白模型也有了持久的约束双方都在同一个前提下工作。我记得第一次意识到这件事重要是在一个周五晚上。当时我在调一个接口模型给出的代码用了axios但我项目里一直是fetch封装。我改完之后顺手问了句你为什么会用 axios它说因为你没有告诉我用什么。那一刻我明白了它不是不会是不知道。3.2 一份能用的全局 md 文档最小结构下面这份是我现在在用的模板去掉注释大概七八十行。核心思路是只写模型猜不到的东西。什么叫猜不到比如用 TypeScript它能猜到但错误统一用 Result 类型而不是抛异常它就猜不到。# 项目全局约定 ## 1. 项目一句话 一个面向内部同事的设备借用登记小工具单页应用 轻量服务端。 ## 2. 技术栈不要替换 - 运行时Node.js 20 LTS - 语言TypeScriptstrict 模式必须开启 - 前端原生 DOM 少量模板字符串不引入框架 - 服务端Fastify - 数据层SQLite通过 better-sqlite3 同步访问 - 包管理pnpm ## 3. 目录职责 - src/api/ 仅放路由定义与参数解析不写业务 - src/domain/ 业务规则纯函数优先不依赖框架 - src/infra/ 数据库、日志、外部调用 - src/web/ 前端页面与交互 - scripts/ 一次性脚本 ## 4. 编码约定 - 变量与函数用 camelCase类型与接口用 PascalCase文件名用 kebab-case - 不抛异常统一返回 { ok: true, data } 或 { ok: false, code, message } - 所有对外输入必须在 api 层完成校验domain 层默认输入可信 - 日志只用一个 logger禁止 console.log - 时间统一用 UTC 毫秒时间戳展示层再转本地时区 ## 5. 明确禁止 - 未经说明不得新增第三方依赖 - 不得修改 src/infra/db.ts 的导出签名 - 不得留下 TODO 占位实现或空函数 - 不得在 domain 层直接访问数据库 ## 6. 常用命令 - 安装pnpm install - 开发pnpm dev - 类型检查pnpm typecheck - 测试pnpm test - 构建pnpm build ## 7. 交付标准 每次改动结束前必须保证 pnpm typecheck 与 pnpm test 全绿。这份文档有几个设计上的小心思。第一不要替换和明确禁止这两节是整份文档里最有价值的因为它们直接堵住了模型最容易自作主张的地方。第二目录职责那一节直接降低了耦合模型知道该把代码放哪不会把所有逻辑堆在一个文件里。第三交付标准那一节把验收条件写成了命令这样模型自己就能跑一遍再交给你。3.3 分层文档全局、目录级、任务级三层配合一份全局文档管不了所有细节。真到具体模块你还需要更细的约束。我的做法是三层第一层根目录的全局文档。放技术栈、目录职责、编码约定、禁止项、命令。这部分变动很少大概一两周才改一次。第二层目录级文档。在src/domain/下再放一个 md写这个目录特有的规则比如这里的函数必须是纯函数不允许读写外部状态所有金额以分为单位存储。这类规则放在全局文档里会显得很杂放在目录里就恰到好处。第三层任务级提示。这是每次对话临时给的只针对当前这一件事比如这次只改 api 层不要动 domain。这个分层的好处是上下文的注入量和你实际需要的精度是匹配的。做一个小改动不需要把所有目录的规则都塞进去做跨模块重构再把相关的几份都带上。我踩过的坑是一开始把所有规则都堆在全局文档里结果它膨胀到四百多行模型反而抓不住重点还浪费了宝贵的上下文空间。3.4 文档写完之后怎么验证它真的生效了写完不代表生效。我有个特别简单的验证方法开一个全新的会话什么都不说直接让它做一个小改动比如给用户列表加一个按注册时间排序的选项。然后看它输出的代码有没有用你指定的技术栈文件放对目录了吗返回值格式是{ ok, data }还是裸数据有没有引入新依赖如果这四点都对说明文档生效了。如果有一两条不对通常是那部分描述太含糊。比如我一开始写错误处理要统一模型理解成了统一打日志后来改成明确的返回结构示例立刻就对了。提示验证文档生效时一定要用新会话。在旧会话里测是不准的因为历史消息里可能有你没意识到的暗示。4. 从零跑通一个 Vibe Coding 循环的完整步骤4.1 环境准备里最容易忽略的两件事工具本身其实没什么好挑的能读文件、能改文件、能跑命令的编程助手基本都能用。真正容易被忽略的是两件事。第一件是版本控制的状态要干净。开始之前务必确认工作区没有未提交的改动或者至少先把当前状态提交一次。原因很实在Vibe Coding 的尝试次数很多你需要一个随时能退回去的锚点。如果工作区本来就有一堆零散改动一旦生成的东西把某处改坏了你连退回到哪都说不清。第二件是让命令能一条跑通。类型检查、测试、构建这三个命令必须是开箱可用的。如果项目本身跑测试都要配半天环境那模型每次想自检都卡在环境上整个循环就断了。我现在的习惯是项目初始化第一件事就是把这三条命令配好再开始写业务。还有个小细节如果有格式化工具先跑一遍把存量代码格式化统一。不然模型生成的代码和你原有的风格混在一起后面 diff 会非常难读。4.2 第一条指令应该怎么给第一条指令决定后面几轮的质量。我的经验是遵循一个结构目标 边界 验收方式 参考。举个例子。不要这样说帮我做一个设备借用功能。这样说目标在 src/domain 下新增设备借用的业务规则函数实现借用和归还两个操作。 边界只写 domain 层的纯函数不碰数据库不碰路由入参和返回值都用已有的类型定义。 规则同一台设备同时只能被一个人借用归还时必须校验借用人一致每次操作需要记录时间戳。 验收写完在 src/domain/borrow.test.ts 里补上覆盖上述三种情况的测试然后跑 pnpm test。第二种说法好在哪它把做什么和不做什么都说清楚了把验收标准也给了。模型不需要猜你的意图也不需要为了保险多做一堆你没要的东西——比如顺手把路由也写了那才是真的添乱。我自己的体感是第一条指令里不做什么往往比做什么更重要。因为模型天然的倾向是多做一点显得更完整而这个倾向在有明确边界的项目里就是灾难。4.3 迭代节奏小步、可回滚、随时验收一个健康的循环大概是这样描述一个小目标控制在一次能改完的范围内让模型改改完自己先跑一遍类型检查和测试我这边跑一遍实际功能看输出对不对对了就提交不对就继续描述哪里不对关键是第 2 步一定要让模型自己先跑检验命令。这条我坚持了很久收益特别明显。它自己跑一遍能拦住大概七成的低级错误——拼错的方法名、漏掉的导入、类型不匹配。这些错误如果留到我这我可能得看半天才知道问题在哪。第 4 步的提交频率也要高。我的习惯是每完成一个小功能就提交一次哪怕这个功能只有二十行。提交信息写清楚这次干了什么。这样做的好处是一旦发现三次尝试之前那个方向是错的往回退的时候不会一波带走太多正确的改动。至于小步到底多小我的标准是出错的时候能一眼看出是哪一步引入的。如果一次改动涉及了五个文件、改了两百行那就不算小步应该拆。4.4 什么时候必须停手自己动手写这个判断力比什么技巧都重要。我总结了几条红线碰到就自己写第一同一个问题描述了三遍还没解决。说明要么是模型理解不了这个领域要么是这个问题本身需要更细的拆解。继续描述下去大概率是浪费双方的时间。第二出现了改了这里坏了那里的循环。这说明底层结构有问题让模型在错误的结构上继续打补丁只会越来越乱。第三涉及核心算法或性能瓶颈。这种地方能跑和跑得好差距巨大靠描述很难传达清楚。第四你自己读不懂生成的那段代码。这是最硬的一条。读不懂就意味着无法维护无论它现在跑得多好都是定时炸弹。宁可花时间重写一遍也比留一段黑盒强。5. Vibe Coding 失控时的典型症状与排查链路5.1 症状一改一处坏一处越改越慌这个症状我遇到的次数最多。表现是你让模型改 A 功能它顺手动了 B你再让它修 BA 又坏了。来回几次之后你自己都说不清当前代码是什么状态。这种局面的根因通常不是模型不行而是它没有完整的视野。它每次只看到你给的那几个文件不知道其他模块怎么依赖这些接口。所以它的修改在局部看是合理的在全局看就破坏了契约。对应的做法是一旦进入这个循环立刻停下。然后按这个顺序处理用git diff --stat看这一轮到底动了多少文件通常会发现问题比你想象的大把当前改动全部退回上一次提交重新描述需求时明确列出所有相关的文件并且加上一句不要修改这个列表之外的文件最后这句约束特别管用。它把模型的行动范围框死了破坏其他模块的概率大幅下降。我现在的习惯是任何涉及公共接口的改动都要在指令里显式列出影响面。5.2 症状二能跑但里面全是硬编码这是我踩过的最隐蔽的坑。功能测试全过界面也正常但打开代码一看设备类型是写死的数组、超时时间是写死的数字、路径拼接里混着绝对路径。它在把事办成这个目标上完成得很好在这个代码能不能改上完全不及格。为什么会出现这种情况因为你的验收标准里没有包含可配置性这一项。模型是按你的验收标准干活的你只测了功能它就把功能做到位你没提可配置它就用最简单的方式实现。所以从第二次迭代开始我在全局文档里加了一条所有会随环境变化的量地址、超时、开关、类型枚举必须来自配置文件或环境变量不得硬编码。加了这一条之后这类问题基本消失了。如果已经出现了处理办法是让模型专门做一次去硬编码的重构并且在指令里明确列出要抽出来的量有哪些——不要指望它自己找全它找不全。5.3 症状三文档和代码开始脱节全局 md 文档写得越认真这个问题越容易出现。因为你在文档里规定了目录职责但某一轮对话里模型为了方便把代码放在了别处。三周之后文档说的和代码做的是两回事文档就成了一纸空文。我的处理方式是把文档校验变成循环的一部分。具体做法是每隔一段时间我大概是一周一次开一个新会话问它请对照项目全局约定文档检查 src 下所有文件的目录归属是否合规列出不符合的地方和原因。让它自己做一次审计。这个操作花不了几分钟但能及时发现漂移。发现之后不要直接让它改先自己看一眼因为有时候是文档本身需要更新而不是代码需要挪。5.4 一条可以照着走的排查顺序出问题的时候最怕的就是乱试。我固定用这个顺序排查效率最高步骤动作判断依据1看git diff --stat改动范围是否符合预期2跑类型检查有没有结构性错误3跑测试有没有回归4让模型复述它的假设它的理解和你的意图是否一致5二分回滚问题是哪一轮引入的6退回后重述需求把缺失的约束补进指令第 4 步特别值得说。问它你实现这个功能时假设了什么前提往往能直接暴露问题。我遇到过一次它假设输入一定非空所以没做空值判断而实际上游可能传空数组。这个假设它自己说得清清楚楚比我翻十遍代码都快。6. 长期项目里必须立住的几条硬规矩6.1 版本控制是唯一的保险绳这句话我写在文档最前面。Vibe Coding 的所有便利都建立在一个前提上你随时能退回去。一旦这个前提不成立整个模式的风险就会放大好几倍。具体要做到几点每次生成之前工作区是干净的每个功能完成就提交提交信息写清楚改了什么、为什么改。分支策略上我建议每个稍大的功能开一个分支因为 Vibe Coding 的尝试过程会产生很多零碎提交混在主分支里很难看。还有一个容易忘的不要在有未提交改动的状态下接受大范围重构。这时候如果出了问题你连哪些是它的改动、哪些是我的改动都分不清回滚就变成了猜谜。6.2 类型检查和测试给感觉装上刹车前面说过Vibe Coding 靠感觉推进。但感觉必须有东西兜底。类型检查和自动化测试就是那个兜底。我的配置标准是这样的类型系统开严格模式宁可多写几个类型定义也不要any满地跑因为any会绕过整套检查。测试上我不追求覆盖率数字而是盯住三件事——核心业务规则、边界输入、错误分支。这三块有测试心里就有底。顺带说一句让模型自己写测试有个副作用就是它写的测试往往会迎合它的实现而不是验证需求。我遇到过测试全绿但功能就是不对的情况原因是测试用例本身写错了。所以我现在的习惯是关键功能的测试用例我自己列让模型去实现而不是让它自己列自己实现。6.3 全局文档的更新节奏文档不是写完就完事。我的更新规则有三条第一每次新增一条约束就立刻加进去。比如这次发现它爱硬编码就加一条禁止硬编码。不要攒着攒着就忘了。第二定期删掉已经内化的内容。有些规则写了一段时间后发现模型基本不会犯这个错了就可以删掉给文档减负。上下文空间是有限的留给出错概率高的约束。第三每次大重构之后把文档通读一遍。因为重构常常会改变目录结构文档里的目录职责描述可能已经过时了。我现在的文档大概每两周动一次每次动只是加一两行或者删一两行。这个维护成本很低但收益很明显。6.4 团队里怎么共享这套规则如果只有你一个人用文档放根目录提交到仓库就够了。如果是团队就需要多考虑两点。一是不要各写各的。每个人项目里有自己的一份全局文档结果就是提交来提交去互相覆盖。我们后来的做法是把这份文档当作项目资产改动走正常的评审流程。二是区分强制项和建议项。文档里有些是硬约束比如技术栈、返回值格式有些是偏好比如命名风格。硬约束要写得斩钉截铁偏好可以写得宽松些。全都写得一样强硬模型会束手束脚反而影响效率。还有一点体会团队里最好有一个人负责这份文档的最终拍板。否则讨论到底用不用某个库会没完没了文档也就没法稳定。7. 几个更细的技巧和我自己的体会7.1 用反向提问榨出更多信息这是我用得最多的一招。当模型给出一个方案后不要急着接受先问它这个实现有哪些已知的局限在什么情况下会出问题它的回答经常能帮你省掉一次返工。因为模型对自己的产出其实是有认知的它知道哪里是权宜之计、哪里用了简化假设只是除非你问它不会主动说。我问过几次之后发现它自己列出的风险点往往比我事后发现的还要准。7.2 让它先写文档再写代码这个习惯是从写全局文档那件事延伸出来的。对于稍微复杂一点的功能我会先让它写一份简短的设计说明接口长什么样、数据怎么流、有几个分支。我确认之后再让它照着写代码。好处有两个。一是纠正成本低改一段文字比改一百行代码容易得多。二是前后一致性有保障因为它手里有自己写的说明实现时不太会跑偏。这个流程多加了一步但整体上反而更快。7.3 我这几周攒下来的一份踩坑清单不要在同一个会话里滚太久。聊到二三十轮之后早期的约定基本失效了模型开始飘。宁可分新会话重新开场。不要给它顺便优化一下。这个指令会引发大范围改动风险极高。要优化就单独立一个任务。不要接受没跑过的代码。哪怕它说应该没问题也要实际跑一遍。我因为这个吃过一次亏浪费了一个下午。不要让它在没有测试的模块上大改。先补测试再动逻辑。不要相信我改好了。要它给出证据跑了什么命令、输出了什么。这一条能省掉大量来回。最后分享一个我最近才意识到的事Vibe Coding 真正的门槛不在提示词写得多漂亮而在于你能不能把一个模糊的想法切成一连串边界清晰的小问题。这件事以前是隐性的因为写代码的过程本身就逼着你做拆解现在拆解这一步被显式地摆到了台面上做不好后面全乱。我自己在这上面吃过不少亏也还在练。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →