[SDD 方法论]系统化实践指南

Version: v1.0 | Last Updated: 2026-06-27

本文档定义 SDD(规约驱动开发,Spec-Driven Development)的通用方法论。

目标读者:AI Agent(如 Claude Code)和人类开发者。

本文档可直接拷贝到任何采用 SDD 方法论的新工程中使用。


一、核心理念

1
2
3
Specs(什么是对的)→ 开发上下文(代码怎么写)→ 制定计划 → 代码实现
↑ ← (行为变化时回写) |
└───────────── 测试验证 + 文档同步 ←──────────────────┘

SDD 的三层体系:

层级 角色 修改频率
Specs 行为契约——定义”什么是对的” 很少(需求变更时才改)
开发上下文 架构知识——描述”代码怎么写的” 随代码演进
Code 实现——实际运行的代码 日常

铁律

  • Specs 是最高权威。如果代码行为与 specs 冲突,以 specs 为准,改代码。
  • Specs 是活文档。需求变更或 specs 不充分时,优先更新 specs,再动手。

二、开发工作流

2.1 接到新任务时

第一步:确定影响范围

问自己三个问题:

  1. 改动涉及哪个模块?
  2. 涉及哪个外部接口/格式?
  3. 是否跨模块?(跨模块改动风险最高,需特别小心)

第二步:读 spec,评估充分性

⚠️ Specs 是活文档。 读 spec 时带着批判眼光:

  • Specs 是否覆盖了当前场景?定义是否清晰无歧义?
  • Specs 的行为定义是否合理、内部是否一致?
  • 如果不充分或不合理 → 优先更新 specs,再往下走。 不在不稳固的基础上盖楼。

Code ≠ Spec 处置流程: 当发现代码行为不在 spec 覆盖范围内:

  1. 判断当前行为是否合理
  2. 合理 → 补 spec 使之成为正式契约,代码不动
  3. 不合理 → 按 spec 精神修正代码(此时 spec 虽未明确但精神可循),同步补 spec

关键原则:永远不让”spec 没写”成为行为模糊的借口。没写就是缺失,缺失就要补。

第三步:对照开发上下文文档

读开发上下文文档(如 CLAUDE.md)的相关章节,重点关注:

  • Known Gotchas(常见陷阱汇总)
  • Critical Implementation Details(关键实现细节)

第四步:制定开发计划

读完 specs(知道”什么是对的”)和开发上下文文档(知道”代码怎么写的”)之后,显式制定开发计划再写代码:

  1. 列出涉及的所有文件:哪些需要创建、修改、删除
  2. 确定改动顺序:按依赖关系排定先后(先改基础接口,再改上层调用)
  3. 识别风险点:可能影响哪些现有功能?哪个环节最容易出错?
  4. 复杂任务先规划:跨模块改动或新增能力时,先出完整方案再动手

先计划,再实现——避免在实现中途发现架构冲突、推倒重来。

2.2 提交前

  1. 跑测试(必须通过)
  2. 格式检查(lint / format)
  3. 测试与 spec 的关系:测试是 spec 的可执行验证。每个 spec 定义的行为契约都应该有对应的测试用例覆盖。如果 spec 改了而测试没改,说明 spec 更新不完整——改 spec 的同时必须更新或新增测试。反之,如果一个行为已有测试但没有 spec 覆盖,应将行为补入 spec(或标记为实现细节,不需要契约化)。
  4. 文档同步检查(每次改动必做):
优先级 文档 检查条件 操作
P0 Specs 行为发生变化(接口、契约、数据流) 必须同步更新
P1 开发上下文文档 新增架构细节、新陷阱、新硬约束、新关键实现 同步更新
P1 README API 变化、新增功能入口、安装步骤变化 同步更新用户文档
P2 示例代码 用户 API 变化、调用方式变化 更新示例

Git commit 格式(Conventional Commits):

1
2
3
<type>(<scope>): <subject>

<body if needed>

类型:feat / fix / docs / refactor / test / style / chore / perf / build / ci


三、代码审查检查清单(通用)

每次改动后自查:

  • 架构硬约束未被违反(模块间依赖规则、分层约束)
  • 新增函数/类有对应的测试
  • 测试全部通过
  • 行为变化已同步更新 specs(P0)
  • 新增架构细节/陷阱已同步更新开发上下文文档(P1)
  • API / 功能入口变化已同步更新 README(P1)
  • 用户接口变化已同步更新示例代码(P2)

四、Specs 目录结构

4.1 核心原则:WHAT 与 HOW 分离

Specs 目录按两层组织——WHAT(外部契约)和 HOW(内部架构)。这是 SDD 最重要的结构设计。

1
2
3
4
5
6
7
8
9
10
11
12
specs/
├── <contract-layer-1>/ # WHAT — 外部数据/接口契约
│ └── index.md

├── <contract-layer-2>/ # WHAT — 其他契约层(可选)
│ └── index.md

└── modules/ # HOW — 内部模块架构
├── index.md
├── spec_<module-1>.md
├── spec_<module-2>.md
└── ...

WHAT 层的命名取决于项目领域:

项目类型 建议命名 示例内容
数据处理/格式转换 formats/ 数据格式定义、转换规则
Web API api/ REST/GraphQL 接口契约
库/SDK interfaces/ 公开 API 签名、类型定义
评估/评测 evaluate/ 指标定义、基准线
协议/通信 protocols/ 消息格式、状态机

WHAT 层可以有多个(如 formats/ + evaluate/),每个定义一类外部契约。HOW 层只有一个 modules/,与代码模块一一对应。

4.2 每层 index.md 模板

每个 spec 子目录都需要一个 index.md 作为入口。推荐结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# <Layer Name> — Specification Index

> **Status:** Canonical — these documents define the authoritative
> <contract-type> for <project-name>.

## What This Layer Covers

简短说明这一层定义了什么,以及它是谁的"ground truth"。

## Documents

| # | Document | Purpose |
|---|----------|---------|
| 1 | `spec_xxx.md` | 一句话描述 |
| 2 | `spec_yyy.md` | 一句话描述 |

## Relationship to Other Layers

用 ASCII 图说明本层与其他 spec 层的关系:
- 本层(WHAT)对应 modules/ 中的哪个模块(HOW)
- 本层与哪些其他层独立

## Reading Order

按角色/任务给出推荐阅读顺序:
- 新手上路 → 先读什么
- 特定任务 → 读什么

4.3 WHERE 原则

每个 spec 文件只回答一个问题:

文件类型 回答的问题
数据格式/协议 spec 这个外部契约长什么样?定义了什么必填字段?
转换/适配 spec A 怎么变成 B?边界条件怎么处理?
模块 spec 这个模块的公开接口和行为契约是什么?

如果一个 spec 文件同时回答了”数据长什么样”和”代码怎么实现”,就应该拆开。

4.4 Spec 版本管理

Specs 是活文档,需要版本追踪来回答”这份 spec 是什么时候改的、为什么改”。

最低要求:

  • 每个 spec 文件开头标注版本号和最后更新日期
  • 行为变化时同步更新版本号
  • 版本号与 CHANGELOG 条目双向引用(commit 写 “docs(spec): …”,CHANGELOG 记 spec 变更)

推荐格式:

1
2
# Spec: <Title>
> **Version:** vX.Y | **Last Updated:** YYYY-MM-DD

版本号策略:

场景 版本变化
新增定义/扩展现有契约 小版本递增(v1.0 → v1.1)
行为变更(breaking change) 主版本递增(v1.2 → v2.0)
澄清/措辞修正(不改变行为) 加日期、不改版本号

五、开发上下文文档模板

5.1 概述

开发上下文文档(如 CLAUDE.md)是 SDD 三层体系的中间层——连接 specs(什么是对的)和代码(具体实现)。它的读者是 AI Agent 和开发者,目的是降低上手成本、防止踩坑

5.2 推荐结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# CLAUDE.md (或 CONTRIBUTING.md / DEVELOPER_GUIDE.md)

## 1. Project Overview
一句话说清项目是什么、解决什么问题。

## 2. Specifications
指向 specs/ 目录,声明 specs 的权威地位,说明 specs vs 本文档的关系。

## 3. Architecture
- 模块依赖图(ASCII art)
- 架构硬约束表(# | 约束 | 违反后果)
- 数据流管道(输入 → 处理 → 输出)

## 4. Critical Implementation Details
集中记录"最容易出错的地方"。以下各领域的典型陷阱:

| 领域 | 常见陷阱示例 |
|------|------------|
| 数据编码约定 | 数值精度(金额/金融)、字符集(后端)、字节序(网络) |
| 序列化规则 | binary ↔ text 转换用哪种编码、浮点数精度处理 |
| 状态生命周期 | 连接池状态机、缓存失效策略、会话过期逻辑 |
| 并发边界 | 哪些操作线程安全、锁的粒度、异步上下文传播 |

- 每个细节用代码片段 + ❌/✅ 对比说明

## 5. Development Commands
- 安装
- 测试(含单文件/单用例示例)
- Lint/Format
- 手动验证命令

## 6. Known Gotchas
常见陷阱清单,每条一句话标题 + 一句话解释。
按影响面排序(最常踩的排前面)。

## 7. Test Structure
测试目录布局,让开发者知道测试放哪里、测试数据放哪里。

5.3 编写原则

原则 说明
时效性 随代码演进同步更新,不是一次写完就不管
具体性 写”连接超时设为 30s”而非”合理设置超时”;写”密码必须 bcrypt 哈希后存储”而非”安全存储密码”
可验证 每条 Critical Detail 应该能对应到一条测试或用例
分层清晰 本文档描述”怎么做”,不重复 specs 的”是什么”
长度控制 目标 200-400 行。超过 400 行考虑是否有内容应该下沉到 spec 或上升为通用文档

六、Known Gotchas 编写规范

6.1 为什么需要

Known Gotchas 是开发上下文文档中投入产出比最高的部分——一行警告可以节省数小时的调试时间。它记录的是理论上不该出错、实际上反复有人踩的陷阱。

6.2 条目格式

1
2
3
4
5
6
7
8
9
10
序号. **问题关键词**:现象 + 正确做法。具体细节。

反例:
- "注意编码" → 没用,不知道注意什么

正例:
- "Secrets: Always load from environment variable, never hardcode in source files."
→ 场景(凭据管理)、错误(硬编码)、正确(环境变量)、原因(安全性 + 配置分离)
- "DB connections: Use connection pool (min=5, max=20), never create per-request."
→ 场景(数据库访问)、错误(每次新建)、正确(连接池)、原因(连接开销可控)

6.3 条目来源

来源 触发条件
Bug 修复 每次修 bug 后检查:这条是否可预防?是 → 新增 Gotcha
新人上手 新人遇到的每个阻碍都是潜在的 Gotcha
代码审查 审查中反复指出的问题类型
架构决策 违反后果严重的架构约束

6.4 维护规范

  • 踩坑频率排序,最常见的排最前面
  • 每条 Gotcha 与相关 spec / Critical Detail 保持双向引用
  • 如果一条 Gotcha 对应的 bug 已被架构重构根除,删除它(避免腐化)
  • Gotchas 数量没有硬性上限,但超过 20 条时考虑是否有一些应该升级为 Critical Detail

七、文档语言策略

7.1 核心原则

SDD 中的术语和解释需要不同的语言策略:术语必须和代码一致,解释可以用开发者的母语。 这条原则对所有非英语母语的开发者适用——中文、韩文、日文、法文……逻辑相同。

内容类型 语言 原因
术语、字段名、API 签名 English 必须和代码中的符号精确匹配,否则不可 grep
公式、算法描述 English 数学/代码符号不可翻译
代码引用(类名、函数名、路径) English PaymentGateway 不是 支付网关,否则搜索不到
解释、背景、”为什么” 随意 提升理解效率,用最熟悉的语言写最复杂的逻辑
注意事项、陷阱警告 随意 清晰度优先,出错的代价远高于语言的统一
纯工程架构描述 随意 modules/ 可以全英文(结构清晰,不涉及复杂领域知识)

7.2 分层推荐

对大多数非英语母语团队,推荐按 spec 层级选择语言密度:

1
2
3
4
5
6
7
8
9
10
11
12
13
specs/
├── SDD_METHODOLOGY.md # 混合 — 可跨工程拷贝,接收方可自行英文化
├── SDD_GUIDE.md # 混合 — 工程特有,需深入理解,混合语言最佳

├── <contract-layer-1>/ # 混合 — 术语 English,解释母语
│ ├── index.md # 复杂领域知识最适合混合学习
│ └── spec_xxx.md

├── <contract-layer-2>/ # 混合 — 同上
│ └── ...

└── modules/ # English — 纯架构设计,术语自明
└── spec_xxx.md

为什么 modules 可以全英文? 架构描述天然是术语密集型——类名、方法签名、依赖方向本身就是英文。母语翻译反而引入歧义(”用户服务” vs UserService——哪个更精确?)。

为什么 WHAT 层推荐混合? 领域知识(数据格式规范、评估指标定义、API 契约)涉及抽象概念,母语解释能显著降低学习曲线。但所有术语、字段名、公式保持英文。

7.3 反例与正例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❌ 全母语术语:
"支付结果用字符串存金额,单位是分,别用小数。"
→ PaymentResult 叫什么?amount 字段名是什么?grep 什么?

✅ 术语保持英文,解释用母语:
"PaymentResult.amount 为 string 类型,单位为分(cent),禁止 float。"
→ 每个术语都能直接 grep:PaymentResult、amount、string、cent、float。

❌ 模糊的行为描述:
"请求失败时重试几次就行。"
→ 几次是几次?间隔多长?失败的定义是什么?

✅ 可 grep 的精确描述:
"HTTP 请求失败(≥500 或 timeout)时指数退避重试(max_retries=3, backoff=1s/2s/4s)。"
→ 每个参数都能搜到,行为无歧义。

八、适配新工程

将本方法论应用到新工程时,推荐按以下顺序完成初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 创建 specs/ 目录结构(WHAT 层 + HOW 层)
└── 参考第四章:Specs 目录结构

2. 为每层写 index.md
└── 参考第四章:每层 index.md 模板

3. 写出第一个 spec(通常是核心数据格式或 API 契约)
└── 参考第四章:WHERE 原则

4. 创建开发上下文文档(CLAUDE.md)
└── 参考第五章:开发上下文文档模板

5. 确定文档语言策略
└── 参考第七章:文档语言策略

6. 随着开发推进,持续补充:
├── Critical Implementation Details
├── Known Gotchas(参考第六章)
└── 工程特有的开发场景速查和检查项

建议创建一份工程特有的开发指南文档(如 SDD_GUIDE.md),放在 specs/ 目录下,引用本方法论并补充工程特有内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# SDD 开发指南

> 通用方法论见 SDD_METHODOLOGY.md。本文档聚焦 <工程名> 的特有内容。

## 一、工程架构
- 模块体系(具体模块名 + 依赖图)
- 架构硬约束
- 外部接口/格式清单

## 二、开发工作流(工程特有补充)
- 第一步模板(具体模块名/格式名)
- spec 映射表(改动类型 → 对应 spec)
- 工程特有实现细节(数据编码约定、序列化规则、状态机等)

## 三、Specs 导航地图
- "我要找什么?" → spec 快速索引
- 完整文件清单

## 四、常见开发场景速查

## 五、工程特有检查清单

## 六、参考