2026-07-18-简谈SDD(规格驱动开发)

4759 个字
24 分钟
2026-07-18-简谈SDD(规格驱动开发)

关于规格驱动开发(SDD)#

规格驱动开发(SDD)是一种以“规范文档”为核心的工程方法论。它和普通的 AI Coding 最大的区别在于普通 AI Coding 更多是在“帮开发者写一段代码”,而 SDD 更接近于“让 Coding Agent 基于一份完整的规格说明,主动完成一项开发任务”。

SDD的核心理念可以概括为:

  • 规范第一,代码第二:规范不再是代码的附属文档,而是开发的起点和核心
  • 意图驱动:开发活动应以“业务目标和用户需求”为基础
  • 可执行规格:规格文档可以自动生成代码、API合约和测试用例
  • 持续精炼:规格文档在整个生命周期中不断完善

也就是说,SDD 的核心是先把需求、技术方案、边界条件、验收标准等内容先写清楚,再让 Coding Agent 基于这些上层信息去理解需求、拆分任务、修改代码,最终交付一个可以编译通过、主体功能可用的版本。与传统开发相比,SDD实现了权力反转:过去是“代码为王”,现在是“代码服务规格”。规格足够精确时,它可以直接生成计划与实现,减少意图与落地之间的鸿沟。

为什么推荐 SDD 工作模式#

当前大部分软件开发,本质上仍然是一种流水线模式。对很多研发来说,每天的工作流程大致是:从产品、设计或上游研发那里获取需求文档,理解需求之后进入开发阶段,最后再交付一个可以测试的版本给 QA。这个过程会不断循环,构成了日常研发工作的基本节奏。在这种模式下,研发真正追求的是两件事:

第一,更快地处理需求。 第二,同时处理更多事情。

而 SDD 工作模式刚好能够提升这两点。其方法论主要有调研先行(在“古法编程”里也有这个基本要求hhh)和并行开发(开一堆shell,发挥 SDD 工作模式并行处理能力的优势)

简单来说的话,SDD实践的话我觉得要做到两点:

  • 先调研,先增加自己的 Context,再写 Spec,再指挥 Agent 开发。
  • 主需求 + 优化需求,或者 大需求 + 小需求的拆分,让主需求正在由 Agent 实现的时候,研发可以同时让另一个 Agent 做一些相对独立的事情

SDD 主流程图#

Spec Kit 框架: SDD 的实践工具#

文档站点:https://github.github.io/spec-kit/

Spec Kit 是 GitHub 官方开源的 SDD 工具包,把 AI 辅助编程组织成一套可控的结构化流程。它做三件事:与 Copilot、Claude、Gemini 等主流 Agent 集成;把自然语言需求逐层解析成规格、方案和实现计划;内置一套"宪法式"架构原则约束整个开发流程。

安装与初始化#

Terminal window
# 安装 CLI
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@vX.Y.Z
# 初始化项目
specify init my-project --integration copilot
cd my-project

升级相关命令:

Terminal window
specify self check # 只读检查是否有新版
specify self upgrade --dry-run # 预览升级动作
specify self upgrade # 就地升级到最新稳定版
specify self upgrade --tag vX.Y.Z[suffix] # 锁定到指定发布标签

specify self upgrade 会像 pip install -U 一样直接执行,底层对 uv tool 安装走 uv tool install --force,因此锁定 dev、alpha/beta/rc 等标签同样有效;uvx 临时运行和源码检出会被自动识别,只给建议不实际安装。需要限时可设 SPECIFY_UPGRADE_TIMEOUT_SECS

完整工作流#

Spec Kit 把开发拆成七个前后衔接的步骤,每一步对应一个斜杠命令(大多数助手用 /speckit.*;Codex CLI 技能模式用 $speckit-*;Copilot CLI 用 /agents 选择助手)。

步骤命令作用产物
1/speckit.constitution确立代码质量、测试、体验、性能等全局准则constitution.md
2/speckit.specify只描述"做什么"和"为什么",不谈技术栈spec.md
3/speckit.clarify自动追问不确定点,把返工前置为提问更新 spec.md
4/speckit.plan给定技术栈,生成架构与实施计划plan.md / data-model.md / contracts/
5/speckit.tasks拆成带依赖关系的可执行任务清单tasks.md
6/speckit.analyze实现前校验规格/计划/任务是否一致冲突报告
7/speckit.implement按 TDD 节奏逐个执行任务、生成代码源码

一个典型的照片管理应用,四条核心命令大致长这样:

