需求最初总是散落在聊天记录里:「那个导出功能,还要支持按门店筛选,顺便把上次说的权限也带上」。三个月后没人记得当初为什么这么定,AI 助手照着一句模糊描述写了 800 行代码,评审时谁也说不清它是不是符合预期。
规格驱动开发(spec-driven development)想解决的就是这件事:把"要做什么"写成一份人和 AI 都要读的文档,代码是它的实现,而不是反过来。
1. 要解决的问题
| 症状 | 现场表现 | 根因 |
|---|---|---|
| 需求只存在于对话里 | 换个接手人就要重新问一遍 | 没有单一的书面来源 |
| AI 按模糊描述乱写 | 生成的代码"看着像",细节全错 | 输入信息量不足,模型只能猜 |
| 验收无可对照物 | 评审靠"感觉不对" | 没有可逐条核对的条目 |
| 变更无人记录 | 半年后不知道某行为什么在那 | 决策没有留痕 |
| 反复返工 | 同一需求改五遍还在讨论范围 | 边界没写下来 |
这些问题不是"文档写得不够多",而是文档的形态不对:散在 wiki、issue、聊天里,没有强制约束力,也没有和代码建立引用关系。
2. 核心思路
一句话:先写规格,再写代码;规格是需求方、开发、AI 三方的共同输入。
它带来三个可检验的效果:
- 输入变确定:AI 拿到的是结构化描述,不是一句话;
- 验收可枚举:规格里的每条行为都能对应一个测试或一次手工核对;
- 变更可追:改行为必须先改规格,diff 就是变更记录。
3. 这类工具通常包含什么
| 层次 | 回答的问题 | 产物 |
|---|---|---|
| 需求层(spec) | 要做什么、边界在哪、验收标准 | 用户故事 + 场景 + 明确的不做项 |
| 设计层(design/plan) | 怎么做、有哪些取舍 | 技术方案、数据模型、接口约定 |
| 任务层(tasks) | 谁做、按什么顺序做 | 可勾选的任务清单 |
| 约束层(constitution/规范) | 全局必须遵守的规则 | 编码规范、架构约束、评审门槛 |
目录约定大同小异,形态通常是这样:
specs/
refund-policy/
spec.md # 需求与场景
design.md # 技术方案与取舍
tasks.md # 任务清单(可勾选)
changelog.md # 规格自身的变更记录
一份 spec 的最小可用形态:
# 退款时效说明
## 背景
客服需要向用户解释退款到账时间,当前口径不统一。
## 场景
- 场景 1:用户询问退款时效
- Given 订单已退款成功
- When 用户询问"多久到账"
- Then 回答"3–5 个工作日",并给出订单号
- 场景 2:退款失败
- Given 退款状态为失败
- Then 说明失败原因,并提供人工入口
## 不做
- 不承诺具体到账时刻
- 不查询银行侧状态
## 验收
- [ ] 两类场景都能被正确识别
- [ ] 失败场景不返回时效话术
注意"不做"这一节:它比"要做"更值钱,因为大部分返工来自范围蔓延。
4. 与 AI 编程工具的协作命令
这类工具的第二个组成部分是一组命令约定,让 AI 按固定节奏产出文档,而不是直接开始写代码:
| 阶段 | 典型命令形态 | 产出 |
|---|---|---|
| 初始化 | init |
建立目录与模板 |
| 立规 | constitution / 规范文件 |
全局约束 |
| 写需求 | specify / proposal |
spec.md |
| 出方案 | plan / design |
design.md |
| 拆任务 | tasks |
tasks.md |
| 实现 | implement / 执行 |
代码 + 勾选任务 |
| 归档 | archive / validate |
校验与收口 |
# 形态示意:具体子命令与参数按当前工具版本核对
openspec init # 在当前仓库建立规格目录
openspec list # 看有哪些规格与状态
openspec validate refund-policy # 校验规格结构是否完整
openspec archive refund-policy # 已交付的规格归档
命令名不是重点,节奏才是重点:规范 → 需求 → 方案 → 任务 → 实现。跳过中间任何一步,AI 就会回到"按一句话猜"的模式。
任务清单要能被勾选,否则它只是另一份待办清单:
# tasks.md
- [x] 定义退款状态枚举与话术映射
- [ ] 实现两类场景的意图识别
- [ ] 补充失败场景的人工入口链接
- [ ] 加测试:失败场景不得返回时效话术
5. 落地方式
5.1 规格进仓库,和代码同源
规格放在 specs/ 下,跟代码一个仓库、一个 PR 流程。这样"规格与代码是否一致"变成一次 code review 能看见的事,而不是靠人记。
# 变更的正确顺序:先改规格,再改代码
git switch -c feat/refund-eta
$EDITOR specs/refund-policy/spec.md # 1. 改规格
git commit -m "spec: 补充退款失败场景"
# 2. 让 AI 按新规格出方案与任务
# 3. 实现代码,勾选 tasks.md
git commit -m "feat: 退款失败场景话术"
5.2 用 CI 守住一致性
# 最小校验:改动代码却没动规格,就该被问一句
- name: spec-check
run: |
changed_code=$(git diff --name-only origin/main...HEAD | grep -c '^src/' || true)
changed_spec=$(git diff --name-only origin/main...HEAD | grep -c '^specs/' || true)
if [ "$changed_code" -gt 0 ] && [ "$changed_spec" -eq 0 ]; then
echo "代码有改动但规格未更新,请确认是否属于纯重构"; exit 1
fi
这条检查不追求绝对正确——纯重构本来就不该改规格。它的作用是让"忘了写规格"变成一次显式的判断。
5.3 把规格喂给 AI 的正确姿势
把整份 spec 丢给模型之前,先确定它读到了三样东西:术语表、已有接口约束、验收条目。缺了术语表,同一个词在模型眼里可以有三种含义;缺了接口约束,它会自己发明一套。
读 specs/refund-policy/spec.md 与 design.md。
约束:
1) 术语按 specs/glossary.md 的定义,不得自造;
2) 只改 RefundService 与话术模板,不动订单模型;
3) 每条验收项都要有对应测试,列在 tasks.md。
先输出方案与任务清单,等我确认后再写代码。
最后一句是关键:让模型先交方案,再交代码。一次生成到底的代码,几乎必然在某条验收项上跑偏。
6. 与其他做法的关系
| 载体 | 记录什么 | 与规格的关系 |
|---|---|---|
| 规格(spec) | 要做什么、边界、验收 | 主来源,行为的事实标准 |
| ADR | 为什么这么选(架构决策) | 引用规格,解释取舍 |
| Issue | 谁在什么时候做哪件事 | 从 specs 的 tasks 派生 |
| Wiki / 文档站 | 对外说明与教程 | 从规格改写,不反向约束 |
| 测试用例 | 行为是否成立 | 规格的机器可执行版本 |
别让它们互相替代:用 issue 记需求,三个月后 issue 关闭了需求就丢了;用 ADR 记需求,会把"要做什么"和"为什么这样设计"搅在一起。
7. 什么团队适合,什么不适合
| 情况 | 适合度 | 原因 |
|---|---|---|
| 需求相对稳定、交付周期长 | 高 | 规格的写作成本能被摊薄 |
| 多人协作、需要交接 | 高 | 单一书面来源减少口头传递 |
| 用 AI 大量生成代码 | 高 | 结构化输入直接提升生成质量 |
| 有合规/审计要求 | 高 | 变更留痕本身就是交付物 |
| 一次性脚本、内部小工具 | 低 | 写规格比写代码慢 |
| 探索期原型、方向天天变 | 低 | 规格会变成追着改的负担 |
| 单人项目、需求在脑子里 | 低 | 没有沟通损耗可省 |
判断口径:**规格的成本是"每次变更多写一段",收益是"每次交接少解释一轮"。**团队规模小、交接少,收益就撑不起成本。
8. 常见的六种失败形态
| 现象 | 原因 | 处理 |
|---|---|---|
| 规格写完就没人看 | 没有和代码建立引用 | 代码注释/PR 模板里引用 spec 路径 |
| 规格与代码脱节 | 改代码时跳过规格 | CI 加一致性检查,评审时对照验收项 |
| 规格越写越像设计文档 | 需求层塞了实现细节 | 需求层只写行为,方案放 design.md |
| 过程太重压过收益 | 每个小改动都走全流程 | 按变更规模分级:小改动只更新 spec 的一节 |
| AI 生成的规格空泛 | 没给上下文与约束 | 先给背景、术语表、已有接口 |
| 多份规格互相矛盾 | 没有唯一来源与索引 | 一份规格一个目录,交叉引用而非复制 |
最后一条最隐蔽:复制粘贴出来的第二份规格,从诞生起就在腐化。
9. 边界:什么时候别引入
| 场景 | 更合适 | 理由 |
|---|---|---|
| 几天就交付的一次性脚本 | 直接写,写清注释 | 流程成本高于维护成本 |
| 需求还在剧烈探索 | 先做原型,稳定后再补规格 | 规格会变成频繁重写的负担 |
| 已有成熟的需求管理流程 | 把规格层接进现有流程 | 不要并行两套 |
| 团队没有评审习惯 | 先把评审跑起来 | 没有评审,规格只是自说自话 |
| 纯粹的技术重构 | ADR + 测试 | 行为不变,规格无需改 |
一句话定位:规格驱动开发不是"多写文档",而是"把行为定义提前到代码之前,并让它成为可核对、可 diff、可交接的资产"。
10. 自测
- 需求散落在聊天记录里,会具体造成哪三类问题?
- 规格里的"不做"一节为什么比"要做"更值钱?
- 规范 → 需求 → 方案 → 任务 → 实现,跳过中间一步会怎样?
- 规格与 ADR、issue 各自的职责边界是什么?
- 什么样的团队不适合引入规格驱动开发?
- 规格与代码脱节,除了人工评审还能怎么守?