SDD 规格驱动开发简明教程

周五,六点。一位开发者在用编码智能体完成一个冲刺后合上了笔记本电脑。三十个生成的Markdown文件与通过代码审查并部署到生产环境的代码并排而立。

两周后,他回到了同一个功能。那些文件中没有一份反映现实。智能体添加了验证,有人在热修复中引入了租户作用域,一个库更改了其签名。上一个冲刺的Markdown已经是考古学了。

这并非因为他缺乏纪律性。这是他在不知不觉中选择的架构所固有的特征。

"我认为构建软件最困难的部分是对这个概念构造的规格说明、设计和测试,而不是表示它的劳动。" — Frederick Brooks,计算机科学家,《没有银弹》(1986年)

规格束之高阁不是纪律失败。它是大多数团队从未有意识地做出的架构决策的后果。正确的答案不是维护一份文档 — 而是将规格说明拆分为五种具有不同生命周期的文档类型。 这就是本文的主题。

1、PRD、SRS和LLD是为人类编写的

LLM需要不同的信息架构。

当你写下"系统应该对用户进行身份验证"时,你的团队成员知道你的意思。你们使用Postgres,你们切换到了JWT,三月份的一条Slack讨论决定了用Auth0。这个人能读懂言外之意,有疑问时会在咖啡机旁询问。

LLM没有咖啡,也没有Slack。智能体根据其训练数据中的统计分布来选择OAuth、JWT还是会话cookie — 偏向于2023年GitHub上流行的任何东西。这不是模型缺陷。这是接收者本质上的差异。

PRD、SRS和LLD是委员会时代的遗产。模糊性在它们中是一种特性 — 它为协商、走廊澄清和演示后的修订留下了空间。这些文档假设存在隐性知识:团队中填补空白的未成文知识。而给LLM的规格说明不能假设任何东西。

PRD与LLM规格说明的对比 — 这不是风格上的差异,而是本质上的差异。右栏中的每一层都关闭了一个缺口,而在左栏中,这些缺口默认由人类来填补。

关键洞察:这不是关于一个更好的PRD版本。 而是关于一个完全不同类别的文档。幻觉是合乎逻辑的结果:智能体收到了一份为拥有它所缺乏的上下文的读者设计的文档。

术语澄清:氛围编码(vibe coding) — 一位著名AI研究员在2025年提出的术语,描述在没有计划的情况下与智能体对话进行软件开发。规格驱动开发(SDD) 是一种工作流,你在开始指导AI之前,先编写一份描述"做什么"和"为什么"的文档。

结论:我们必须从零开始为LLM设计规格说明,而不是继承PRD和SRS的惯例。

2、七个信息层:跨领域需求

在我介绍具体的文档类型之前,有一个观察:无论你在编写哪种文档,它都必须包含七个信息层。 这些不是章节 — 它们是品质。每份SDD文档都以某种比例包含它们。

  • 第1层:语言精确性。 "系统应该加密密码"意味着一切也意味着虚无。"系统应使用bcrypt以≥12的开销因子加密所有密码"则是一个契约。EARS(Easy Approach to Requirements Syntax)— 一种由五种句子模式组成的格式,由劳斯莱斯于2009年开发 — 强制要求精确性。在LLM时代之前很久就被航空和汽车行业采用 — 事实证明这也是为智能体做的准备。
  • 第2层:负面需求。 没有明确的"系统不做X"这一节,智能体会添加社交登录,因为"这是最佳实践"。智能体默认的乐观主义是一个缺陷,而不是一个特性。
  • 第3层:可衡量的成功标准。 "实现JWT认证"意味着二十行或两百行代码。没有验收标准,智能体会随意决定。这是它知道何时停止的唯一方式。
  • 第4层:硬性技术约束。 什么是禁止的 — 库、模式和API。没有这一层,智能体根据训练数据中的流行度来选择技术 — 有时选择的库已经被废弃了两年。
  • 第5层:架构上下文 — 代码库锚定。 一份锚定在现有仓库中的规格说明:智能体知道具体的文件和依赖关系;它不会"在真空中"编写。没有锚定,智能体会构建一个平行世界 — 这就是它创建重复类然后试图"统一"的方式。
  • 第6层:作为显式场景的边缘情况。 没有命名的边缘情况,智能体只会生成快乐路径。这种模式随处可见:场景是被命名的,而不是暗示的。
  • 第7层:经过验证的库规格 — 反幻觉。 经过验证的库规格说明描述的是实际的API签名,而不是统计猜测。功能规格说明应包含具体的库版本和关键API约束。
七个信息层。它们不是章节,而是品质 — 五种文档类型(第3节)中的每一种都以不同比例包含它们。

2022年的独立研究发现,安全关键上下文中超过40%的代码存在漏洞 — 其中很大一部分可以直接追溯到这七个层级中某一层的缺失。这些层级是需求。下面的五种文档类型是表达它们的机制。

3、你需要的五种规格文档类型

要让智能体生成生产代码,你需要五种不同的文档。每种回答不同的问题,具有不同的生命周期,由不同的负责人维护。它们共同构成了与LLM的完整契约。

对于每种类型,我将介绍:用途生命周期必须包含的内容可复制的模板

3.1 项目宪章

用途。 团队与智能体之间的永久协议。不可协商的规则 — 安全、技术栈和基本架构模式。智能体在每次会话、每个层级都会读取。

生命周期。 数年。每季度编辑一次,语义化版本控制。宪章变更是可审计事件。

必须包含的内容。

  • 技术栈 — 允许列表(附具体版本)和禁止列表。一个条目就能省去数百次纠正。
  • 架构模式 — 必需的("数据库访问仅通过仓库层")和禁止的("禁止全局可变状态")。
  • 安全 — 锚定于既定标准(OWASP Top 10、CWE)的自定义规则。映射到CWE-ID使宪章成为可审计的合规制品。
  • 测试门禁 — 覆盖率阈值和测试分类(单元/集成/端到端)。最小化即可,因为智能体处理了大部分工作。
  • 代码风格底线 — 命名约定和目录结构。LLM默认处理大多数约定 — 不要过度复杂化。
  • 执行级别 — CRITICAL(阻止合并)、SHOULD(警告)和MAY(信息性)。

模板。

# 项目宪章 — vX.Y.Z

## 第一条 — 技术栈 [CRITICAL]
ALLOWED: TypeScript ≥5.3, React ≥18, PostgreSQL ≥15
FORBIDDEN: jQuery, class components, CommonJS modules

## 第二条 — 架构模式 [CRITICAL]
系统应仅通过仓库层访问数据库。
在控制器中直接调用ORM是PROHIBITED的。

## 第三条 — 安全 [CRITICAL]
系统应对所有数据库查询进行参数化(CWE-89)。
系统应使用bcrypt以cost ≥12对密码进行哈希(OWASP A02)。

## 第四条 — 测试门禁 [SHOULD]
变更文件的覆盖率在合并前≥80%。

## 第五条 — 代码风格 [MAY]
仓库模式: src/repositories/{Entity}Repository.ts

九到十二条。不少于(太抽象),不多于(合规过载 — 智能体会迷失在指令中)。

3.2 功能规格说明

用途。 描述单个功能的"做什么"和"为什么"。用户得到什么、为什么,以及你如何验证它已完成。

生命周期。 按功能分支。合并后可以消亡(故意为之)— 这是它的权利。

必须包含的内容。

  • 业务目标 — 一段话。为什么存在这个功能。
  • 用户故事 — 作为{角色},我希望{操作},以便{收益}。
  • 功能需求 — 使用EARS格式(SHALL)。编号:FR-001和FR-002。
  • 成功标准 — 可衡量、可验证。SC-001:"登录在<2s p95内完成。"
  • 非目标 — 这个功能不做的事情。没有这一节,智能体会添加功能。
  • 边缘情况 — 显式、命名的。"如果登录失败5次,则账户被锁定15分钟。"
  • 代码库锚定 — 哪些文件、哪些模式、在哪里接入。
  • 开放问题 — 一个"[NEEDS CLARIFICATION]"部分用于标记模糊之处。让智能体停下来比让它猜测更好。

模板。

# 功能规格说明: {名称}

## 目标
[1段 — 为什么需要这个功能]