Terminal window
/speckit.constitution Create principles focused on code quality, testing standards, UX consistency, and performance
/speckit.specify Build an app to organize photos into albums grouped by date, reorderable by drag-and-drop, no nested albums, tile preview inside each album.
/speckit.plan Use Vite with minimal libraries, vanilla HTML/CSS/JS, images stay local, metadata in a local SQLite DB.
/speckit.tasks
/speckit.implement

各阶段产物示例#

constitution.md——把不可协商的规则固化下来,Agent 生成代码时自动遵守:

### 代码质量
- 每个 PR 必须通过自动化 lint / 格式化,合并前强制检查
- 公共 API 保持最小化,附带示例和输入/输出文档
- 复杂或跨文件的架构决策必须在 PR 里附简短设计说明
### 测试标准
- 鼓励测试优先;每个新功能至少含单元测试 + 跨组件集成测试
- CI 跑单元/集成/冒烟测试,失败不得合并
- 不稳定测试立即标记、隔离、修复,不得长期忽略

spec.md——把需求规格化为带优先级和验收场景的用户故事:

### 用户故事 1:按日期创建和查看相册 (P1)
作为用户,我希望照片自动按日期分组,以便按日/月浏览。
验收场景:
1. 给定 2025-11-06 和 2025-11-07 的照片,打开应用应看到两个对应日期的相册
2. 无时间戳的照片归入"未知日期"相册,并支持手动指定

plan.md——产出项目结构与分阶段实施计划:

specs/1-organize-photos/
├── plan.md research.md spec.md
├── data-model.md quickstart.md
├── contracts/api.md
└── tasks.md (由 /speckit.tasks 生成)
## Phase 2:实施计划
- [ ] T004 用 sql.js 实现数据库层 + 文件持久化垫片 [性能]
- [ ] T010 主相册视图,支持拖放排序和键盘备用方案 [体验][测试]
- [ ] T011 相册视图,平铺网格 + 虚拟化 + 增量加载 [性能][测试]

配套的 contracts/api.md 定义本地 IPC 接口(渲染进程 ↔ 主进程),data-model.md 定义实体结构,例如 Photoid / source_path / timestamp / checksum 等字段,并在 (checksum, user_id) 上加唯一约束去重。

tasks.md——按阶段拆分、标注依赖和并行标记 [P]:

## 阶段 1:设置
- [x] T001 初始化项目骨架(package.json、Vite、Electron 入口)
- [x] T002 CI 骨架 + lint/test 步骤 [代码质量]
- [x] T004 Vitest / Playwright 配置 [测试]
## 阶段 2:基础
- [ ] T007 [P] 用 sql.js 实现数据库模式和迁移表,提供 initDb()

最后 /speckit.implement 按任务顺序逐个执行,严格走 TDD,把规格真正落成代码。


monorepo中的 SDD 实践#

到了 monorepo——尤其是那种多运行时、web 和其他端共用一堆 packages 的仓库——第一个绕不开的问题就是 到底该放哪。

目前个人比较推荐的一种分法是按信息的归属维度来切,全局 spec 集中存放,包级 README 就近归档。

项目中spec文档描述的东西会有两种区分,「一个功能 / 一次架构决策」或者「一个包的内部约定」。横跨多个包的功能规格,以及架构层面的决策,维度是「功能」和「架构」,不属于任何单一的包,集中放在根目录的 specs/ 下。一个跨端分享功能会同时牵动 apps/webpackages/featurespackages/platform,没法「属于」其中哪一个,那就放在在中立的顶层。

而单个包的内部契约,天然属于那个包,就近放进包内的 README.md。比如 packages/platform/README.md 写清楚 PlatformAPI 的接口约定,packages/core/README.md 说明 store 的实体缓存规则。这些都可以作为包的说明,跟着包走,改包的人一眼就能看到,不用跑到十万八千里外的根目录去翻。

两者之间用链接互相指过去就行:根目录的 specs/features/share-page/design.md 里链到 apps/webpackages/features 的具体位置;包的 README 再反向链回相关的 spec。这样无论你是从功能视角还是从包视角进来,都能顺着链接摸到另一头。

落到目录上大概是这样:

repo/
├── specs/
│ ├── features/
│ │ └── share-page/
│ │ ├── requirements.md # 要做什么、验收标准
│ │ ├── design.md # 怎么做、涉及哪些包、关键接口
│ │ └── tasks.md # 可勾选的实现清单
│ └── adr/
│ ├── 0001-ssr-only-in-web.md
│ ├── 0002-platform-adapter.md
│ └── 0003-pnpm-turborepo.md
├── apps/
│ └── web/README.md
└── packages/
├── platform/README.md # PlatformAPI 接口约定
└── core/README.md # store 实体缓存规则

关于治理 spec 腐化#

Spec会和代码悄悄漂移,比如代码改了三版,spec 还停在第一版,时间一长大家就都不信它了,于是彻底沦为摆设。 防止这个问题有一些解决方案:

第一,把 spec 拆成三段,对应功能的生命周期。 这里可以直接借鉴 Kiro / spec-kit 的 requirements → design → tasks 三件套 讲要做什么和验收标准,design 讲怎么做、涉及哪些包、关键接口长什么样,tasks 是一份能打勾的实现清单。关键在于 tasks 做完之后不要删,让它作为「已交付」的历史存档留在那儿。这样 feature spec 本身就是一条完整的时间线,从提出到落地都有痕迹,而不是一个随时可能过期的静态文档。

第二,在 spec 里显式记下「影响的包」。 每个 feature 的 design.md 顶部放一张「Affected packages」表,列清楚它落在哪几个 apps/*packages/* 上。这张表是弥合「功能维度」和「包维度」错位的关键,能从一份 spec 反查出要动哪些代码,也能从一个 PR 反过来对照该改哪几份 spec。

## Affected packages
| 包 / 应用 | 改动性质 |
| ----------------- | -------------------------- |
| apps/web | 新增分享页路由与 SSR |
| packages/features | 分享逻辑主体 |
| packages/platform | 新增分享相关的 PlatformAPI |

第三,架构决策用 ADR,而且只增不改。 前面几轮定下来的那些决策——「SSR 只在 web 端」「platform adapter 负责吸收环境差异」「用 pnpm + Turborepo」——每一条都写成一份带编号、带日期的 ADR,结构固定为「背景 / 决策 / 后果」三段。ADR 的铁律是不修改历史:决策变了不是去改旧文件,而是新增一条,并标明它 supersede(取代)了哪一条旧的。这样决策的演进是可追溯的,新人进来翻 ADR 就能看懂「为什么当初这么定、后来又为什么改了」,而不是面对一堆没有来由的约定。

更新和维护:让「改 spec」变成流程的一部分#

monorepo 里的 SDD 能不能长期跑下去取决于有没有一套让 spec 跟着代码一起变的机制,机制立起来之后,日常怎么让它活着,靠的是把维护动作嵌进已有的开发流程里,而不是额外开一摊。

可以在 PR 模板里加一行强制勾选:「本次改动是否涉及 spec / ADR?若是,已同步更新并贴出链接。」搭配前面那张 Affected packages 表,review 的人很容易判断这个 PR 该不该带上 spec 改动。改功能顺手改 spec,这件事只要进了 checklist,就不容易被漏掉。

再往前一步,可以让 spec-kit 的 /speckit.analyze 在实现前先跑一遍,校验 spec、plan、tasks 三者之间有没有互相矛盾——发现冲突时,规矩是先改文档再改代码,而不是闷头把代码写完让文档去追,在动手之前把漂移掐掉。

下面是可以接在文章后面的一节,把你列的几点局限整理成博客风格的连贯叙述。我在保留你原意和例子的基础上,把逻辑串得更顺了一些,也补了一两句收束。


SDD 工作模式的局限#

SDD不是银弹,也有不擅长的场景,开发者也要知道它的边界在哪。

不适合多需求相互耦合、且上下文没打通的场景#

SDD 的天花板很大程度上取决于 Context 的组织程度。

比如 A 需求里的某个需求点,其实在 B 需求中也应该生效。但产品没提,需求文档里也没写,可 QA 认为要实现,产品被问到时也觉得要实现。而如果只按正常的需求开发流程走,「不生效」才是最符合文档的结果。

这本质上是上下文没打通的问题,顺带也暴露了产品团队提需混乱。在传统开发里,这类隐性信息往往靠研发之间口头同步、产品临时补充、项目群里的一句话,或者某个老员工的经验判断来兜住。但在 SDD 模式下,只要这些信息没有显式写进 Spec,Agent 大概率不会主动发现——它没法替你去读那些散落在会议纪要和聊天记录里的"潜规则"。

所以当多个需求之间存在交叉影响,而上下文又分散在不同文档、不同人、不同会议里时,SDD 的效果会明显下降。除非团队有一个部门级的 Context 仓库,能把所有需求决策、产品变更、技术方案和历史背景统一沉淀下来,再有一个 Agent 在开发前自动复核所有相关上下文——否则,单个需求级别的 SDD 很难稳定处理这种跨需求耦合。

不适合多团队协作的超大需求"一次性写完"#

加入业务要新增一个模块,这种需求通常不是某个研发改几个文件就能搞定的,它往往横跨多个团队、多个模块、多个阶段,本身就是一个大型专项。这种情况下一次 Spec、一次 Agent 执行,肯定不能把整个需求端到端做完。

推荐的做法是先把超大需求拆成多个阶段,再在每个阶段内部用 SDD。比如第一阶段做技术调研和整体方案,第二阶段做 Demo 验证,第三阶段拆客户端基础能力,第四阶段接服务端协议,第五阶段完善体验和异常处理,第六阶段进测试和灰度。SDD 在每个阶段内部依然好用,但它不负责替你决定这些阶段该怎么切。

尤其是超大需求的前期,真正难的地方往往不是写代码,而是达成共识。光是整体技术方案就可能要讨论一周甚至更久,涉及模块边界、团队分工、风险评估、上线节奏、兜底策略——这些都不是 Coding Agent 一把梭能定下来的,得靠人拉齐。

不适合频繁变化、缺乏稳定输入的产品需求#

如果产品需求本身就飘忽不定,文档想一出是一出、小巧思特别多,今天改交互、明天改路径、后天又推翻核心逻辑,那不管你用什么工作模式,研发效率都会被拖垮。

而 SDD 在这种情况下甚至可能更吃亏——因为它的前置成本(写 Spec、定 plan、拆 tasks)是押注在"需求相对稳定"这个前提上的。需求一天变三回,开发者认真写的 Spec 就跟着一天报废三回。当产品团队还不是 AI First 的工作方式时,这个矛盾会格外明显。

SDD 把质量押在了"输入"上。需求清晰、上下文完整、边界稳定——满足这些前提时,才能把人从逐行盯代码里解放出来(古法编程也这样其实);可一旦前提不成立,再好的流程也救不回来。所以说 SDD 比起一套"写代码的方法",更像一面镜子,照出的是团队在 Context 沉淀和需求管理上的水平。

写在最后#

回头看会发现一件挺有意思的事 听起来是个很"新"的东西,可它骨子里那些讲究,几乎全是软件工程几十年积累下来的老道理。先想清楚再动手、把需求和边界写明白、用文档沉淀决策、拆分任务、测试先行、对架构选择留下可追溯的记录——这些在"古法编程"时代就被反复强调的东西,换到 AI 时代非但没有过时,反而因为 Agent 的加入变得更重要了。

原因也不难理解。过去这些规范之所以常常被当成"最佳实践"束之高阁,是因为人可以偷懒:需求没写清,靠脑子记;文档没更新,靠口头问;边界没定义,靠经验蒙。人有容错、有默契、有临场发挥,烂一点的流程也能靠人硬扛过去。但 Agent 不吃这一套——你喂给它多少清晰的 Context,它就还你多少确定的结果;你含糊,它就跟着含糊。于是那些原本"最好要做"的软件工程动作,在 SDD 里变成了"不做就跑不动"的硬约束。AI 没有淘汰这些方法论,而是把它们从可选项变成了必选项。

所以真正的变化不在于工具,而在于对开发者的要求。当写代码这件事本身越来越多地交给 Agent,人的价值就上移到了"想清楚"这一层——怎么把模糊的需求拆成精确的规格,怎么在动手前识别出跨需求的耦合和风险,怎么判断一个技术方案的边界和兜底,怎么让整个团队的上下文沉淀得足够 Agent 能读懂。这些恰恰是最难自动化、也最需要经验和判断的部分。换句话说,AI 接管了"体力活",却把对"脑力活"的门槛抬高了。

这也是为什么在这样一个看似"人人都能靠 AI 写代码"的时代,持续学习反而比以往任何时候都不能停。今天要学的不只是某个新框架或某个 Agent 的用法——那些迭代得太快,追不过来也不必追(只要学得慢就不用学,毕竟很多概念活不过两个月纯营销);更该沉下心去补的,是那些经久不衰的基本功:工程方法论、系统设计的取舍、对复杂度的敬畏,以及把一件事想透彻、说清楚的能力。工具会一茬一茬地换,但这些底层的东西,才能让开发者在现在浪潮里始终站得住,走得远(除非ai迭代到不需要任何harness了)。

总之软件工程好啊,软件工程得学.jpg

分享到社交平台

将本文分享给你的朋友们

2026-07-18-简谈SDD(规格驱动开发)
https://zhongye1.github.io/posts/2026/2026-07-18-简谈sdd规范驱动开发-copy/
作者
Zhongye
发布于
2026-07-18
版权声明
CC BY-NC-SA 4.0

评论

Profile Image of the Author
Zhongye
南漂中
公告
新的博客站!旧站点传送门 👇
音乐
专辑封面

音乐

暂无播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章数
147
分类数
14
标签数
209
总字数
445,674
运行天数
0
最后更新
0 天前
总访问量
42296
访客数
29212

目录