之前提到了 Vibe Coding 踩过的坑——重复实现、偷懒 mock、风格混乱、架构散沙。这些坑的根源其实是同一个问题:我们没有告诉 AI"该写什么"。
直接让 AI 写代码,就像把一个人扔进项目里,不给需求文档、不给设计图、不给技术选型,只说一句"帮我把这个功能做了"。它能写出来,但写出来的东西大概率不是我们想要的。
于是出现了 Spec 驱动 AI 开发的思想,彻底改变了 Vibe Coding 的稳定性:在让 AI 写任何代码之前,先写一份 Spec。
什么是 Spec 驱动开发
Spec(Specification,规格文档)不是什么新概念。传统软件工程里,写代码之前先写设计文档是标准流程。
但在 AI 编码的语境里,Spec 的意义完全不同。
传统开发里,设计文档是写给人看的——团队成员读完之后,各自理解需求,然后动手写代码。理解有偏差的时候,可以当面沟通、可以看上下文、可以翻历史记录。
AI 没有这些能力。它没有记忆,不知道项目的历史决策,不会主动问"这里我不确定,能确认一下吗"。给它的信息就是它全部的上下文——Spec 写了什么,它就按什么来;Spec 没写的,它就自己猜。
所以 Spec 驱动开发的核心不是"多了一步文档",而是:把 AI 的工作边界写清楚,让它在边界内执行,而不是自由发挥。
苏米注:我刚开始用 Claude Code 写代码的时候,也是直接丢一句"帮我写个用户管理系统",结果每次生成的代码风格都不一样,文件结构也经常变。后来发现,花 10 分钟写一份 Spec,AI 生成的代码质量直接上一个台阶。这不是效率问题,是可控性问题。
为什么三层文档体系是 AI 编码的基石
传统开发里,很多团队不写文档也能跑——因为人有记忆,有上下文,遇到不确定的地方能当面问一句。
但 AI 没有这些能力。文档是 AI 唯一的上下文来源。不写文档,AI 就只能靠猜。
而且不是写一份文档就够了。一个真正稳定的 AI 驱动开发流程,需要三层文档,每一层解决不同的问题:
第一层:需求文档(PRD)— 解决"做什么"
- 这个功能要解决什么问题
- 目标用户是谁
- 核心场景有哪些
- 成功标准是什么
没有需求文档,AI 不知道为什么要做这个功能。它会按照自己的理解去实现——实现出来的东西可能技术上没问题,但根本不是用户需要的。
第二层:设计文档(SDD)— 解决"怎么做"
- 系统架构是什么样的
- 模块怎么划分,模块之间怎么通信
- 数据模型长什么样
- 接口怎么定义
- 技术选型是什么,为什么选这个
没有设计文档,AI 每次都会自己做架构决策。而且每次决策都不一样——今天用这个方案,明天用那个方案,项目越来越乱。
第三层:实现规格(Spec)— 解决"具体怎么写"
- 这个功能的代码放在哪里
- 用什么库、什么写法
- 边界情况怎么处理
- 不做什么
没有实现规格,AI 会在细节上自由发挥。风格不统一、依赖乱加、边界不处理——都是因为这一层没写清楚。

三层文档的关系
需求文档(PRD)→ 定义"做什么"和"为什么做"
设计文档(SDD)→ 定义"怎么做"和"用什么做"
实现规格(Spec)→ 定义"具体怎么写"和"不能怎么写"
三层不需要都很长。小功能可能 PRD 就一句话,SDD 就一段话,Spec 写清楚边界和约束就行。大功能才需要每层都认真写。
关键是:每一层都要有,哪怕只是几句话。因为 AI 不会自己补全缺失的层——PRD 没写,它不知道为什么做;SDD 没写,它不知道架构怎么定;Spec 没写,它不知道细节怎么处理。
Spec 里该写什么(6 个核心要素)
一份给 AI 用的 Spec,不需要像传统 SDD 那样写几十页。但至少要覆盖这几件事:
1. 需求背景(为什么做这个)
一两句话说清楚:这个功能解决什么问题,给谁用。AI 知道了"为什么",才能在遇到模糊地带时做出更合理的判断。
2. 功能边界(做什么,不做什么)
"不做什么"比"做什么"更重要。AI 的默认行为是尽可能多做——给它一个需求,它会顺手加上它觉得"应该有"的东西。写清楚"本次不做 X、不做 Y、不做 Z",能省掉大量返工。
3. 模块设计(放在哪里,怎么拆)
这个功能涉及哪些模块、新代码放在哪个目录、和现有模块怎么交互、数据流方向是什么。不写这个,AI 会自己决定文件放在哪里——每次决定都不一样。
4. 接口定义(输入输出长什么样)
如果涉及前后端交互,把接口的请求和响应格式写清楚。不要让 AI 猜接口长什么样。它猜的和后端实际返回的,大概率对不上。
5. 技术约束(用什么,不用什么)
用哪个库("用 axios,不要用 fetch")、用哪种写法("用函数式组件,不要用 class")、用哪种样式方案("用 Tailwind,不要写内联样式")。这些如果不写,AI 每次都会自由选择。
6. 验收标准(怎么算做完)
哪些场景必须能跑通、哪些边界情况必须处理、需不需要写测试。写了验收标准,实现完之后对照检查就行。
Spec 驱动的完整流程(5 步)
实际操作中,我现在的开发流程是这样的:
Step 1:写 Spec
花 10-15 分钟,把上面 6 个部分写清楚。不需要很长——一个中等功能的 Spec 通常 200-400 字就够了。关键是写清楚边界和约束,不是写得多。
Step 2:把 Spec 喂给 AI
把 Spec 作为任务的第一条消息发给 AI。可以直接贴在对话里,也可以放在项目的 spec/ 目录下让 AI 读取。如果用 Claude Code 或 Codex CLI,可以直接说:"按照 spec/feature-x.md 的设计实现这个功能。"
Step 3:AI 按 Spec 实现
AI 读完 Spec 之后开始写代码。因为有了明确的边界和约束,它的实现会比"自由发挥"稳定得多:不会乱选技术栈、不会乱放文件、不会多做不需要的功能。
Step 4:对照 Spec 检查
实现完之后,拿 Spec 当 checklist 逐条对照:功能边界有没有超出、接口格式对不对、技术约束有没有遵守、验收标准能不能通过。有了 Spec,代码审查就有了标准。
Step 5:迭代
如果检查发现问题,不是直接让 AI"改一下"——而是先更新 Spec(把遗漏的约束补上),再让 AI 按新的 Spec 重新实现或修改。Spec 是活的文档,会随着开发迭代更新。

