理解规约驱动开发(SDD):Kiro、spec-kit 与 Tessl
原文:Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl 作者:Birgitta Böckeler(Thoughtworks 杰出工程师、AI 辅助交付专家,拥有 20 多年软件开发、架构与技术领导经验) 本文是”Exploring Gen AI”系列的一部分。该系列记录了 Thoughtworks 技术专家对使用生成式 AI 技术进行软件开发的探索。
我一直在尝试理解 AI 编程领域最新的流行语之一:规约驱动开发(Spec-driven development,SDD)。我研究了三个自称 SDD 工具的产品,并试图厘清截至目前它到底意味着什么。
定义
与这个快节奏领域中许多新兴术语一样,“规约驱动开发”(SDD)的定义仍在变化之中。以下是我从目前见到的用法中归纳出的理解:规约驱动开发意味着在用 AI 编写代码之前先编写一份”规约”(“文档先行”)。这份规约成为人与 AI 共同的真理来源(source of truth)。
GitHub:“在这个新世界中,维护软件意味着演进规约。[……]开发的通用语言上移到了更高层次,而代码只是最后一公里的实现手段。”
Tessl:“一种开发方法,其中规约——而非代码——是主要产物。规约用结构化、可测试的语言描述意图,由智能体(agents)生成与之匹配的代码。”
在考察了这个术语的用法以及一些声称实现 SDD 的工具之后,在我看来,实际上它存在多个实现层次:
- Spec-first(规约优先):先编写一份深思熟虑的规约,然后在当前任务的 AI 辅助开发工作流中使用它。
- Spec-anchored(规约锚定):任务完成后仍保留规约,继续使用它来演进和维护相应的功能。
- Spec-as-source(规约即源码):规约是长期的主要源文件,人只编辑规约,永远不直接接触代码。
我找到的所有 SDD 方法和定义都属于 spec-first,但并非都致力于达到 spec-anchored 或 spec-as-source。而且,规约随时间的维护策略应该是什么样,往往含糊其辞或完全留白。

什么是规约(Spec)?
定义方面最关键的问题当然是:什么是规约?目前似乎并没有一个通用定义,我见过最接近一致的定义,是将规约类比为”产品需求文档”(Product Requirements Document,PRD)。
这个词目前的含义相当过载(overloaded),以下是我对规约的界定尝试:
规约是一种结构化的、面向行为的工件(artifact)——或一组相关联的工件——用自然语言编写,表达软件功能,并作为对 AI 编程智能体的指导。规约驱动开发的每种变体都各自定义了规约的结构、详细程度,以及这些工件在项目中的组织方式。
我认为,规约与代码库中更一般的上下文文档之间存在一个有用的区分。那种一般上下文包括规则文件(rules files),或对产品和代码库的高层描述。一些工具将这种上下文称为记忆库(memory bank),我这里也沿用这个叫法。这些文件对整个代码库中的所有 AI 编程会话都相关,而规约只与那些真正创建或修改该特定功能的任务相关。

评估 SDD 工具的挑战
事实证明,要以一种接近真实使用的方式来评估 SDD 工具和方法,相当耗时。你必须用不同规模的问题、绿地(greenfield)项目、棕地(brownfield)项目去试用它们,并且真正花时间去审查和修订中间产物,而不是走马观花。因为正如 GitHub 关于 spec-kit 的博客文章所说:“至关重要的是,你的角色不仅仅是引导,更是验证。在每一阶段,你都要反思和精炼。”
我试用的三个工具中,有两个要引入既有代码库似乎工作量更大,因此更难评估它们对棕地代码库的实际效用。在我听到有人在”真实的”代码库上使用它们一段时间后的实践报告之前,关于这在现实生活中如何运作,我仍有很多悬而未决的问题。
话虽如此——下面进入这三个工具。我会先介绍它们的工作方式(或者说我认为它们的工作方式),把我的观察和疑问留到最后。请注意,这些工具的演进速度非常快,自 9 月我使用它们以来,可能已经发生了变化。
Kiro
Kiro是我试用的三个工具中最简单(或最轻量)的一个。它似乎主要是 spec-first:我找到的所有示例都是用它来完成一个任务或一个用户故事,没有任何地方说明如何在跨越多个任务的时间里,以 spec-anchored 的方式使用需求文档。
工作流: 需求(Requirements)→ 设计(Design)→ 任务(Tasks)
工作流的每一步都由一份 Markdown 文档表示,Kiro 在其基于 VS Code 的发行版中引导你完成这 3 个工作流步骤。
需求(Requirements): 结构化为一个需求列表,其中每个需求代表一条”用户故事”(采用”As a…”格式),并附验收标准(采用”GIVEN… WHEN… THEN…”格式)。

