Skip to content

OpenSpec、Superpowers、SDD、TDD、BDD:一套更稳的研发方法

很多团队在讨论研发方法时,容易把几个概念混在一起:OpenSpecSuperpowersSDDTDDBDD。表面上它们都和“提高开发质量”有关,但如果不把角色分清楚,最后往往只剩下一句口号:要规范、要测试、要先设计。

真正有价值的问题不是“这些词是什么意思”,而是:

  • 它们分别解决什么问题
  • 它们为什么真的能起作用
  • 在一条真实研发链路里,它们应该怎么配合

这篇文章尝试把这几件事讲清楚。

先说结论:它们不是同一层东西

如果只用一句话概括,可以这样理解:

text
OpenSpec      = 用工件约束决策
Superpowers   = 用流程约束执行
SDD           = 先定义规格,再开始实现
TDD           = 先写测试,再推动代码落地
BDD           = 先描述行为,再对齐业务规则

它们并不是互相替代的关系,而是分布在不同层级。

  • BDD 更偏业务行为层
  • SDD 更偏需求和设计层
  • TDD 更偏编码和验证层
  • OpenSpecSDD 的落地载体
  • Superpowers 是一组把工程流程前置约束起来的执行机制

所以真正合理的关系不是“TDD 还是 SDD 二选一”,而是:

text
先用 BDD 说清行为
再用 SDD / OpenSpec 说清规格和边界
最后在实现阶段按需要使用 TDD 落地
同时用 Superpowers 管住整个执行过程

OpenSpec 解决的不是编码问题,而是“定义问题”

很多项目并不是死在“不会写代码”,而是死在“还没定义清楚就开始写”。

最常见的情况是这样:

text
一句需求
 -> 开始改代码
 -> 改到一半才发现边界不清
 -> 联调时发现影响面比预想大
 -> 文档、实现、测试三边失配

OpenSpec 要解决的就是这个问题。

它的核心机制不是“多写文档”,而是把关键决策显式化成一组工件。典型工件通常包括:

  • proposal.md:为什么做、范围是什么
  • design.md:怎么做、关键方案和边界是什么
  • tasks.md:按什么顺序落地、拆成哪些任务

这件事为什么有用?因为它把原本藏在脑子里、会议里、聊天记录里的决策,变成了可检查、可追溯、可续做的内容。

也就是说,代码不再直接从一句模糊需求长出来,而是从明确的规格工件长出来。

这本质上就是 Spec-Driven Development,也就是 SDD

Superpowers 解决的是“人和流程容易失控”

如果说 OpenSpec 更偏“做什么”,那 Superpowers 更偏“怎么做”。

它不是一个单独的方法,而是一组前置工作流。它的目标不是让人显得专业,而是把最容易犯错的工程行为拦在入口。

比如:

  • 新功能不是直接写,而是先 brainstorming
  • Bug 不是先猜修复,而是先 systematic-debugging
  • 实现不是随手改,而是尽量通过 test-driven-development
  • 完成不是主观宣称,而是先做 verification-before-completion

这套机制真正起作用的原因,在于它改变了默认路径。

默认路径通常是:

text
看到需求 -> 开写
看到报错 -> 开猜
改完代码 -> 说修好了

Superpowers 强制把它变成:

text
看到需求 -> 先澄清
看到问题 -> 先定位根因
开始实现 -> 先建立验证方式
准备交付 -> 先跑验证

这不是“更聪明”,而是“更不容易乱来”。

SDD 才是上层核心,TDD 只是实现阶段的重要手段

很多人一聊工程方法,注意力就会全部落到 TDD 上,好像只要先写测试,整个研发流程就自动变稳了。

这其实是把问题看窄了。

TDD 很重要,但它解决的是实现阶段的问题,比如:

  • 代码是否围绕可验证行为展开
  • 改动有没有回归保护
  • 接口行为是否被测试约束

但它解决不了这些更上游的问题:

  • 需求到底是什么
  • 哪些在范围内,哪些不在
  • 为什么选这个方案,不选另一个方案
  • 影响的是一个模块,还是多个仓库

这些问题,靠的是 SDD,也就是先把规格定义清楚,再进入实现。

所以如果非要问“这套方法的核心更像 TDD 还是 SDD”,我的判断是:

text
上层核心是 SDD
TDD 是实现阶段的局部强约束

这也是为什么 OpenSpec 这类东西通常要先于编码存在。

BDD 又是什么,它和 TDD 的边界在哪里

BDDBehavior-Driven Development,行为驱动开发。

它关注的不是内部类怎么拆、数据库怎么落,而是系统对外必须表现出什么行为。最常见的表达方式是:

text
Given 某个前置条件
When  发生某个操作
Then  应该得到某个结果

比如订单取消场景:

gherkin
Feature: 取消订单

  Scenario: 用户取消待支付订单
    Given 存在一个订单,状态为待支付
    And 当前操作人是下单用户本人
    When 用户发起取消订单
    Then 订单状态应变为已取消
    And 系统记录取消时间
    And 系统记录取消原因为用户主动取消

  Scenario: 用户取消已支付订单
    Given 存在一个订单,状态为已支付
    And 当前操作人是下单用户本人
    When 用户发起取消订单
    Then 操作应失败
    And 系统提示“当前订单状态不允许取消”
    And 订单状态保持不变

这段描述有一个很大的价值:产品、测试、开发都能读懂,而且读完之后更容易对齐。

所以:

  • BDD 更像是在定义业务行为和验收标准
  • TDD 更像是在用测试约束实现过程

可以粗暴理解成:

text
BDD 负责说清楚“系统应该怎么表现”
TDD 负责保证“代码真的这样表现”