一个真实的 Spec 示例
以"用户列表页面"为例,一份实际的 Spec 长这样:
# Spec: 用户列表页面
## 需求背景
后台管理系统需要一个用户列表页面,展示所有注册用户,支持搜索和分页。
## 功能边界
- 做:列表展示、搜索(按姓名/邮箱)、分页(每页 20 条)
- 不做:用户编辑、删除、批量操作、导出(后续迭代)
## 模块设计
- 页面组件:src/pages/users/UserListPage.tsx
- API 层:src/api/users.ts(新建)
- 类型定义:src/types/user.ts(新建)
## 接口定义
GET /api/users?page=1&pageSize=20&keyword=xxx
响应:{ "data": [...], "total": 100, "page": 1, "pageSize": 20 }
## 技术约束
- 请求用 axios(项目已有封装在 src/api/request.ts)
- 状态管理用 Zustand(不要用 useState 管列表数据)
- 样式用 Tailwind(不要写 CSS 文件)
- 表格用项目已有的 Table 组件(src/components/Table)
## 验收标准
- 页面能正确展示用户列表
- 搜索输入后 500ms 防抖触发请求
- 分页切换正常
- 加载中有 loading 状态
- 接口报错有错误提示
200 多字,写起来 10 分钟。但有了这份 Spec,AI 写出来的代码会比"帮我写一个用户列表页面"靠谱十倍。
Spec 和规则文件的区别
可能会有疑问:Spec 和 AGENTS.md / CLAUDE.md 有什么区别?

规则文件是长期的、全局的。它定义的是"这个项目永远要遵守的规则"——代码风格、命名规范、禁止事项、技术栈选型。
Spec 是单次的、局部的。它定义的是"这一个功能具体怎么做"——需求、接口、模块、边界。
两者配合使用:
- 规则文件保证风格一致性(不管做什么功能,代码风格都一样)
- Spec 保证实现正确性(这个功能按照设计来,不跑偏)
没有规则文件,AI 每次风格都不一样。没有 Spec,AI 每次实现都可能跑偏。两个都要有。
苏米注:我的做法是在项目根目录放一个 AGENTS.md 定义全局规则(代码风格、技术栈、命名规范),然后在 spec/ 目录下为每个功能写独立的 Spec 文件。这样 AI 既能保证风格统一,又能按具体设计实现。两者缺一不可。
写 Spec 的成本,比想象中低
很多人觉得"写 Spec 太麻烦了,不如直接让 AI 写代码快"。
实际体验下来:写 Spec 花 10 分钟,能省掉后面 2 小时的返工。
不写 Spec 的典型流程:
- 1. 让 AI 写代码(5 分钟)
- 2. 发现不对,描述问题让它改(10 分钟)
- 3. 改完发现另一个地方也不对(10 分钟)
- 4. 越改越乱,推倒重来(30 分钟)
- 5. 重来之后还是有问题...
写了 Spec 的流程:
- 1. 写 Spec(10 分钟)
- 2. AI 按 Spec 实现(5 分钟)
- 3. 对照 Spec 检查,微调(10 分钟)
- 4. 完成
前者看起来快,实际上慢。后者看起来多了一步,实际上快得多。
苏米注:Spec 驱动开发不是什么高深的方法论,就是一个简单的习惯:写代码之前,先把"该写什么"写清楚。对人类开发者来说,这个习惯有用但不是必须的——因为人有记忆、有上下文、能沟通。对 AI 来说,这个习惯是必须的——因为它没有记忆、没有上下文、不会主动问。Spec 就是它的记忆和上下文。