## 用户故事
- US-1: 作为注册用户,我希望使用邮箱+密码登录
        以便我能访问我的账户。

## 功能需求
FR-001: 系统应通过邮箱+密码对用户进行身份验证。
FR-002: 当登录失败5次时,系统应锁定账户15分钟。

## 成功标准
SC-001: 95%的登录在<2s内完成。
SC-002: 登录失败不应透露邮箱是否存在。

## 非目标
- 社交登录(计划v2)
- 密码重置流程(单独功能,FR-XXX)

## 边缘情况
- 如果邮箱存在但未确认,则阻止登录 + 发送确认邮件。
- 如果账户被锁定,则返回423 + Retry-After头。

## 代码库锚定
- src/auth/AuthService.ts — 扩展
- src/middleware/rateLimit.ts — 复用模式
- src/db/schemas/User.ts — 无变更

## [NEEDS CLARIFICATION]
- 2FA策略: TOTP vs SMS vs 无 [NEEDS CLARIFICATION]

3.3 技术计划

用途。 描述"怎么做"。架构决策、带版本的库、实现阶段和风险。这是智能体在开始生成之前阅读的文档 — 以了解它所工作的技术上下文。

生命周期。 按功能,比功能规格说明更短。执行后,它成为历史记录。

必须包含的内容。

  • 技术栈和版本 — 具体的。不是"Node ≥18",而是"Node 20.11 LTS"。
  • 架构决策 — 关键选择及其理由。如果选择重大,链接到单独的ADR。
  • 依赖 — 哪些库、哪些版本,以及为什么选这些而不是其他。
  • 阶段/里程碑 — 实现顺序。
  • 性能目标 — 具体的。"1000 req/s p95 <200ms,每个实例<100MB内存。"
  • 风险 — 什么可能出错以及你将如何检测到。

模板。

# 技术计划: {名称}

## 技术栈
Node 20.11 LTS, TypeScript 5.4, Fastify 4.26, PostgreSQL 16.

## 架构决策
- AD-1: JWT (jose) + httpOnly cookie. 刷新令牌存储在单独的表中。
        理由: 无状态API,但可以通过DB删除实现令牌失效。
- AD-2: bcrypt (cost=12) 而不是 argon2。
        理由: 宪章第三条,OWASP A02。

## 依赖
- jose@5.2 (JWT)
- bcrypt@5.1 (哈希)
- @fastify/rate-limit@9.1 (现有模式)

## 阶段
1. 数据库迁移 (users.password_hash, users.locked_until)
2. AuthService (注册/登录/锁定逻辑)
3. 路由 + 中间件
4. 测试 (单元 + 集成)

## 性能目标
- 登录 p95 <2s, p99 <5s.
- bcrypt cost=12 → 每次哈希约250ms。可接受。

## 风险
- bcrypt阻塞事件循环 → 如果p95 >2s则使用工作线程。
- 端点上的暴力破解 → 速率限制已存在,但需要审计规则。

3.4 任务列表

用途。 原子工作单元。每个任务都是确定性的、可验证的,可独立提交。智能体逐个执行列表中的任务,每次使用全新的上下文。

生命周期。 按功能。执行后,它成为git中的历史(每个任务一次提交)。

每个任务必须包含的内容。

  • ID + 名称 — T001和T002。简短、可操作的标题。
  • 文件路径 — 它涉及哪些文件。
  • 操作 — 具体要做什么。如果可能,提供完整代码。
  • 验证 — 确认任务完成的命令或测试。
  • 完成定义 — 可衡量的。
  • [P] — 并行安全任务(独立任务)的标记。
  • 依赖 — 有依赖关系任务的顺序排列。

模板。

# 任务列表: {名称}

## T001: Users数据库迁移
**文件:** db/migrations/2026_05_15_login.sql
**操作:** ALTER TABLE users ADD COLUMN password_hash TEXT NOT NULL,
          ADD COLUMN locked_until TIMESTAMP NULL.
**验证:** psql -c "\\d users" — 显示新列。
**完成:** 迁移在本地DB上运行;回滚通过。

## T002 [P]: 测试 AuthService.hashPassword
**文件:** src/auth/__tests__/AuthService.test.ts
**操作:** [完整测试代码,包含bcrypt cost=12 + 空密码的边缘情况]
**验证:** npm test -- AuthService.test
**完成:** 测试失败(TDD的RED阶段)。

## T003 [依赖: T002]: 实现 AuthService.hashPassword
...

[P]标记允许智能体在没有依赖关系时并行执行任务。每个任务一次原子提交 — 使回滚和审查更容易。

3.5 变更规格说明 — 增量和错误修复

用途。 描述对现有系统的变更。可以是迭代(增量)或缺陷修复(bug修复)。格式是语义差异,而不是完整的重新规格说明。

生命周期。 按变更。带时间戳归档(审计跟踪)。

增量必须包含的内容。

  • ADDED — 新需求或功能。
  • MODIFIED — 变更的需求,以"旧→新"格式。
  • REMOVED — 弃用的需求。
  • 对其他组件的影响 — 系统的哪些部分需要审查。

模板 — 增量。

# 增量: 为登录添加2FA (2026-05-20)

## ADDED
- FR-010: 当用户启用2FA时,系统应在每次登录时要求TOTP验证码。
- SC-010: TOTP设置流程端到端在<60s内完成。

## MODIFIED
- FR-001 (先前版本): "系统应通过邮箱+密码对用户进行身份验证"
- FR-001 (新版本): "系统应通过邮箱+密码对用户进行身份验证,
                    如果启用了2FA,则随后要求TOTP验证。"

## REMOVED
- (无)

## 影响
- AuthService.login — 添加2FA挑战步骤。
- LoginRoute — 添加步骤状态机。

错误修复必须包含的内容。

  • 当前行为 — 系统 NOW 的行为方式(不正确的)。
  • 预期行为 — 修复后应如何表现。
  • 不变行为 — 哪些必须保持不变。最重要的部分。 没有它,修复会在远程模块中引入回归。
  • 复现步骤 — 重现问题的步骤。
  • 根本原因 — 原因,而不仅仅是症状。

模板 — 错误修复。

# 错误修复: 速率限制器忽略X-Forwarded-For (BUG-2026-042)

## 当前行为
当请求通过负载均衡器到达时,速率限制器使用
  负载均衡器的IP,而不是终端客户端的IP → 所有客户端共享
  一个IP的限额。

## 预期行为
当请求包含来自受信任代理的X-Forwarded-For头时,
  速率限制器应使用原始客户端IP。

## 不变行为
- 当请求没有X-Forwarded-For时,速率限制器使用
  socket IP(与当前相同)。
- 当X-Forwarded-For来自不受信任的源时,忽略它
  (安全:防欺骗保护)。

## 复现步骤
curl -H "X-Forwarded-For: 1.2.3.4" localhost/login (循环执行) →
  第一个客户端在5次尝试后被阻止(应为:各自独立的限额)。

## 根本原因
速率限制中间件在配置中没有trustedProxies列表。

不变行为部分是区分"修复了问题的bug修复"和"引入了新问题的bug修复"的关键。没有它,智能体会将整个模块视为"需要重写"。

五种文档类型。每种回答不同的问题,具有不同的生命周期,由不同的负责人维护。它们共同构成了与LLM的完整契约。

五份文档看起来像是额外开销。它们不是 — 因为每份文档只有一个职责和一个生命周期。试图将所有这些塞进一个文件中,产生的恰恰是让开发者头疼的漂移混乱。

4、生命周期:什么消亡,什么存活

在每个国家,宪章很少变更,而普通法律每周都在变。你的项目需要同样的分离。 宪章:数年,罕见变更,语义化版本控制。按功能的三元组(功能规格说明、计划、任务):数周,合并后故意消亡。变更规格说明:带时间戳的归档和审计跟踪。

Thoughtworks的Birgitta Böckeler(2025年10月)引入了关于合并后规格说明发生什么的经典分类法:规格优先(spec-first)、规格锚定(spec-anchored)和规格即源(spec-as-source)。大多数开发者通过工具选择而非刻意决策,不知不觉地选择了第一条路径。本文讲述的是规格优先。

在规格优先模型中,按功能的三元组(规格+计划+任务)在合并后故意消亡 — 这是它的权利。宪章存活下来。变更规格说明被归档。澄清一下:另外两条路径是规格锚定(规格与代码并存)和规格即源(代码是编译器的二进制输出)。两者都是小众用法,不在本文讨论范围内。

五种规格文档类型的生命周期。宪章经受一切。按功能的三元组故意消亡。变更规格说明被归档 — 可用于审计,不会膨胀对话上下文。

Yegge在2025年花了一个月时间淹没在数百个Markdown文件中。没有一个被再次阅读。那不是因为缺乏纪律性 — 而是规格优先路径缺少一个专门的持久层。 一份能够存活下来的宪章。

机制很简单。宪章在每次会话开始时被智能体读取 — 一次基于文件的交接,独立于对话上下文。它经受住了上下文腐化(随着上下文窗口填满,生成质量下降),因为它不在对话历史中。文件在第二十次会话中与第二次会话中一样可读。

5、决策树:为你的团队选择规格组合

五种类型是完整集合。大多数团队不需要从一开始就使用全部五种。

  • 个人/小团队/新项目: 宪章(轻量级,5-7条规则)+ 功能规格说明(与技术计划合并为一个部分)+ 任务列表。变更规格说明仅作为bug修复模板,仅当项目超过6个月时才使用增量。大多数情况。
  • 个人/棕地/兴趣项目: 最小宪章(3-5条规则 — 主要是技术栈)+ 临时功能规格说明。更多时候重写而非维护。跳过正式的任务列表。
  • 团队/长期项目: 所有五种类型。扩展的宪章(9到12条规则),技术计划中的正式阶段,带有[P]标记的显式任务列表,以及每次迭代的增量规格说明。边缘情况被显式命名,因为没有任何智能体会原谅你在远程模块中的回归。
  • 企业/受监管行业(金融科技、医疗、国防):所有五种类型 + 映射到既定标准(OWASP、CWE)的重型宪章,作为每次生成的硬性门禁。在这里,仅规格优先作为唯一模式已经不够 — 但这是另一篇文章的主题。就本文而言,宪章是无论其他选择如何都将整个制度维系在一起的基础。

SDD决策树。大多数团队落在个人/新项目 → 轻量级宪章 + 功能规格说明组合 + 任务列表。受监管领域和棕地项目需要所有五种类型配合重型宪章。

走一遍决策树只需要两分钟。这个决策可以在未来两年中节省三十小时对抗规格漂移的时间。跳过这个决策的团队得到的是工具的默认组合 — 然后纳闷为什么审计师不接受Slack讨论作为合规文档。

6、结束语

让我们回到周五六点的那位开发者。两周后,他的仓库有五种文档类型:一份包含九条规则的宪章,智能体在每次会话中都会读取;一个按功能的三元组(功能规格说明、技术计划和任务列表),在合并后故意消亡;以及一个增量归档,保留了变更历史。上一个冲刺的Markdown仍然是考古学 — 但在归档中,而不是在根目录中。一旦你接受按功能的规格说明注定要消亡 — 并将必须存活的内容分离出来 — 规格束之高阁就不再是问题了。

带走五件事:

  • PRD、SRS和LLD是委员会时代的遗产 — 从零开始设计信息架构。
  • 七个信息层是跨领域需求 — 它们以不同比例出现在所有五种文档类型中。
  • 你需要的五种规格文档类型: 宪章、功能规格说明、技术计划、任务列表和变更规格说明(增量+错误修复)。每种只有一个职责和一个生命周期。
  • 宪章比其他一切存活得都久 — 这是对规格束之高阁问题的经验性答案。
  • 热修复友好的SDD不存在,这是一个事实,不是一个缺陷。宪章就是你的热修复门禁。

规格优先的解剖图,一图概括。五种具有不同生命周期的文档类型。宪章存活 — 其他一切都可以故意消亡。

从宪章开始。九到十二条规则,每季度编辑一次。然后为第一个真正的功能创建按功能的三元组。当第二次合并到来时添加变更规格说明。三十分钟的决策可以节省未来两年中三十小时对抗漂移的时间。


原文链接: SDD — Designing a Spec That Survives Code Generation — Spec-First — Spec-Driven Development

汇智网翻译整理,转载请标明出处