这段时间“vibe coding”这个词在开发者圈子里是真的火——用自然语言描述个大致想法,让AI噼里啪啦把代码写了,自己也懒得逐行看,跑通了就感觉“人机合一”。我在不少技术社区看到新手用这种方式一晚上搭出个小工具,确实爽。但爽完之后,等需求稍微复杂一点、项目稍微长大一点,问题就来了:AI改版几次之后改懵了,自己也不知道当前代码到底在干嘛,甚至因为一句描述不当,整个模块的逻辑都悄悄跑偏。
我自己也踩过这类坑,后来真正稳住工作流的关键,就是把“vibe”换成“spec”——也就是从“凭感觉描述”切换成“按规格说明驱动”。所谓spec-driven,简单说就是先写一份清晰的规格文档,把功能边界、输入输出、验收标准都定清楚,再让AI照着干活。这篇文章我会直接拆解传统vibe coding到底痛在哪、spec-driven为什么能治这个痛,并且给出可以直接抄走的一套落地模板和实操流程。适合正在用AI辅助写代码的新手,也适合想把AI编程纳入正规项目流程的团队。
1. 传统vibe coding的四个典型翻车现场:问题不在AI,在方式
先说明一下,我不反对vibe coding,我自己也用。但用归用,它的问题不能回避。你如果不清楚它会在什么地方坑你,那真的很容易把一个小项目写成一场灾难。
1.1 上下文漂移:AI“改着改着就忘了自己要干嘛”
这是最经典的一类问题。你跟AI对话式开发,连续聊了30多轮,第二轮说“用户信息要存数据库”,第20轮说“这里改成从接口读”,到第27轮让它在原逻辑上加个缓存。AI并不会像人一样牢牢记住“第2轮定的方案”和“第20轮改的需求”之间的冲突,它只会根据近期对话推测你的意图。
结果就是:它把第20轮的“从接口读”改没了,回到了第2轮的数据库直连,甚至为第27轮的缓存写了一套根本用不上的逻辑。你发现的时候还不一定马上能定位,因为改动是局部的,不对比中间版本根本看不出来。
我见过有人调侃这是“AI失忆”,但其实更准确的说法是:对话式开发没有一个稳定的需求基线。所有需求都散在聊天记录里,而聊天记录越堆越长,AI对早期内容的引用就越模糊。
1.2 隐藏债务:你根本不清楚AI在项目里悄悄埋了什么东西
我有个朋友做过一个实验:让AI给他写一个带登录、积分、订单三个模块的小商城,然后自己不看代码直接部署。最终跑起来确实没问题,但事后审计代码时发现,AI一共引入了十多个依赖库,其中两个是他听都没听过的冷门库,还有一个版本的传递依赖存在已知漏洞。
这种“隐藏债务”是vibe coding最大的雷。因为你的关注点都在功能层面——能跑,页面上有东西,接口有返回——但代码内部的依赖管理、异常路径、安全边界、资源释放,AI不会主动跟你报备。它追求的是“完成任务”,不是“健康设计”。你可以说“用的时候小心点就行”,但事实是:你根本不知道哪里需要小心。
1.3 测试缺失:改一处,崩一片,你还找不到是谁干的
传统vibe coding工作流里,几乎没有人会要求AI先写测试再写实现,因为大家的心理预期是“先看效果,回头再补测试”。但AI改代码是全局性的,它可能为了满足你一个新需求,重构了某个底层util函数,然后所有调用方一起出问题。
如果是人手开发,你能通过git blame快速找到改动点;但AI的提交信息通常写得很泛,比如“refactor code”或“update logic”,你很难从提交历史里获得有效信息。更别说很多vibe coder根本不做细粒度提交,让AI直接改完覆盖。等出bug的时候,你连“哪次改动引入的”都说不清。
1.4 验收无锚点:你说不清“做好了”到底长什么样
最后这个问题最致命:在vibe coding模式下,“完成”是一个感觉,不是一个标准。AI说“已经实现好了”,你看着界面觉得差不多,但没有一份明确列出的验收条件,你根本不知道它有没有漏掉边界情况、有没有处理异常输入。
比如你让它做一个URL短链服务,它给你生成了能用的短链生成逻辑。但雪花算法ID重复怎么办?用户输入非法URL怎么办?短链过期后数据库清理策略怎么设计?这些如果没有提前写进规格里,AI大概率是不处理的——因为它不知道“做完”包括这些。
2. Spec-Driven的核心思路:从“让AI自由发挥”变成“让AI按图施工”
前面那些坑,讨论到最后其实会指向一个共同根源:需求没有以结构化方式传给AI。对话内容都是自然语言散句,缺乏结构,AI从中提取的优先级、边界、约束很可能跟你的意图不一致。Spec-Driven就是把这块补齐。
2.1 认知转变:规格文档不是文档负担,是给AI的“安全带”
很多人一听“先写规格文档”就头大,觉得这是又回到了重流程的老路。但理解一下原理就不会这么想:AI在没有约束时的自由发挥能力,既是优势也是危险。给它清晰的规格,等于是给它一副骨架,让它在这个骨架范围内发挥创意、填充肌肉和血管。这样做之后,它生成代码的方向性和稳定性都会大幅提升。
我自己的感受是:花在写规格上的时间,相比省下的调试和返工时间,大概是1比4甚至更多。写一份核心规格可能只需要20到40分钟,但它能让AI少跑偏好几次。
2.2 最小可用Spec包含哪些要素:不需要写一本论文
完整的软件规格说明有很多种格式,比如传统的SRS(软件需求规格说明书),有各种IEEE标准格式。但个人和小团队用,根本不需要那么重。我实际用下来,一份能压住场面的最小Spec只需要四块:
| 对象 | 回答什么问题 | 具体内容 |
|---|---|---|
| 项目目标 | 为什么要做这个? | 项目要解决的痛点、给谁用、核心价值 |
| 范围边界 | 做到什么程度为止? | 做什么、明确不做什么、禁止引入什么 |
| 功能规格 | 每个功能具体要哪些行为? | 输入、处理逻辑、输出、异常行为、权限校验 |
| 验收标准 | 怎样才算“完成”? | 可执行的测试样例、表现指标、代码约束 |
这四块不是学术要求,而是每一块都在解决前面提到的某类痛点:项目目标防止AI偏航,范围边界防止AI无限扩大改动,功能规格把“输入输出和异常”钉死,验收标准给你一个不说“我感觉还行”而是能说“对,这就是完成了”的依据。
2.3 Spec-Driven和“先写一堆设计文档再开发”不是一回事
这里要区分一下,很多人听到spec,以为就是老掉牙的“瀑布流”——先写全量文档、冻结需求,再进入开发。那是误解。Spec-Driven是“需求锚定+任务分块”的方法论,不是“文档写一次就不改了”。
实践中,spec是可以迭代的:你花15分钟写个v1版本,AI按v1做出第一个功能块;当你验证后发现需求理解错了,改spec的v2版本,再让AI改对应模块。它和敏捷开发并行不悖,而且因为每个change都对应spec中的更新,你天然就有了变更记录和回滚锚点。
3. 一份能落地的Spec长什么样:完整模板与拆解实例
光说概念还不够,我直接给一份我实际用过的模板,并且用一个真实小项目拆解给你看。这个项目是一个“Markdown笔记转HTML静态站”的小工具,拿它做例子很合适,因为它足够小、功能边界也清晰。
3.1 项目目标与范围边界:这一部分怎么定,AI才不会“过度设计”
项目目标这一段,我通常用两到三句话写清楚:给谁用、解决什么问题、核心交付是什么。别写愿景,写“接下来这个迭代的目标”就行。
项目目标(v1.0) - 目标用户:独立开发者,需要将本地Markdown笔记批量导出为可部署的静态HTML。 - 核心价值:一条命令完成全部转换,无需手动处理页面模板和资源引用。 - 本迭代交付:支持单目录下的Markdown批量转换,生成index.html和文章详情页。范围边界是很容易被忽略的一段,但特别关键。AI有个特性:你只说“做什么”,它会忍不住把“相关的”也做了。比如你让它做Markdown转HTML,它可能顺手给你加一个主题切换功能,理由是这个“很合理”。但对你来说,这可能是额外的样式负担、额外的测试面。范围边界就是打预防针:
范围边界(v1.0) - 不做:在线编辑器、多用户系统、自定义主题市场、(明确说)这些是v2.x的候选功能。 - 禁止:引入重型前端框架(如Vue/React)、引入非必要的构建链(如Webpack)。 - 依赖限制:仅允许使用自带的markdown解析库,或者你明确指定的库。这一小段写下来,AI就不会自作主张地“锦上添花”了。
3.2 功能规格的撰写范式:把“行为”拆成输入、处理、输出
功能规格是整个Spec的核心。我建议每条功能都按照“输入→处理→输出→异常”的结构来写,而不是写一句话描述。
功能F1:批量转换Markdown为HTML - 输入:指定目录下的所有.md文件(含子目录)。 - 处理: 1. 解析每个文件的frontmatter,提取title、date、tags字段。 2. 将Markdown正文转换为HTML。 3. 使用模板生成完整HTML页面,将本地图片路径改为相对路径。 4. 生成所有文章的索引页index.html,按日期倒序排列。 - 输出:构建目录下生成index.html和post/子目录下的文章文件。 - 异常: 1. 目录不存在时,输出错误信息并退出(退出码1)。 2. 单个文件解析失败时,跳过该文件并在日志中记录警告,不能中断整体流程。 3. frontmatter缺失时,用文件名作为title,不报错。你有没有发现,光是把“异常”这两条写出来,就堵掉了好几个vibe coding常见的坑:AI通常会因为一个坏文件就整个崩溃,或者因为frontmatter少了字段而报错。有了显式的异常行为定义,AI一次性就能写对。
每个功能都这样写,可能会让你觉得“是不是太多了”?我的经验是:关键的3到5个核心功能值得这样写,边缘小功能可以简单一行描述。不要过度工程化规格本身。
3.3 验收标准与测试锚点:让“完成”有可操作的判断依据
验收标准是最能被vibe coder跳过的部分,也是我后来最喜欢单独花时间写的一部分。它不一定复杂,一小段可执行的描述就行。
验收标准(F1) - 给定测试目录(包含5个有效md文件、1个损坏md文件、1个缺frontmatter的md文件),执行转换命令。 - 预期:命令执行成功;index.html正确列出5篇文章并按日期倒序;损坏文件被跳过且路径记录在日志中;缺frontmatter的文件生成成功且文件名作为title。 - 执行时间不应超过10秒(针对不超过100个文件的目录)。有了这个,你不需要和AI说“你可不可以确保健壮性”,你只需要把验收标准贴给它,并告诉它“实现完成后,自己按这个标准给你列出的样例数据做一次自检”。这个操作直接把“AI自由发挥”变成了“AI自我验收”。
3.4 变更记录的节奏管理:Spec不是写一次就冻结的石头
最后谈一下迭代。Spec它天然是活文档。我推荐的做法是每次改spec时,在文档末尾维护一个“变更记录”表格:
| 版本 | 日期 | 变更内容 | 影响范围 |
|---|---|---|---|
| v0.1 | 2025-01-10 | 初版规格 | 全部功能 |
| v0.2 | 2025-01-12 | 补充F1的异常处理策略 | F1 |
| v0.3 | 2025-01-15 | 新增F4:生成RSS订阅文件 | 全局 |
这个表格不必写入AI去执行,但它对你和团队很重要:当你发现某个功能改乱了,可以回看spec哪个版本做过变更,让AI只针对对应部分做增量修改。这比在聊天记录里翻上下文高效得多。
4. 在真实项目里跑通Spec-Driven工作流:从开工到验收的全流程
这一节是纯实操。我拿一个真实项目讲讲spec-driven的完整循环,包括几条可以直接用的提示词框架和遇到问题时怎么办。
4.1 推荐的迭代节奏:“先窄后宽,跑通之后再扩展”
很多人第一次用spec-driven时,最容易犯的错是把第一个版本的规格写得太雄心勃勃,把所有功能都列出来。结果AI生成的代码量巨大,你的检查负担也大,出现问题根本分不清是哪块逻辑造成的。更好的节奏是这样:
- 第一个迭代只做最小闭环。比如上面那个Markdown转HTML工具,第一个迭代只做“单篇文章转换”,不做索引页,不做子目录扫描。
- 跑通这个最小闭环后,再在spec里追加第二个功能,让AI增量实现。
- 每轮新增功能量控制在“你拿半小时能人工过一遍代码”的程度。
这个节奏的核心优势是:问题一旦出现,你能快速定位是“这轮新增的代码”导致的,而不是在一大片AI生成代码里捞针。我甚至会在每轮迭代时明确告诉AI:“不要修改与本次需求无关的文件。”这句话能大量减少AI顺手重构的冲动。
4.2 让AI按照Spec执行的提示词框架:可以直接复制使用
很多人以为spec-driven就是“把spec丢给AI”,然后就好了。不是的,你需要给AI一个清晰的任务指令,否则它可能会把spec泛泛地读一遍,然后按自己的节奏来。我常用的提示词框架是这样的:
你正在参与一个spec-driven开发任务。请严格遵循以下流程: 1. 阅读规格文档《xxx》的全部内容,包括范围边界和验收标准。 2. 如果规格中存在歧义或缺失的细节,先列出问题清单,不要擅自假设。 3. 按照本迭代的功能条目(F1、F2)逐条实现,不要修改与这些条目无关的文件。 4. 实现完成后,按照验收标准进行自测,并逐项报告结果。 5. 只报告结论和必要的变更说明,不要额外解释你“还能做什么”。如果你在用GitHub Copilot或者Cursor这类工具,可以把spec文档放在项目根目录如spec.md,并在每轮对话开始时让它先“读spec.md确认需求”。
我自己用下来,这套提示词最大的作用不是“教AI做事”,而是建立秩序:你要求它列问题清单,它就不会在你没授权时自己脑补;你要求它按验收标准自测,它就不会说“完成”但不验证。
4.3 我踩过的坑和应对:这三个点几乎每次都中招
哪怕有了spec,实践里依然有几个高频坑,我先帮你踩了,你注意避开。
第一个坑是“ai对spec视而不见”。有时候你在对话窗口发了一整份spec,AI嘴上说“已了解”,但写出来的代码还是跑偏了。应对办法是让它在回答开头先复述它理解的验收标准和功能边界,如果复述内容不对,你马上纠正,再让它动手。这一步只花一分钟,但能避免一次大返工。
第二个坑是“ai过早抽象”。有些AI逻辑性很强,你让它实现两个相似功能,它会立刻抽象出通用基类或宏大的工具函数。但是需求变了,你只想改A功能,却发现它的抽象影响到了B和C。我的策略是:在spec里加上“避免不必要的抽象”或者在提示词里写明“在你的抽象设计成本达到必要性之前,先复制代码实现”。等确实有第三个重复点,再讨论抽象方案。
第三个坑是“依赖库选择自作主张”。这个问题我在1.2小节提过,UI类项目尤其严重。AI为了一个进度条效果,可能引入一个很酷但维护状态不明的npm库。我现在会在spec中单独写一行:“新增任何第三方依赖前,必须暂停实现,列出备选库和选择理由,等待确认。”这一步能多花几分钟,但可以让你的依赖树保持可控。
4.4 当AI质疑Spec时怎么办:这是好信号,不要压制
这是个很有意思的现象:当你给了AI一份规格文档时,有时候AI会在回复里说“检测到规格中某处定义与现有代码不一致,建议调整”或者“验收标准中第3条可能存在歧义”。
很多人的第一反应是“AI怎么不听话,让它做就完事了”。但我建议你把这种信号当成礼物。AI在逻辑一致性检查方面已经很强,它发现spec内部矛盾,确实可能有问题。这是spec-driven模式独有的优势:有了明确的规格文档,AI才有能力、才有依据去做“需求审查”。传统vibe coding里AI不会这么做,因为它没有参考点。
处理方式也很简单:如果AI提出的质疑是对的,直接更新spec,并告诉AI“已确认,按你的建议修订”;如果AI的理解跑偏了,则把spec相关条目再明确一遍,纠正认知。
5. 从个人开发到小团队协作:Spec-Driven还能怎么扩展
最后聊聊Spec-Driven的成长路径。这个方案不只是给个人开发者用的,它的复用性和扩展性我觉得比想象中更大。
5.1 单人项目:Spec就是你的“第二大脑”
我用了一段时间后发现,spec.md最大的受益者不是AI,而是未来的自己。三个星期后你回头改一个模块,翻代码的效率远不如翻spec文档来的高效。spec里的功能定义、决策记录、变更记录,就是当时的“你”留给以后的“你”的便签。
有条件的我建议把spec放在git仓库里,每次更新单独提交一次,commit message写成“update spec: F3 add pagination logic”。这样做之后,你的git历史里就同时有了“需求变更历史”和“代码变更历史”,排查问题的时候双线对应,效率高很多。
5.2 多人协作:把“评审代码”变成“评审Spec与代码的差距”
在做小团队协作时,spec-driven也有它的独特价值。传统团队里做code review,大家讨论的是代码风格、实现逻辑;而有了spec之后,review多了一个核心维度:这段代码是否符合规格定义。
评审人看到PR时可以先打开对应的spec版本,逐条比对代码实现。哪条没有覆盖、哪条多实现了、异常处理是不是按规格定义做的,一目了然。这个流程对新人特别友好,因为新人还不熟悉团队的代码库,但他有了spec,就能理解改动意图。
5.3 后续更新方向:这个文档我会持续迭代的部分
因为我正在多个不同规模的项目里持续使用这套玩法,所以这个主题的博文会有比较多的实践迭代空间。后面我打算按这几个方向继续更新内容:
- 结合不同类型项目的spec范本(比如纯前端项目、AI Agent项目、数据同步服务)
- 针对不同AI工具(Cursor、Copilot、或者开源模型本地部署)的spec注入姿势
- 更复杂的团队分工模式,比如PM写目标、技术负责人写功能规格、AI负责实现,如何分权
我目前把自己的模板和样例都放在一个持续更新的项目里,每轮迭代带来的经验教训也会同步进来。如果你也在尝试spec-driven,建议先拿一个小而真实的项目练手,别一上来就重构你的大系统。把规格写清楚,让AI按图施工,你会发现,以前那种“AI写码我追责”的无力感,其实是可以被一套好流程轻松消解的。