用 Spec Kit 进行规范驱动开发
作为软件开发者,我们在项目中见过太多次这样的场景:规格说明书在项目启动时写好,争论了几周,然后被悄悄埋在 sprint 看板、Jira 工单和"我们代码评审时再看"的借口之下。
最终,代码成了唯一的真相来源,而规格说明书则沦为过时的摆设。
现在,AI 编程智能体正在改变这一模式,一个新术语也应运而生:规范驱动开发(Spec-Driven Development,简称 SDD)。其背后的理念很简单,对于那些习惯以文件和 Pull Request 为单位思考的人来说,甚至有点令人不安:
规范,而非代码,才是我们应该维护的首要产物。代码只是从规范中生成的产物。
在这篇文章中,我将介绍规范驱动开发是什么,它如何重塑软件开发生命周期,以及在我们使用 Spec Kit 和 Claude Code 构建一个小型 REST API 时,它在实践中是什么样子。
1、什么是规范驱动开发?
规范驱动开发是一种构建软件的方式,其中结构化、可执行的规范处于工作流程的中心。
与其直接从 Jira 工单跳到代码,我们首先精确描述系统应该做什么、必须遵守哪些约束、以及变更的边界在哪里。然后,AI 智能体使用这些规范来规划、组织和实施工作,更重要的是,规范在代码生成后仍然保持活力。
这与许多团队在过去几年中使用 AI 助手的方式形成了有意义的转变。标签补全式编码(有时被亲切地称为"氛围编程")对于小段的胶水代码来说效果不错,但在团队规模上它有一个众所周知的失败模式:
智能体对系统的意图没有共同的理解,每个开发者用略有不同的提示来影响它。结果是快速、不一致且难以演进的。
2、传统开发 vs 规范驱动开发
SDD 直接解决了这个问题。规范不再是藏在 Confluence 页面里的 Word 文档,
它是仓库中一个版本化、可审查的文件,AI 智能体阅读它的方式与新团队成员阅读它的方式相同。
一个直观的对比:
- 传统开发:需求 → 代码(代码是真相来源)
- 规范驱动开发:需求(规范) → AI 智能体 → 代码(规范是真相来源)
GitHub 的开源项目 Spec Kit 是这一理念最显眼的实现之一,而 Kiro 和 Tessl 等工具也在探索类似的领域。它们并非完全相同,但共享一个共同的骨架:一小套结构化文档和一小套由 AI 驱动的阶段,将这些文档转化为可工作的软件。
3、SDD 如何改变软件开发生命周期
传统的软件开发生命周期(SDLC)包括分析、设计、实现、测试、部署和维护。每个阶段都会产生产物(需求文档、设计文档、源代码、测试报告),而这些产物中的大多数会逐渐脱节。
SDD 并没有抛弃 SDLC,而是重新组织了每个阶段的核心焦点:
分析不再仅仅是为了将来阅读而创建文档,它现在产生直接指导实现的规范。我们的问题变得更加精确,因为模糊的回答会在几小时而不是几个月内暴露为 bug。
设计转化为智能体或开发者可以遵循的清晰计划。它明确记录架构决策、技术选型和非功能性约束,通常以**"宪法"风格**的文档形式出现,概述项目的强制性规则。
实现是智能体完成大部分打字工作的地方,但开发者仍然对结果负责。角色从"写每一行代码"转变为"审查意图、验证行为,并在代码暴露出缺失细节时完善规范"。
测试与规范更加紧密地对齐。规范中概述的验收标准作为智能体创建的初始测试,让人类和智能体都能在回归测试期间参考规范。
维护可能是长期收益最大的地方。当规范保持最新时,引入新工程师或启动新的智能体会话不需要反向工程代码库。你提供规范,其他一切就会各就各位。
阶段保持不变,但现在规范在整个过程中充当主要指南,而不是在实现开始时被丢弃。
4、规范驱动开发的工具链
规范驱动开发主要是一种纪律,但已经出现了一批工具包来降低实践它的摩擦。讨论最多的三个是 GitHub 的 Spec Kit、AWS 的 Kiro 和 Tessl。
Spec Kit 是开源的、由 CLI 驱动的选项,它与大多数团队已经在使用的智能体集成得最为顺畅:Claude Code、Cursor、Copilot、Gemini 和 Codex,因此我将在本文的其余部分重点介绍它。
5.1 安装 Spec Kit
Spec Kit 作为一个名为 specify 的小型 Python CLI 发布。在你的机器上通过 uv 安装一次:
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
specify --version
然后,在你想要使用 SDD 的项目内部初始化它:
specify init . --ai claude
--ai claude 标志将 Spec Kit 的提示模板接入 Claude Code,同样的标志也接受其他智能体(cursor、copilot、gemini、codex)。在 specify init 之后,Claude Code 的斜杠菜单中会出现五个斜杠命令:
/speckit.constitution:定义项目范围内不可协商的事项。/speckit.specify:从自然语言描述生成功能规范。/speckit.plan:从规范和技术约束生成实现计划。/speckit.tasks:将计划分解为有序的任务列表。/speckit.implement:执行一个或多个任务。
三个额外的辅助脚本用于审查和完善:
/speckit.clarify:在需求到达计划阶段之前标记模糊之处。/speckit.analyze:交叉检查规范、计划和任务之间的不一致性。/speckit.checklist:生成可以交给人类审查者的审查清单。
specify init 命令实际上会创建这些文件:
运行 "specify init" 命令后生成的文件
5.2 Spec Kit 命令与原生 Claude Code 命令的对比
我们实际上并不需要 Spec Kit 来进行规范驱动开发。它是一个便利层,类似于其他智能体(如 Claude Code)中的工作流运行,通过常规提示让智能体在相同的文件夹中生成相同的文件。
宪法层甚至内置了 Claude Code /init 命令的部分类似物,它在项目根目录下搭建一个 CLAUDE.md,Claude 在每个会话中都会读取它。这两个文件位于项目的同一层,但回答的问题略有不同:
"CLAUDE.md"主要是描述性的("项目是这样布局的,用这个命令运行测试"),而"constitution.md"主要是规定性的("这些是每个功能必须遵守的不可协商规则")。
其他 Spec Kit 命令,如 /speckit.specify、.plan、.tasks 和 .implement,完全没有原生的 Claude Code 等价物,你必须么安装 Spec Kit,要么自己编写提示。
如果你的团队计划认真使用 SDD,安装 Spec Kit 值得付出小小的前期成本,而不是依赖原生的 Claude Code 命令和自由形式的提示。
因为相同的 "/speckit.*" 命令无论你指向 Claude Code、Cursor、Copilot 还是 Gemini 都会产生相同的产物,所以如果你更换智能体,项目的规范也能干净地迁移。6、实践示例:Spring Boot 预订 API
让我们开始吧。假设我们想要构建一个小型的 REST 服务来管理会议室预订。在传统流程中,我会创建一个 Spring Boot 项目,草拟一个 Booking 实体,添加一个控制器,连接一个仓库,并在过程中摸索验证规则。
在规范驱动流程中,第一个产物不是类,而是一个与源代码并存并处于版本控制之下的规范文件目录。
.specify/memory/constitution.md 文件包含每个功能必须遵守的项目范围规则,包括编码标准、测试策略和安全基线。
specs/<feature>/ 文件夹存放每个功能的产物。src/ 下的所有内容都是生成的内容,它是输出,而不是真相来源,其中的每一行都应该可以追溯到 specs/ 下的某个文件。
在安装了 Spec Kit 并有了宪法之后,以下是预订 API 的四步工作流程。
🚨 Claude Code 中 /speckit-specify 技能的新技能钩子架构存在一个 bug,这在较新版本的 Spec Kit 中有所体现。在 .specify/extensions.yml 中,我们有 auto_execute_hooks: true 和一个强制的 before_specify 钩子,它首先运行 /speckit-git-feature:
before_specify:
- extension: git
command: speckit.git.feature
enabled: true
optional: false # 强制的
我们需要编辑 .specify/extensions.yml,将 before_specify 钩子从 optional: false 改为 optional: true,以在开始之前修复这个错误。
1️⃣ 规范
这一步从开发者编写自然语言提示开始。智能体将该提示转化为结构化的 spec.md 文件。开发者随后审查并编辑它。
💬 我会输入到智能体(Claude Code)中的提示:
/speckit.specify 构建一个服务,让员工可以预订会议室的特定时间段。它必须防止同一房间的双重预订。预订只能由创建它们的用户取消。员工还应该能够列出给定房间和日期的预订。运行 "/speckit.specify" 命令后的 Claude Code 结果
智能体将结果写入 specs/001-meeting-room-bookings/spec.md,内容大致如下:
# Feature Specification: Meeting Room Booking
**Feature Branch**: `001-meeting-room-booking`
**Created**: 2026-05-08
**Status**: Draft
**Input**: User description: "Build a service that lets employees reserve a meeting room for a defined time window. It must prevent double-booking of the same room. Bookings can only be canceled by the user who created them. Employees should also be able to list bookings for a given room and day."
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Reserve a Meeting Room (Priority: P1)
An employee wants to reserve a specific meeting room for a team discussion. They select a room, specify a start and end time, and submit the reservation. If the room is available for that time window, the booking is confirmed. If the room is already booked for any overlapping period, the request is rejected with a clear explanation.
**Why this priority**: This is the core capability of the service — without the ability to create a booking, no other functionality has value.
.
.
.
注意 spec.md 文件中没有 Spring Boot、没有 JPA、没有 REST 动词、没有 DTO 名称。这是有意为之的,规范描述的是问题,而技术选型属于下一步。
在 /speckit-specify 写入 spec.md 之后,该技能还会生成 <feature-directory>/checklists/requirements.md,一个规范质量检查清单,它同时也是规划前的就绪检查点。它是一个固定的 markdown 结构,包含三个部分(内容质量、需求完整性、功能就绪度),涵盖诸如"没有实现细节泄露到规范中"、"没有遗留的 [NEEDS CLARIFICATION] 标记"、"成功标准是可衡量且与技术无关的"以及"每个功能需求都有明确的验收标准"等项目。智能体逐项检查规范,修复任何失败的项目(最多 3 次迭代),暂停询问任何未解决的澄清问题,并将结果与规范一起提交。
"/speckit-specify" 命令生成的文件
📋 我们在这个阶段的工作是审查。如果缺少某个规则(时区、最大预订范围,或者什么算作同一个"天"),我们直接编辑 spec.md 或要求智能体修改它。然后该文件像任何其他源代码产物一样提交到仓库。我们也可以审查和更新 requirements.md,但设计假设你通常不会这样做,而当你这样做时,你应该有意识地考虑你真正在修复的是哪个文件。
2️⃣ 计划
有了 spec.md 之后,开发者提示智能体制定技术计划,提供有意从规范中省略的技术约束:
💬 规划的提示:
/speckit-plan使用 Java 25 和 Spring Boot 4.0*.x。使用 Spring Data JPA 持久化到 PostgreSQL。在*/api/v1/bookings下暴露 REST 端点。使用 Jakarta Bean Validation 验证输入。在数据库层面强制执行重叠预防,而不仅仅是在应用程序代码中。使用 JUnit 5 和 Testcontainers 进行测试。
运行 "/speckit-plan" 命令后的 Claude Code 结果
智能体读取 spec.md 加上这个提示,然后写入 specs/001-meeting-room-bookings/plan.md。使用 Spec Kit 时,它通常还会在同一个文件夹中生成支持文件:
data-model.md描述实体形状contracts/目录存放 OpenAPI 片段research.md记录为什么选择某个特定的库或方法
"/speckit-plan" 命令生成的文件
生成的 plan.md 的简短版本:
# Implementation Plan: Meeting Room Booking
**Branch**: `main` | **Date**: 2026-05-09 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/001-meeting-room-booking/spec.md`
## Summary
Build a RESTful Spring Boot 4.0.x service (Java 25) that lets employees reserve, cancel, and list meeting room bookings via `/api/v1/bookings`. Double-booking prevention is enforced at the PostgreSQL level using an `EXCLUDE USING GIST` constraint on a `tstzrange` column; the application handles concurrent conflicts by catching constraint violations and returning HTTP 409.
## Technical Context
**Language/Version**: Java 25
**Primary Dependencies**: Spring Boot 4.0.x, Spring Data JPA (Hibernate 7), Spring Web MVC, Jakarta Bean Validation (Hibernate Validator), Flyway
**Storage**: PostgreSQL 16+ via Spring Data JPA; EXCLUDE USING GIST constraint for overlap prevention
**Testing**: JUnit 5, Testcontainers (PostgreSQLContainer), Spring Boot Test, Mockito
**Target Platform**: Linux server (JVM / containerized)
**Project Type**: web-service (REST API)
**Performance Goals**: 95% of operations within 2 seconds under organizational load (SC-006); booking listing within 2 seconds (SC-004)
**Constraints**: Zero double-bookings (SC-002); concurrent conflict → exactly one booking succeeds (SC-005)
**Scale/Scope**: Organizational scale — tens of rooms, hundreds of employees
.
.
.
📋 与上一步类似,智能体起草,开发者拥有。如果计划选择了不想要的依赖或做出了我们不同意的架构选择,我用纯文本提出异议,智能体会修改 plan.md。
3️⃣ 任务列表
下一个命令将规范和计划转化为具体的待办事项列表:
💬 提示:
/speckit-tasks使用 Spec Kit 时,该命令不需要额外的提示,智能体已经有 spec.md 和 plan.md 可以工作。它写入 specs/001-meeting-room-bookings/tasks.md:
---
description: "Task list for Meeting Room Booking implementation"
---
# Tasks: Meeting Room Booking
**Input**: Design documents from `/specs/001-meeting-room-booking/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/openapi.yml
**Tech Stack**: Java 25, Spring Boot 4.0.x, Spring Data JPA, PostgreSQL, Flyway, Jakarta Bean Validation, JUnit 5, Testcontainers
**Tests**: Included — JUnit 5 + Testcontainers explicitly required by the feature spec.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no dependencies on incomplete tasks)
- **[Story]**: Which user story this task belongs to (US1, US2, US3)
- Each task includes the exact file path to create or modify
## Path Conventions
Single Spring Boot project at repository root:
- Source: `src/main/java/com/example/booking/`
- Resources: `src/main/resources/`
- Tests: `src/test/java/com/example/booking/`
- Test resources: `src/test/resources/`
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Initialize the Spring Boot 4.0.x Maven project with all required dependencies and base configuration.
- [ ] T001 Create pom.xml with Spring Boot 4.0.x parent, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-validation, postgresql driver, flyway-core, spring-boot-testcontainers, testcontainers-postgresql, and junit-jupiter at repository root
- [ ] T002 Create BookingApplication.java at src/main/java/com/example/booking/BookingApplication.java
- [ ] T003 [P] Create application.yml with datasource, JPA (ddl-auto: validate), Flyway, and virtual threads (spring.threads.virtual.enabled: true) at src/main/resources/application.yml
.
.
.
运行 "/speckit-tasks" 命令后的 Claude Code 结果
💡 实用技巧:在实施前审查。 /speckit-tasks 是在问题变成代码之前发现它们的最便宜时机。从头到尾阅读生成的列表,并询问每个任务是否有明确的验收标准,顺序是否合理(数据库迁移在实体之前,契约测试在控制器之前),以及任何缺失是否因为规范缺失了它(在这种情况下,回去编辑 spec.md,而不是 tasks.md)。在这里运行 /speckit-analyze 也值得多花几秒钟 —— 它交叉检查 spec.md、plan.md 和 tasks.md,标记你可能在实施到一半时才发现的不一致性。
4️⃣ 实现
直到这时,智能体才开始将实际的 Java 文件写入 src/。开发者按任务逐步驱动这一步:
💬 实现的提示:
/speckit-implement 只运行 Phase 1 中的任务 T001*。*💡 实用技巧:按任务逐步实现,并开启计划模式。 一次性运行 /speckit-implement 并让智能体在单次运行中完成 tasks.md 中的每个任务是很有诱惑力的。不要这样做。一次驱动一个任务(只运行任务 T001 来自 Phase 1),并在每次之前切换到 Claude Code 的计划模式(Shift+Tab 直到屏幕底部显示 plan mode on)。计划模式让智能体写出逐文件的计划,并在触碰任何代码之前等待你的批准,这是规范与差异之间的真正检查点。每个任务完成后,快速审查差异,运行测试,然后再继续。如果某个任务揭示了规范中的缺失细节,停下来先更新 spec.md,不要"在代码中修复"然后就完事了。
当未来的变更请求到来时,例如允许重复预订,我们首先更新 "spec.md",然后重新生成受影响的 "plan.md""、"tasks.md"" 和相关 Java 文件的部分。规范与系统保持同步,因为它是变更进入的唯一途径。
7、结束语
规范驱动开发不是银弹,也不能替代工程判断。它更侧重于 AI 无法为我们完成的工作部分,例如澄清意图、权衡选择以及验证构建的内容是否确实是需要的。
智能体负责打字。我们负责思考。
如果你想在真实项目中尝试 SDD,我建议从小处开始,例如,从一个功能、一个仓库开始,并抵制住"我已经知道要做什么"就跳过规范的诱惑。这种捷径正是 SDD 试图打破的习惯。一旦规范就位,SDLC 的其余部分就会感觉不那么像一系列脱节的交接,而更像一条从意图到运行代码的单一、可追溯的线。
我也很喜欢使用 Spec Kit 作为 SDD 工具包,它为 SDD 充当纪律,强制执行一致的文件布局,将每个步骤包装在测试过的提示模板中,并将架构规则捕获在每个功能都必须遵守的宪法中。而且因为相同的 /speckit-* 命令无论我们指向 Claude Code、Cursor、Copilot 还是 Gemini 都会产生相同的产物,所以如果工具发生变化,工作可以迁移,使我们与智能体无关。
原文链接:Spec-Driven Development with Spec Kit and Claude Code
汇智网翻译整理,转载请标明出处