设计(Design): 在我的尝试中,设计文档包含的章节见下方截图。我只保留了其中一次尝试的结果,所以我不确定这是否是固定结构,还是会随任务而变化。

任务(Tasks): 一个任务列表,可追溯到需求编号,并附带一些额外的 UI 元素,用于逐个运行任务、按任务审查变更。

Kiro 也有记忆库的概念,他们称之为”steering(掌舵文档)“。其内容很灵活,他们的工作流似乎并不依赖其中必须有特定的文件(我是在使用尝试之后才偶然发现 steering 这个部分的)。当你要求 Kiro 生成 steering 文档时,它默认创建的结构是 product.md、structure.md、tech.md。

Spec-kit
Spec-kit是 GitHub 版本的 SDD。它以 CLI 形式发布,可以为多种常见的编程助手创建工作区配置。结构设置完成后,你通过编程助手里的斜杠命令(slash commands)与 spec-kit 交互。由于它的所有产物都直接放进你的工作区,它是本文讨论的三个工具中最可定制的一个。
工作流: Constitution → 𝄆 Specify → Plan → Tasks 𝄇
spec-kit 的记忆库概念是规约驱动方法的先决条件,他们称之为宪法(constitution)。宪法应当包含”不可变”的高层原则,这些原则应当始终应用于每一次变更。它基本上就是一个非常强大的规则文件,被工作流大量使用。

在工作流的每一步(specify、plan、tasks)中,spec-kit 通过一个 bash 脚本和一些模板实例化出一组文件和提示词。随后,工作流大量使用文件内部的检查清单(checklists),来跟踪必要的用户澄清、宪法违规、研究任务等。它们就像每个工作流步骤的”完成的定义”(definition of done)(只不过由 AI 来解读,所以无法 100% 保证它们会被遵守)。

下面是一个概览图,用来说明我在 spec-kit 中看到的文件拓扑。注意,一个规约由许多文件构成。

乍一看,GitHub 似乎正在追求一种 spec-anchored 的方法(“这就是为什么我们要重新思考规约——不是把它当作静态文档,而是当作随项目一起演进的、活的、可执行的工件。规约成为共享的真理来源。当某些东西讲不通时,你回到规约;当项目变得复杂时,你精炼它;当任务感觉太大时,你拆分它。“)然而,spec-kit 会为创建的每份规约创建一个分支,这似乎表明他们将规约视为在一个变更请求的生命周期内活着的工件,而不是一个功能的生命周期。
这个社区讨论正是在谈论这种困惑。这让我觉得 spec-kit 目前仍属于我所说的 spec-first,而不是长期的 spec-anchored。
Tessl Framework(仍处于私有 Beta 阶段)
与 spec-kit 类似,Tessl Framework以 CLI 形式发布,可以为多种编程助手创建全部工作区和配置结构。这个 CLI 命令同时也可兼作 MCP 服务器。

Tessl 是这三个工具中唯一明确追求 spec-anchored 方法的,甚至还在探索 SDD 的 spec-as-source 层次。Tessl 规约可以作为被维护和编辑的主要工件,生成的代码甚至顶部带有一条注释,写着 // GENERATED FROM SPEC - DO NOT EDIT。目前规约与代码文件是 1:1 映射的,即一份规约对应代码库中的一个文件。但 Tessl 仍处于 beta 阶段,他们正在试验不同的版本,所以我可以想象这种方法也可以应用在”一份规约映射到包含多个文件的代码组件”这一层次上。alpha 产品将支持什么,还有待观察。(Tessl 团队自己认为,他们的框架比其当前的公开产品 Tessl Registry 更具前瞻性。)
下面是一个规约示例,是我让 Tessl CLI 从既有代码库中的一个 JavaScript 文件反向工程出来的(tessl document --code ...js):

@generate 或 @test 这样的标签似乎是告诉 Tessl 要生成什么。API 部分展示了这样一种思路:至少在规约中定义要暴露给代码库其他部分的接口,推测是为了确保生成组件中这些更关键的部分完全处于维护者的掌控之下。针对这份规约运行 tessl build,就会生成对应的 JavaScript 代码文件。

将 spec-as-source 的规约放在相当低的抽象层次(按代码文件逐个编写),可能会减少 LLM 必须执行的步骤和解读次数,从而降低出错的概率。不过,即使在这种低抽象层次上,我也见识了非确定性的实际表现——用同一份规约多次生成代码,结果并不一致。通过迭代规约、让它越来越具体,来提高代码生成的可重复性,是一次有趣的练习。这个过程让我想起编写一份无歧义、完整规约的一些陷阱和挑战。
观察与疑问
这三个工具都自称实现了规约驱动开发,但它们彼此之间差异很大。这是谈论 SDD 时要记住的第一件事:它并不只是单一的东西。
一种工作流适配所有规模?
Kiro 和 spec-kit 各自提供了一种带有强烈主张(opinionated)的工作流,但我很确定它们都不适合现实中大多数编程问题。尤其是,我看不出它们如何能够适配足够多的问题规模,从而具有普遍适用性。
当我让 Kiro 修复一个小 bug(就是我过去试用 Codex 时用过的同一个 bug)时,很快就发现这个工作流就像用大锤砸核桃。需求文档把这个小 bug 变成了 4 个”用户故事”,总共 16 条验收标准,其中包括这样的”神来之笔”:“用户故事:作为一名开发者,我希望转换函数能优雅地处理边缘情况,以便在引入新的类别格式时系统仍能保持健壮。”
使用 spec-kit 时我遇到了类似的困扰:我不太确定该用它处理多大问题。现成的教程通常基于从零开始创建一个应用程序,因为那对教程来说最容易。我最终尝试的用例之一是一个功能,在我过去所在的团队里大概值 3-5 个故事点。这个功能依赖于大量已有代码——它要构建一个汇总面板,总结既有仪表盘中的一批数据。考虑到 spec-kit 走过的步骤数量,以及它为我创建的、等我去审查的 Markdown 文件数量,这又一次让人感觉对这个规模的问题来说是杀鸡用牛刀。它比我在 Kiro 上用的那个问题更大,但工作流也繁琐得多。我甚至从未完成完整的实现;不过我想,在运行和审查 spec-kit 结果所花的同样时间里,我本可以用”朴素”的 AI 辅助编程把那个功能实现出来,而且会感到更有掌控感。
一个有效的 SDD 工具至少必须为几种不同的变更规模和类型,提供几种不同的核心工作流。
审查 Markdown 而不是审查代码?
正如刚才提到的,从上面的工具描述中也能看出,spec-kit 为我创建了大量需要审查的 Markdown 文件。它们彼此重复,也与已有代码重复,有些甚至已经包含了代码。总体来说,它们只是非常啰嗦,审查起来枯燥乏味。Kiro 那边好一点,因为只有 3 个文件,而且”需求 > 设计 > 任务”的心智模型更直观易懂。然而,如前所述,对于我要修的那个小 bug,Kiro 也实在太过啰嗦。
说实话,比起审查所有这些 Markdown 文件,我宁愿审查代码。一个有效的 SDD 工具必须提供非常好的规约审查体验。
虚假的掌控感?
即使有所有这些文件、模板、提示词、工作流和检查清单,我仍然经常看到智能体最终并没有遵循所有指令。是的,上下文窗口现在更大了,这经常被当作 SDD 得以实现的推动因素之一。但窗口更大,并不意味着 AI 会正确地吸收其中的所有内容。
举个例子:spec-kit 在规划过程中的某处有一个研究步骤,它会对既有代码和已有内容做大量研究。这很棒,因为我要求它添加一个构建在已有代码之上的功能。但最终,智能体忽略了这些笔记是对既有类的描述,而是把它们当作全新的规约,把它们全部重新生成了一遍,造成了重复。但我看到的不仅仅是无视指令的例子,我还看到智能体因为过于急切地遵循指令而做得过头的情况(比如宪法中的某一条款)。
过去的历史表明,让我们对自己构建的东西保持掌控的最佳方式是小步迭代。因此,我严重怀疑大量的前期规约设计是个好主意——尤其是在它过度啰嗦的时候。一个有效的 SDD 工具必须适配迭代式的方法,但小而碎的工作包几乎与 SDD 的理念相悖。
如何有效区分功能规约与技术规约?
在 SDD 中,有意识地划分功能规约与技术实现之间的界限,是一个常见的想法。我猜其底层期望是:最终我们可以让 AI 填补所有的方案设计和细节,用同一份规约切换到不同的技术栈。
现实中,当我试用 spec-kit 时,我经常困惑于什么时候该停留在功能层面,什么时候又该加入技术细节。教程和文档在这一点上也不太一致——对于”纯功能(purely functional)“究竟意味着什么,似乎存在不同的解读。而当我回想起职业生涯中读过的无数份未能妥善区分需求与实现的用户故事时,我不认为我们这个职业在此有良好的既往记录。
目标用户是谁?
规约驱动开发工具的许多演示和教程都包含诸如定义产品和功能目标之类的内容,甚至用到了”用户故事”这样的术语。这背后的想法可能是把 AI 作为跨技能培训(cross-skilling)的赋能者,让开发者更多地参与需求分析?或者让开发者在使用这个工作流时与产品人员结对?然而这些都没有被明确说明,仿佛开发者天然就应该做所有这些分析工作,这是不言自明的前提。
既然如此,我会再次自问:SDD 适合什么规模和类型的问题?恐怕不适合那些仍然非常不明确的大型功能——因为后者无疑需要更多的产品专业技能和需求工程技能,以及大量其他步骤,比如调研和干系人参与?

规约锚定与规约即源码:我们在从过去吸取教训吗?
虽然许多人将 SDD 与 TDD 或 BDD 作类比,但我认为另一个值得对照的重要对象是 MDD(模型驱动开发,model-driven development)——尤其是对于 spec-as-source。我职业生涯初期曾参与过几个大量使用 MDD 的项目,试用 Tessl Framework 时我不停地想起那段经历。MDD 中的模型基本上就是规约,只不过不是用自然语言表达,而是用自定义 UML 或文本 DSL 之类的形式表达。我们构建自定义代码生成器,把这些规约变成代码。

最终,MDD 在业务应用领域从未真正流行起来:它停留在一个尴尬的抽象层次上,制造了太多的开销和约束。但 LLM 消除了 MDD 的一部分开销和约束,因此人们重新燃起了希望:现在终于可以专注于编写规约,然后直接生成代码。有了 LLM,我们不再受限于预定义的、可解析的规约语言,也不必构建精巧的代码生成器。当然,代价就是 LLM 的非确定性。而且,可解析的结构也有我们正在失去的好处:我们曾可以为规约作者提供大量工具支持,帮助他们写出合法、完整、一致的规约。我在想,spec-as-source——甚至 spec-anchored——最终会不会落得两头不讨好:MDD 的僵化死板,加上 LLM 的非确定性。
需要说明的是,我并不是在怀念过去的 MDD 经历,说”不如把它找回来”。但当我们今天探索规约驱动开发时,应该回顾过去从规约生成代码的种种尝试,并从中吸取教训。
结论
在我个人使用 AI 辅助编程的过程中,我也经常先花时间仔细打磨某种形式的规约,再交给编程智能体。因此,spec-first 这一总体原则在许多场景下确实有价值,而关于如何组织规约结构的不同方法也非常受追捧。它们是我近期从实践者那里听到的最高频的问题之一:“我该如何组织我的记忆库?""我该如何为 AI 写出好的规约和设计文档?”
但”规约驱动开发”这个术语目前还没有被很好地定义,而且已经在发生语义扩散(semantic diffusion)。我最近甚至听到有人基本上把”spec”当作”详细提示词(detailed prompt)“的同义词来使用。
至于我试过的这些工具,我在上文列出了对它们现实世界效用的许多疑问。我在想,其中一些是不是在过于字面化地把我们既有的工作流喂给 AI 智能体,结果反而放大了诸如审查负担过重和幻觉之类的既有挑战。尤其是对于那些会创建大量文件的繁复方案,我忍不住想到德语合成词”Verschlimmbesserung”(越改越糟):我们是否正在试图把东西改好,结果却把它改得更糟?
系列文章导航: