
1. 为什么需要规范驱动开发SDD从“代码先行”到“规范先行”做后端开发这些年我见过太多项目死在“需求传导”这个环节上。产品经理口头描述一个功能开发理解成另一个功能测试再根据自己理解写用例最后验收时三方对着一个完全不同的东西开会——这种场景几乎每个团队都遇到过。后来接触到规范驱动开发Specification Driven Development简称SDD这个概念再用上 openspec-cn 这套工具链整个开发流程才算真正顺过来。今天想把这段时间的实战经验整理出来尤其是怎么用 openspec-cn 把 SDD 落到具体项目里。先说清楚 SDD 到底解决了什么问题。传统开发模式里需求文档、接口文档、代码、测试用例是四套互相割裂的东西靠人来保证它们一致这是最大的风险源。而 SDD 的核心思想是反转这个过程——先把行为规范写清楚让规范成为唯一的权威来源代码和测试都从规范推导出来而不是各写各的再靠人工对齐。有人可能会问这跟 TDD测试驱动开发有什么本质区别TDD 是先写测试再写实现但测试本身还是要靠人设计测试的质量决定了代码的质量。SDD 更进一步它要求先定义一份机器可读的规范这份规范既可以被测试直接执行又能用来生成文档、检查代码覆盖率相当于把“需求”本身变成了一种可验证的资产。换句话说TDD 保证的是“代码符合测试”SDD 保证的是“测试和代码都符合规范”。openspec-cn 就是专门为实践 SDD 而生的开源工具链。它自带一套规范的格式定义、校验引擎、测试生成器和文档渲染器把 SDD 里那些抽象的方法论变成了可以实际操作的工作流。用 openspec-cn 跑完一个完整功能开发之后我的体会是它并不是要取代你现有的开发流程而是在流程最前面加了一个“规范层”让需求和实现之间有了一个可追溯、可验证的中间地带。这篇文章不只讲概念我把整个落地的过程、踩过的坑、以及团队协作时的机制设计都整理出来适合两类人看一类是正在被“需求变更频繁、接口文档过期、测试用例失真”折磨的技术负责人另一类是刚接触 SDD 想找个入门到实战路径的开发者。2. openspec-cn 整体设计与工作流解析2.1 openspec-cn 是什么核心组件与仓库结构第一次接触 openspec-cn 的时候我最关心的问题是它到底装了什么、能在项目里干些什么用了大概一个月之后我的理解是这样的——openspec-cn 不是一个重框架而是一组轻量工具的组合核心组件可以分成四个部分。第一部分是规范解析器。它负责读取项目里的规范文件解析成内部的数据结构。openspec-cn 的规范文件不是纯文本而是用 Markdown 编写、同时带 YAML frontmatter 的结构化文档。Markdown 让人好读好写frontmatter 里可以放元数据比如规范 ID、优先级、所属模块、依赖关系等等。这类设计其实借鉴了 Static Site Generator 的思路把文档当数据来处理。第二部分是验证引擎。它可以对规范本身做静态检查比如看有没有重复的规范 ID、有没有循环依赖、有没有引用了不存在的规范编号、每个规范是否都有对应的验收条件。这一步非常关键因为规范的质量直接决定了后面代码和测试的质量如果规范本身是自相矛盾的那后面所有工序都会被带偏。第三部分是测试生成器。openspec-cn 可以根据规范文件里的验收条件自动生成测试代码的骨架支持常见的测试框架和语言。生成出来的测试用例不是随便写的它是严格按照规范里的 Given/When/Then 结构来生成的然后你只需要填充具体的调用逻辑和数据。第四部分是文档渲染器。它能把规范文件渲染成一份结构化的在线文档。这个功能看起来不起眼但在实际协作里特别重要——产品、开发、测试、项目经理看的是同一份随时更新的规范文档而不是各自维护一版互相矛盾的 Word 文档。仓库结构上openspec-cn 推荐在项目根目录下建一个specs/文件夹里面按功能模块分子目录。每个功能目录下至少包含一个spec.md文件这个文件就是该功能的行为规范。项目代码放在src/测试放在tests/形成一个从需求到实现到验证的目录映射关系。这种结构的好处是新成员加入项目时只看目录结构就能对项目有个整体认知。2.2 规范驱动开发的核心工作流需求 → 规范 → 验证理解了组件之后重点来看 openspec-cn 定义的工作流。一句话总结需求先变成规范规范再驱动实现和验证。这个流程我刚接触时感觉平平无奇真正落地之后才意识到它改变了整个开发的节奏。具体来说一个功能从需求到交付要经过五个阶段。第一个阶段是需求结构化。产品经理或者业务方提出一个需求不再是直接丢给开发口头说“给我加个查询功能”而是要把需求拆成多条可验证的行为描述。比如“支持按用户名模糊搜索”就能写成当用户输入部分用户名时系统返回用户名中包含该输入串的全部用户记录。这个阶段对产品要求比较高但 openspec-cn 提供了一个需求模板来引导避免把需求写成含糊的自然语言。第二个阶段是规范评审。写好的规范文件提交到仓库团队成员对着规范做评审。这个评审跟传统的代码评审很不一样——评审的对象不是已经写好的代码而是“我们要做什么”的共识。我经历过几次之后发现规范评审比代码评审能更早发现需求理解的偏差而且纠错成本要低得多。第三个阶段是测试骨架生成。规范评审通过后运行openspec generate命令openspec-cn 会自动读取规范文件里的验收条件为每一条生成对应的测试代码骨架。这些骨架带着空实现但测试的输入输出参数和断言逻辑已经写好了。这一步把“测试先行”的命令变成了自动化操作不需要人再去手工设计测试用例。第四个阶段是实现。开发照着测试骨架去实现业务逻辑。这个阶段其实是最轻松的因为行为的预期已经通过测试用例完全定死了开发不需要纠结“这里该不该为空要不要返回 404”这类问题测试就是判断标准。第五个阶段是验证与回归。跑通全部测试后openspec-cn 能生成一份追溯报告清楚地展示每条规范对应了哪些测试用例、这些测试是否全部通过、相关代码覆盖到了什么程度。有了这份报告发版不再靠人心担保验收变得有据可依。2.3 为什么选 openspec-cn 而不是自研或商业闭源方案在开源工具里有一定规模的企业通常会纠结是自己造一个轮子还是直接用开源工具还是采购商业闭源方案。我帮人做过技术选型调研也在这三种方案里折腾过。最终选定 openspec-cn原因还挺实在的。先说自研。SDD 看着不复杂无非就是“写规范 → 生成测试 → 做追溯”。但真的去实现的时候你会发现每个环节都有不少坑。比如规范怎么解析、验收条件怎么设计得既机器可读又人类可读、生成的测试骨架怎么适配不同业务模块的结构——这些细节没有几个人踩过坑是设计不好的。真自研的话至少要一两个月的人力投入还未必好用不如直接用成熟方案。再说商业闭源方案。市面上确实有企业级的 SDD 工具功能强大而且有技术支持。但是价格不菲同时还有一个隐性问题——规范数据被锁定在别人的平台上。规范是你的核心资产如果哪一天不续费了这些资产就不好导出了。openspec-cn 是开源的规范文件就是普通的 Markdown 文件不绑任何私有格式随时可以完整迁移。最后是生态和活跃度。openspec-cn 本身源于一个开源规范驱动项目中文适配做得不错文档对国内开发者友好社区也在持续更新。再加上它跟 CI/CD 集成很方便——GitLab 或 GitHub Actions 里跑几条命令就能接入——这让我在实际推广时的说服成本低了很多。当然选型这件事没有绝对的对错。如果团队已经有非常成熟的规范体系或者对数据隔离有极其严格的要求也可以考虑在 openspec-cn 的基础上做二次封装。毕竟它是开源项目扩展和定制是自由的这本身就是它的一大优势。3. 实战基于 openspec-cn 落地一个完整功能3.1 环境准备与项目初始化理论讲再多不如直接跑一遍。下面我用一个实际的业务场景——用户管理系统里的“登录日志查询”功能——来完整演示 openspec-cn 的落地过程。先准备环境。openspec-cn 官方提供了两种安装方式我推荐用 npm 全局安装因为后续的命令行交互会更方便。安装过程很简单npm install -g openspec-cn openspec --version执行完这两条命令如果能看到版本号说明安装成功。如果你所在的团队已经用了 Docker也可以拉官方镜像用 Docker 方式运行在 CI 环境下会方便一些不过本地开发我还是推荐直接装 npm 包。初始化项目时运行openspec init命令它会自动问几个问题项目名称是什么、代码用的什么语言、测试框架选哪个。回答完之后openspec-cn 会在当前目录生成一套项目骨架。生成出来的目录结构大概是这样的my-project/ ├── specs/ │ ├── auth/ │ │ └── spec.md │ └── user/ │ └── spec.md ├── src/ ├── tests/ ├── openspec.config.yaml └── .gitignoreopenspec.config.yaml是几个命令的配置文件里面的核心字段是语言和测试框架。注意这套骨架跟目标语言的框架绑定得很松所以无论你用的是 Python 还是 TypeScript 还是 Java流程上都是一样的。3.2 编写第一个规范文件环境初始化好之后打开specs/auth/login-log.md这里我新建了一个登录日志功能目录开始写第一版规范。我先给你看一个我实际用过的规范文件结构然后解释它各个部分的设计意图。--- id: AUTH-LOG-001 title: 登录日志按时间范围查询 module: auth priority: high dependencies: [] --- # 登录日志按时间范围查询 ## 描述 用户登录系统后需要按其选择的起始时间和结束时间来查询登录日志。 ## 验收条件 ### AC1: 按开始时间和结束时间过滤日志 - Given 系统中有 10 条登录日志分布在 2024-04-01 至 2024-04-10 之间 - And 当前用户是系统管理员 - When 用户发起查询请求起始时间为 2024-04-01结束时间为 2024-04-05 - Then 系统返回这段时间内的所有登录日志 - And 返回结果按时间倒序排列 - And 返回结果包含日志总数值为 5 ### AC2: 时间范围为空时返回错误 - Given 用户发起查询请求但未填写起始时间或结束时间 - When 请求到达服务端 - Then 系统返回错误码 PARAM_MISSING - And 错误信息为 startTime and endTime are required ### AC3: 起始时间晚于结束时间时返回错误 - Given 用户发起查询请求 - And 起始时间为 2024-04-10结束时间为 2024-04-01 - When 请求到达服务端 - Then 系统返回错误码 PARAM_INVALID - And 错误信息为 startTime must be earlier than endTimefrontmatter 里的id是这条规范的唯一标识后面写代码和生成报告都会引用它所以格式要避免歧义。module用来做模块归类方便同一模块的规范聚在一起看。验收条件用的是 Given/When/Then 结构化表达这是一种行为驱动开发BDD的标准写法好处是普通人能读懂、机器也能解析。每个 AC 一个编号一个规范可以有多条 AC每条 AC 都是一个独立的可验证单元。3.3 规范验证与测试骨架生成写完规范文件之后先做规范自检。这一步不容跳过因为规范文件的格式哪怕有一点小问题后面生成测试骨架的时候就会暴露出来。运行验证命令openspec validate这个命令会遍历specs/目录下的所有文件检查格式是否合法、ID 是否唯一、引用的模块是否存在、验收条件的结构是否完整。如果规范里有错误命令行会直接告诉你错误发生在哪个文件的哪一行。我第一版的规范文件有一个问题——AC3 的Given步骤里忘了写And关键字结果验证器直接报错。这说明校验本身是有实际作用的不是走过场。验证通过之后运行生成命令openspec generateopenspec-cn 会根据每条 AC 生成测试骨架。以 AC1 为例生成的测试代码会是这样我这里是 Python pytest 环境的输出示例def test_ac1_time_range_filter(): # Given 系统中有 10 条登录日志分布在 2024-04-01 至 2024-04-10 之间 # TODO: 准备测试数据 # And 当前用户是系统管理员 # TODO: 设置管理员身份 # When 用户发起查询请求 # TODO: 发起请求 # Then 系统返回这段时间内的所有登录日志 assert False # TODO: 实现断言 # And 返回结果按时间倒序排列 assert False # TODO: 实现断言 # And 返回结果包含日志总数值为 5 assert False # TODO: 实现断言生成的骨架里有清晰的步骤对应关系开发要做的就是把 TODO 注释替换成真实的业务调用和数据准备代码。这里有个容易忽略的细节assert False是用来保证测试在填充真实逻辑之前一定是失败的防止有人忘了填充就假装测试通过。3.4 填充测试与实现业务代码测试骨架有了接下来的工作就是填实现。我建议的顺序是先填测试数据准备代码再填请求调用代码最后填断言。还是以 AC1 为例。我实际填充完的测试代码大概是这样的def test_ac1_time_range_filter(): # Given 系统中有 10 条登录日志分布在 2024-04-01 至 2024-04-10 之间 for day in range(1, 11): seed_login_log(f2024-04-{day:02d}, user_id1) # And 当前用户是系统管理员 login_as(admin) # When 用户发起查询请求 resp client.get(/api/login-logs, params{ startTime: 2024-04-01, endTime: 2024-04-05 }) # Then 系统返回这段时间内的所有登录日志 assert resp.status_code 200 logs resp.json()[logs] assert len(logs) 5 # And 返回结果按时间倒序排列 timestamps [log[createdAt] for log in logs] assert timestamps sorted(timestamps, reverseTrue) # And 返回结果包含日志总数值为 5 assert resp.json()[total] 5写测试的过程中我逐渐体会到 SDD 的真正价值因为规范已经把行为描述得足够具体写测试的时候几乎不需要做“这个功能应该是怎样”的猜测只需要把规范翻译成代码就行。这样的开发过程是消耗脑力最少的但也最不允许跳步。接下来是业务代码。实现并不复杂我写了一版如下def query_login_logs(start_time: str, end_time: str, page: int 1, page_size: int 20): if not start_time or not end_time: raise BizError(PARAM_MISSING, startTime and endTime are required) if start_time end_time: raise BizError(PARAM_INVALID, startTime must be earlier than endTime) logs, total login_log_repo.query_by_time_range( start_time, end_time, page, page_size ) # 按时间倒序 logs.sort(keylambda x: x[createdAt], reverseTrue) return {logs: logs, total: total}注意这个实现里有几个细节是直接照着规范的 AC2、AC3 来写的参数为空时返回PARAM_MISSING起止时间不合法时返回PARAM_INVALID。正是因为规范写得细实现也不用反复跟产品确认边界行为。3.5 在 MATLAB/Simulink 场景下的延伸应用我注意到最近“MATLAB 规范驱动开发”这个关键词热度很高这里专门说说 openspec-cn 或者 SDD 的思路怎么跟 MATLAB/Simulink 结合。在嵌入式控制领域尤其是汽车电子、机器人这类对安全性和可追溯性要求极高的场景规范驱动开发本身就是行业标准要求的一部分只是传统工具链里更多靠人工写文档、做矩阵回溯。MATLAB 体系里有自己的需求管理工具也可以跟 Simulink 模型做关联。但我们用 openspec-cn 跑下来发现它可以作为需求管理的轻量前置层——先用 openspec-cn 把控制逻辑的行为规范写清楚再做 Simulink 建模生成的测试骨架也能转换成 MATLAB 的测试脚本来跑模型验证。具体来说比如你要给一个电机的转速控制器写规范可以在 openspec-cn 里定义这样一条验收条件### AC1: 当转速偏差超过 10% 时控制器输出饱和 - Given 当给定转速为 3000 RPM实际转速为 2500 RPM - And 偏差持续超过 500ms - When 控制器执行一个步长 - Then 控制输出达到最大值 - And 控制输出保持最大值直到偏差小于 5%这条规范生成测试骨架后你可以把测试逻辑翻译成 MATLAB 脚本用 Simulink 对模型做 SIL软件在环验证跑出来的结果再反向映射回规范。这样从需求到模型到代码的链路就打通了。不过要提醒一句openspec-cn 本身不是 Simulink 的专用插件它跟 MATLAB 的集成需要做一些胶水工作——比如用一个 Python 脚本把 pytest 风格的测试骨架转成 MATLAB 脚本或者在 CI 里同时调用openspec generate和simulink test。这部分的工程量取决于你团队的自动化程度但至少规范管理和追溯这块openspec-cn 能帮上大忙。3.6 追溯报告与 CI/CD 集成代码实现完成测试全部跑绿之后还有一个重要步骤生成追溯报告。运行openspec trace这个命令会扫描测试文件和代码生成一份 MySQL 报告文件用浏览器打开就能看到每条规范的状态——测试是否全部通过、代码覆盖到哪些函数、最近一次验证是什么时候。有时候为了汇报我也把报告导出成 CSV 或 PDF 文件发给项目干系人数据很清晰。CI/CD 集成更是 openspec-cn 的优势项目。我现在的项目在 GitLab CI 里加了三个阶段先跑openspec validate确保规范格式没问题再跑测试最后跑openspec trace生成追溯报告并上传到构建产物。这样任何一次代码提交只要有一条测试挂了流水线就直接失败发布流程自动阻断。下面是一个 GitLab CI 配置的简化示例stages: - validate - test - trace validate-spec: stage: validate script: - openspec validate run-tests: stage: test script: - pytest tests/ needs: [validate-spec] generate-trace: stage: trace script: - openspec trace --export report.html artifacts: paths: - report.html needs: [run-tests]4. 常见问题与排查技巧实录4.1 我在实战中遇到的高频问题速查用了 openspec-cn 一段时间我整理了几个高频问题和对应的排查办法。下面是我总结的速查表问题现象可能原因排查与解决办法openspec validate报 YAML 解析错误frontmatter 里冒号后面没加空格或缩进了 Tab改用空格缩进确认key: value冒号后有空格生成的测试骨架出现重复测试函数名不同 AC 的文字描述过于相似导致生成器归一化后撞名检查规范文件给容易混淆的 AC 加上更独特的描述openspec trace找不到测试测试文件路径不在tests/下或测试函数命名不符合约定调整目录结构或修改openspec.config.yaml里的测试路径配置同一规范改了需求但测试没变规范的 AC 更新后没有重新运行generate每次规范变更后都要重新生成测试骨架不能手工改旧测试生成了很多测试但 CI 跑得很慢规范粒度太细一条功能拆成了太多 AC合并相关性强的 AC保持规范在可读性和可测试性之间的平衡这份速查表里最重要的一条经验就是规范变更后一定不要手动改测试代码而是重新运行openspec generate。我踩过这个坑——有一次因为需求变更我手动改了测试断言结果后来又运行了一次generate手动的修改全被覆盖了白干半天。从那以后我就记住了测试骨架是“生成物”不是“人写物”一切变更要从规范源头发起。4.2 团队落地时的协作与评审机制设计工具层面的问题好解决真正难的是让团队从“代码驱动”切换成“规范驱动”的思维模式。在我们团队推广 openspec-cn 的过程中有几条协作机制亲测有效分享给准备落地的朋友。第一规范评审单独走一个流程不要跟代码评审混在一起。每个迭代开始前指定一个时间专门过规范和产品对需求开发、测试、产品三方坐在一起对着规范文件逐条过。这个过程虽然会增加一些例会时间但后面代码评审的时间会大幅减少因为很多矛盾在规范阶段就已经暴露并被解决了。总体算下来人力成本其实是降低的。第二规范文件要纳入代码审查范围但是审查重点不是“写得通不通顺”而是“行为是否完整”。建议做一个简单的检查清单——这个功能涉及哪几个输入参数每个参数的边界值都处理了吗异常场景覆盖了吗跟其他模块的交互约定明确了吗这份清单能避免开发人员把规范写成代码的简单复述。第三追溯报告要放到项目周报或者看板里。我们团队现在每个功能完成开发之后必须附上openspec trace生成的规范覆盖率数据。不需要特别复杂的指标就看两点规范条目数、通过率。通过率低于100%就说明还有未完成的行为功能不能算开发完。这个机制简单粗暴但非常有效它让“完成”这个词有了一个可验证的含义。4.3 几个值得记住的实操细节最后分享几个在实操中总结出来的小技巧。这些细节不一定写在官方文档里但都很实用。规范文件里的id命名要具有业务含义。我见过有人用纯数字编号20240415001看起来没问题但一旦规范多起来你会发现自己根本不记得这个编号对应的是哪个功能。我建议方案是模块前缀-功能名-序号比如AUTH-LOG-001表示认证模块的登录日志功能的第一条规范。这个习惯适用于所有团队不需要额外工具只是命名规范而已。验收条件的 Given 部分要写得像测试数据准备说明。很多人写 Given 时只写“系统有日志数据”但这个描述太模糊机器生成测试骨架时只能写个 TODO。更好的写法是写明数据条数和分布比如“系统中有 10 条登录日志分布在 2024-04-01 至 2024-04-10 之间”这样生成的测试骨架就非常接近最终可运行的代码。规范写得好不好看生成的测试骨架质量就知道了。再说一个关于变更管理的经验。用 openspec-cn 之后需求变更是逃不掉的但 SDD 让变更的成本变得极其可控。当产品改需求时我们要求他们必须先改规范文件里的对应描述然后跑openspec validate重新生成测试骨架。如果新需求打破了旧的行为约定验证阶段就会暴露出来开发和测试都能提前知道影响面而不是像传统流程那样测试最后才发现用例全挂了。这种“变更前置感知”的能力是我认为 openspec-cn 带来的最大价值之一。另外如果你们项目里同时用到多门语言建议把 openspec 的配置统一进 CI 的公共模板里不要在多个仓库里重复维护。我见过一个团队在三个微服务仓库里各写了一份配置结果规范结构很快就不一致了最后统一管理又花了不少时间。一上来就把它当成基础设施来建设能省掉后面很多麻烦。5. 写在最后SDD 这套方法论还能怎么扩展规范驱动开发的价值我实际用下来之后可以肯定地说不只是提升测试覆盖率那么简单。它本质上是把“需求”从无结构的自然语言变成了可验证的结构化描述让团队里所有人对“做什么”有一个共识版本再围绕这个共识版本来协同。openspec-cn 让我惊喜的地方在于它把这些方法论层面的东西落成了具体的命令行工具和配置文件不需要写一堆解释性文档跑一遍流程就全懂了。目前我正在尝试把 openspec-cn 用在算法服务的持续集成里——跟第 3.5 节提到的 MATLAB 场景类似算法模型的行为描述也能写成规范跑完训练后自动验证模型输出是否满足规范要求。另外还有一个方向是把规范文件跟 API 文档生成打通让接口文档从规范文件自动渲染减少手工维护文档的负担。这两个扩展方向我还在测试中等跑通了再来分享。如果你们团队正在纠结要不要上规范驱动开发我的建议是别搞大规划挑一个相对独立、边界清晰的功能模块先试点把从规范到测试到追溯报告的完整流程跑通让团队感受一下“按规范开发”和“按理解开发”的差别。只要第一炮打响后面推广就是水到渠成的事。如果你们已经在实践 SDD也欢迎多交流实际落地时的一些细节尤其是团队协作机制方面这一块我觉得国内公开分享的经验还不太多值得更多人一起填起来。