一条完整链路里,它们怎么配合

如果把一个真实需求从进入到交付展开,大概会是这样一条链路:

text
需求进入
 -> BDD:先定义关键业务行为
 -> OpenSpec / SDD:再形成 proposal / design / tasks
 -> Superpowers:按合适流程推进探索、排障、实现、验证
 -> TDD:在实现阶段用测试驱动关键代码
 -> 验证完成
 -> 交付

这几个东西放在一起,角色就很清楚了。

第一层:BDD 负责行为对齐

它回答的是:

  • 在什么前提下允许做这件事
  • 触发后系统必须产生什么结果
  • 失败时系统应该如何反馈

如果这层没定义清楚,后面写出来的测试可能也只是“验证了错误的东西”。

第二层:OpenSpec / SDD 负责规格落地

它回答的是:

  • 这次改动的目标是什么
  • 影响哪些模块和接口
  • 为什么这样设计
  • 哪些内容不在当前范围

这层让“行为”进一步变成“工程可执行的设计和任务”。

第三层:Superpowers 负责过程约束

它回答的是:

  • 现在这类任务应该先按什么流程处理
  • 是继续探索,还是已经可以实现
  • 是根因没查清,还是已经可以改代码
  • 是真验证过了,还是只是主观觉得好了

这层防的是执行过程跑偏。

第四层:TDD 负责实现压实

它回答的是:

  • 代码是不是围绕可验证行为收敛
  • 改动会不会引入回归
  • 关键业务规则有没有被稳定保护

这层防的是实现阶段的漂移和脆弱性。

为什么这套组合在多仓项目里更有价值

在单仓、小系统里,很多问题还可以靠人脑兜住。
但在多仓、历史包袱重、领域逻辑复杂的项目里,靠“感觉”做事的成本会快速放大。

比如一个典型的 Java 多模块或多仓工作区,经常会同时存在:

  • 后端服务仓
  • 对外 API 仓
  • 管理端前端仓
  • C 端前端仓
  • 配套脚本和文档仓

这时候最容易出的问题不是“不会写代码”,而是:

  • 改动边界不清
  • 接口变了但上下游没同步
  • 文档没更新
  • 只测了本仓,没考虑联动影响

在这种环境下:

  • BDD 帮你先固定业务规则
  • OpenSpec 帮你固定影响范围和设计决策
  • Superpowers 帮你限制执行动作不要失控
  • TDD 帮你给实现加上稳定保护

也就是说,项目越复杂,这套组合越不是形式主义,反而越像一套必要的减灾机制。

一个更具体的案例:取消订单功能

假设现在来了一个需求:

“支持用户取消待支付订单,但已支付订单不允许直接取消。”

没有这套方法时,很多团队会这样推进:

text
收到需求
 -> 开始改 Controller / Service
 -> 改 DTO
 -> 写两个 if 判断
 -> 联调时才发现错误码、提示文案、审计字段都没统一
 -> 测试补不全

而如果把这几个方法叠起来,路径会变成这样:

第一步:先用 BDD 定义行为

  • 待支付订单允许取消
  • 已支付订单禁止取消
  • 只能本人取消
  • 成功时记录取消时间和原因
  • 失败时返回明确提示,且状态不变

第二步:再用 OpenSpec 固化规格

proposal.md 里明确:

  • 为什么做
  • 本次仅支持待支付取消
  • 不涉及退款链路改造

design.md 里明确:

  • 涉及哪些接口
  • 状态校验放在哪层
  • 审计字段怎么落
  • 错误码和提示文案怎么统一

tasks.md 里拆成:

  • API 调整
  • Service 实现
  • 单元测试
  • 联调验证
  • 文档更新

第三步:实现阶段再用 TDD 压实

测试命名可能像这样:

java
shouldCancelOrderWhenStatusIsPendingPayment();
shouldRejectCancellationWhenOrderIsPaid();
shouldRejectCancellationWhenOperatorIsNotOrderOwner();

这时测试就不只是“补一个流程”,而是在给前面定义好的业务规则做落地保护。

第四步:交付前用 Superpowers 强制验证

比如:

  • 先确认根因和边界是否清楚
  • 先运行最小相关测试集
  • 再跑模块级校验
  • 最后回看文档、接口、任务状态是否一致

这样整条链路才真正闭环。

它为什么不是形式主义

很多人第一次接触这种方法,会本能地觉得“步骤变多了”。
但步骤变多,不等于形式主义。关键要看它拦住了什么成本。

这套方法真正减少的是三类高成本错误:

  • 方向错了,写完才发现需求理解错
  • 范围漏了,联调时才发现影响不止一个模块
  • 验证弱了,改完就说好了,结果线上回归

如果一个流程只是增加文档,没有减少返工,那它确实是负担。
但如果一个流程能把返工、回归、沟通偏差明显前置,那它本质上是在节省总成本。

所以判断它值不值得用,不能只看“前面多写了多少”,而要看“后面少返了多少”。

最后总结

把这几个概念拆开之后,整个结构其实并不复杂:

text
BDD:定义行为
SDD:定义规格
OpenSpec:承载规格工件
Superpowers:约束执行流程
TDD:压实实现质量

它们各自负责不同的事,但组合起来,刚好覆盖了一条研发链路里最容易失控的几个点:

  • 需求没对齐
  • 边界没说清
  • 执行太随意
  • 测试太薄弱
  • 验证不完整

如果只让我保留一句最重要的话,那就是:

text
先用 BDD 和 SDD 管住方向
再用 Superpowers 和 TDD 管住落地

真正稳定的交付,从来不是“写代码更快”,而是“在正确方向上,把代码写对、测对、交付对”。

Last updated: