告别 Vibe Coding:用 SDD 让 AI 编程提效 50%,三工具实战对比
一句话摘要:通过将开发重心前置到"写规范",而非直接让 AI 写代码,我在一个后端项目中把原本需要 5 天的工时压缩到 2.5 天,并系统对比了手搓 SDD、OpenSpec、Superpowers 三种路径。本文是完整的实践复盘。
目录
- [0. 背景与数据](#0. 背景与数据 "#0-%E8%83%8C%E6%99%AF%E4%B8%8E%E6%95%B0%E6%8D%AE")
- [1. 为什么需要 SDD](#1. 为什么需要 SDD "#1-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-sdd")
- [2. 轻量级实践:手搓 SDD](#2. 轻量级实践:手搓 SDD "#2-%E8%BD%BB%E9%87%8F%E7%BA%A7%E5%AE%9E%E8%B7%B5%E6%89%8B%E6%90%93-sdd")
- [3. 工程化实践:OpenSpec](#3. 工程化实践:OpenSpec "#3-%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5openspec")
- [4. 高阶实践:Superpowers](#4. 高阶实践:Superpowers "#4-%E9%AB%98%E9%98%B6%E5%AE%9E%E8%B7%B5superpowers")
- [5. 工作原理深度分析](#5. 工作原理深度分析 "#5-%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E6%B7%B1%E5%BA%A6%E5%88%86%E6%9E%90")
- [6. 综合总结与选型建议](#6. 综合总结与选型建议 "#6-%E7%BB%BC%E5%90%88%E6%80%BB%E7%BB%93%E4%B8%8E%E9%80%89%E5%9E%8B%E5%BB%BA%E8%AE%AE")
0. 背景与数据
近期在负责一个用户行为分析后台 的开发,覆盖数据大盘、漏斗分析、消息效果、数据质量四个模块,典型的"重 SQL 聚合、重 CRUD、快迭代"场景。借此机会,我系统性地尝试了将 SDD(Spec-Driven Development,规范驱动开发) 引入 AI 辅助编程工作流。
工时数据
| 阶段 | 方式 | 实际工时 | 对比基准 |
|---|---|---|---|
| 基准 | 纯人工开发(不借助 AI) | 5 天 | --- |
| 第一阶段 | 手搓 SDD(CLAUDE.md + Markdown Spec) | 2 天 | 提效 超60% |
| 第二阶段 | OpenSpec 架构重写 | 1 天(含学习成本) | 探索工程化边界 |
| 第三阶段 | Superpowers 架构重写 | 1 天(含学习成本) | 探索执行效率上限 |
提效数字只是表象,更重要的发现是:SDD 提升的是代码质量的下限,而不只是速度。
1. 为什么需要 SDD
随着大模型代码生成能力的提升,开发者越来越多地依赖 AI。但当项目复杂度超过某个阈值,传统的"凭感觉编程(Vibe Coding)"会暴露出明显的痛点:
sql
问题 1:上下文易丢失
面对数十个文件的项目,AI 经常"忘记"前置条件,写着写着就跑偏。
问题 2:架构规范难以约束
AI 容易写出面条代码,忽略团队规约------随意的跨库 JOIN、
未处理全局异常、返回格式不统一......
问题 3:幻觉频发且难排查
通过来回对话让 AI 猜意图,生成代码常带有隐蔽 Bug,
人工排查成本极高。
SDD 是什么
SDD(Spec-Driven Development,规范驱动开发) 的核心哲学:
代码不再是一等公民,规范(Spec)才是------代码只是规范的衍生品。
通过将开发重心前置,用严谨的文档约束 AI 的行为边界,解决上述痛点,让开发者的角色从"代码打字员"向"系统架构设计者"转变。
开发流向从:
需求 → 直接撸代码(AI 来写)→ 人工修 Bug
变为:
需求 → 写规范(人工主导)→ 让 AI 按规范生成代码 → 人工 Review
主流工具一览
| 工具 | 出品方 | 核心特点 |
|---|---|---|
| SpecKit | GitHub | Constitution → Specify → Plan → Tasks 四段式工作流,模板驱动 |
| OpenSpec | Fission-AI | specs/(当前状态)+ changes/(变更提案)双目录,支持 20+ AI 工具,有 CLI 状态机 |
| Superpowers | obra | 内置 TDD 质量门控 + subagent 并行执行,流程最严格 |
三个工具都有一定学习曲线。当时项目工期只有 5 天,在不熟悉SDD框架的前提下,第一阶段优先选择零工具依赖的手搓 SDD,完成交付后再利用额外时间探索 OpenSpec 和 Superpowers。
2. 轻量级实践:手搓 SDD
整体思路
人工把控基建 + Markdown 规范 + Claude Code 生成,分四步走:
css
第一步:人工打底 → 搭项目骨架,生成数据层代码
第二步:立规矩 → 写 CLAUDE.md,把架构铁律硬编码为 AI 约束
第三步:写图纸 → 编写 Feature Spec,融合业务规则 + 数据逻辑
第四步:产代码 → Claude Code 读规范生成,人工 Review 验收
第一步:人工打底
AI 不了解公司内部的私有框架、Maven 仓库规范或特定 CI/CD 流程。项目冷启动必须由人工主导:
- 应用公司脚手架拉取项目骨架(包含内部封装的 MVC 框架、多数据源、定时任务等依赖)
- 人工执行代码生成器(MyBatis-Plus Generator)生成底层 Entity 和 Mapper
bash
src/main/java/
└── generate/ ← 全部由代码生成器产出,禁止手动编辑
├── entity/
│ ├── UserEntity.java # 用户表
│ ├── ActionEventEntity.java # 行为事件表
│ ├── MessageRecordEntity.java # 消息记录表
│ └── DailyStatsEntity.java # 每日统计汇总表
├── mapper/
└── service/
这一步确保底层数据模型与真实 DB 表结构绝对对齐,不给 AI 留底层结构上的自由发挥空间。
第二步:建立全局防腐层(CLAUDE.md)
在项目根目录严格维护 CLAUDE.md(Claude Code 的全局上下文规范文件),将所有架构铁律硬编码为项目规则。这是整个方案最关键的一步。
CLAUDE.md 涵盖的核心约束(共 12 节):
| 节 | 约束内容 |
|---|---|
| §0 | SDD 开发流程(最高优先级):任何代码修改必须先改 Spec 文档,再改代码 |
| §1 | 设计哲学:分层隔离、防御性编程、可追溯性、规范先行 |
| §2 | API 规范:URL 结构、统一响应格式、分页结构、DTO 命名、Swagger 注解 |
| §3 | 代码组织:包结构、各层命名与职责、Controller/Service/Mapper 标准写法 |
| §4 | 多数据源规范:user-db 和 biz-db 的路由注解使用,禁止同一事务中混用 |
| §5 | 异常处理:错误码分段规则、GlobalExceptionHandler 的三类拦截要求 |
| §6 | 数据模型规范:Entity 标准字段,字段命名一致性约束 |
| §7--12 | 日志、定时任务、generate 目录使用、多环境配置、技术栈、安全规范 |
关键约束示例------多数据源规范:
markdown
## 四、多数据源规范
| 数据源标识 | 数据库 | 用途 |
|------------|--------|------|
| `user-db` | MySQL | 用户认证、账户数据 |
| `biz-db` | 业务库 | 行为事件、业务数据 |
跨库查询禁止在同一个事务中混用两个数据源。
如果业务需要聚合两个库的数据,在 Service 层分别调用各自数据源的
Service,在内存中合并结果。
Claude Code 读取此文件后,便会在整个会话中自动遵守这些约束。实际验收中,AI 未出现任何跨库 JOIN 或在 Controller 写业务逻辑的情况。
第三步:构建高内聚的 Feature Spec
过去,产品 PRD 和后端 SQL 逻辑是分离的,这会导致 AI 理解割裂。这一步的核心是:将 PRD 的业务展示规则与底层 SQL 计算逻辑融合,转化为高内聚的 Markdown Feature Spec。
.specs/ 目录结构:
csharp
.specs/
├── 00-global-constraints.md # 技术规范(补充 CLAUDE.md)
├── 01-prd.md # 产品需求(功能边界、字段规则、筛选条件)
├── 02-data-logic.md # 全局指标 SQL 定义(唯一真相来源)
├── feature-01-dashboard.md # 数据大盘
├── feature-02-funnel.md # 转化漏斗分析
├── feature-03-msg-effect.md # 消息效果分析
├── feature-04-data-quality.md # 数据质量页
└── feature-05-etl-job.md # 每日统计 ETL Job
Feature Spec 的高内聚体现 (以 feature-01-dashboard.md 为例):
markdown
# Feature Spec: 数据大盘 (Dashboard)
## 1. Context(背景与目标)
帮助运营团队追踪核心 OKR 的达成情况,并在指标下滑时快速定位根因。
## 2. Requirements(业务规则)
- 主 KPI:期间活跃用户数、期间转化任务数
- 漏斗模块:注册用户数 / 搜索用户数 / 浏览结果用户数 / 发起转化用户数 / 持续跟进用户数
## 3. Data & Logic(数据与核心逻辑)
> API 读取数据源:所有统计类 API 读取预聚合统计表 stats_daily,不直查源表。
-- 主 KPI:时间范围内的汇总值
SELECT
SUM(active_user_count) AS active_user_count,
SUM(conversion_task_count) AS conversion_task_count
FROM stats_daily
WHERE stat_date >= #{startDate} AND stat_date < #{endDate};
## 4. Constraints(技术约束)
1. API 层所有查询均走 @DS("biz-db") 的统计表,单库读取,无跨库操作
2. 漏斗数据需返回 List,若统计表中某天数据缺失,Service 层补 0 返回
关键设计:Feature Spec 包含了 PRD 描述 + SQL 逻辑 + 技术约束,让 AI 拿到的是一张完整的施工图纸,而非片段信息。
第四步:Agent 生成与人工验收
将 Feature Spec 和 CLAUDE.md 交由 Claude Code 读取,AI 自动生成分层清晰的代码:
java
// 生成的 Controller ── 完全符合规范,无任何业务逻辑泄漏
@Api(tags = "数据大盘")
@RestController
@RequestMapping("/api/biz/dashboard")
@RequiredArgsConstructor
@Validated
public class DashboardController {
private final DashboardService dashboardService;
@ApiOperation("获取大盘 KPI 汇总数据")
@GetMapping("/kpi")
public JsonResult<DashboardKpiResp> getKpi(@Valid DateRangeReq req) {
return new JsonResult<>(dashboardService.getKpi(req));
}
@ApiOperation("获取漏斗按日明细(折线图数据源)")
@GetMapping("/funnel")
public JsonResult<List<FunnelDailyResp>> getFunnel(@Valid DateRangeReq req) {
return new JsonResult<>(dashboardService.getFunnel(req));
}
}
java
// 全局异常处理器 ── AI 一次生成,精确覆盖 CLAUDE.md 中要求的三类拦截
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
// 业务异常 → HTTP 200,code=业务错误码
@ExceptionHandler(BusinessException.class)
public JsonResult<?> handleBusinessException(BusinessException e) { ... }
// 参数校验失败 → HTTP 400
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
public JsonResult<?> handleValidationException(BindException e) { ... }
// 未预期异常 → HTTP 500,不暴露堆栈
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public JsonResult<?> handleUnexpectedException(Exception e) { ... }
}
开发者退至"Reviewer"角色,专注进行:
- Code Review:验证分层是否正确、SQL 是否安全(
#{}不用${})、多数据源路由是否准确 - API 测试:对照 Feature Spec 验收接口行为
- Bug 修复原则:遇到缺陷不直接改代码,而是先修改 Spec 后让 AI 重新生成,保持文档与代码强同步
小结:轻量级 SDD 的效果
| 维度 | 传统 Vibe Coding | 轻量级 SDD |
|---|---|---|
| AI 输出质量 | 层次混乱,需大量人工修正 | 符合规范,直接可用 |
| 上下文管理 | 依赖长对话,易丢失 | 依赖文件(CLAUDE.md + Spec),随时加载 |
| Bug 修复 | 直接改代码,文档与代码脱节 | 改 Spec → 重新生成,文档代码同步 |
| 可维护性 | 后人看不懂 AI 的意图 | Spec 即文档,意图清晰 |
| 学习曲线 | 无额外成本 | 需要学习写 Spec 的方法 |
工时对比 :纯人工约需 5 天;轻量级 SDD 实际耗时 2.5 天 ,提效约 50%。
核心收益不只是速度,而是质量的下限得到了保障。 AI 在 CLAUDE.md + Spec 的约束下,没有写出任何跨库 JOIN,没有在 Controller 里写业务逻辑,全局异常处理器一次生成即符合要求。
3. 工程化实践:OpenSpec
在完成业务交付后,我利用额外时间用 OpenSpec 对同一套需求做了架构重写。相比手搓 SDD,OpenSpec 的核心差异在于:它是一个有工程化约束的工作流框架,而不只是"让你写 Markdown 规范"的方法论。
接入
OpenSpec 的文件接入非常轻量,在项目目录下初始化 openspec/ 并配置极简的 config.yaml:
yaml
schema: spec-driven
# 可选:注入项目上下文(CLI 会把这段内容透传给每个工件生成指令)
context: |
Tech stack: Spring Boot 2.x, MyBatis Plus 3.5
多数据源:user-db (MySQL) / biz-db (业务数据库)
禁止跨库事务,Service 层内存合并
# 可选:per-artifact 规则
rules:
tasks:
- 每个任务不超过 30 分钟工作量
重要提示:OpenSpec 不只是 Markdown 文件 + Skill 提示词,它实际上是一个三层系统(详见第 5 节原理分析),CLI 是驱动整个工作流的状态机。
OpenSpec 约定了两个核心目录,体现了它最核心的设计思想:
bash
openspec/
├── config.yaml # CLI 读取:schema + 项目上下文 + per-artifact 规则
├── specs/ # 系统"现在是什么"------已实现的能力(稳定)
└── changes/ # 系统"正在变成什么"------飞行中的变更
└── archive/ # 已完成的变更(归档,保留完整决策链路)
specs/ 代表"系统现在是什么",changes/ 代表"系统正在变成什么",两者分离使每个变更都有清晰的生命周期。
四层工件:一次变更的完整生命周期
通过 /opsx:propose 指令,OpenSpec 为每个变更自动生成四层级联工件:
vbnet
proposal.md ──→ Why & What:为什么做这个变更,改了哪些能力
design.md ──→ How:技术决策、风险取舍、待解问题
specs/*.md ──→ 行为契约:用 WHEN/THEN 格式描述可验收的系统行为
tasks.md ──→ 施工清单:代码级的原子任务,AI 按 task 逐项实现
proposal.md(提案)------回答"为什么做":
markdown
## Why
当前后台无法追踪核心 OKR 的达成情况,也无法在未达标时快速定位根因。
需要一套完整的运营数据后台,覆盖数据大盘、转化漏斗、消息效果、数据质量。
## What Changes
- 新增数据大盘 API:支持时间筛选,返回主 KPI 及五步漏斗每日数据
- 新增消息效果 API:周维度比率仪表盘、每日明细、发送漏斗
- 新增数据质量 API:按时间范围统计联系人/公司回复率
- 新增内容列表 API:多条件筛选,分页展示(P1)
## Impact
- 新增 API:约 6 个业务接口
- 数据来源:user-db(用户注册数据)+ biz-db(行为事件),跨库 Service 层内存合并
- 无破坏性变更:全部为新增端点
design.md(技术设计)------回答"怎么做",强制显式化关键决策:
markdown
## Decisions
**决策 1:漏斗数据按天返回列表**
返回 List<DailyDataPoint>,前端负责渲染折线图。后端不做图表逻辑。
**决策 2:消息效果过滤在 SQL 层**
按 reply_type 字段分类,WHERE reply_type IN (...),不在内存过滤。
**决策 3:滞后型指标的处理**
邮件/消息送达、打开、回复数据通常在发送后数小时到数天才最终确定。
Job 每天统计"前一天"截至运行时的累计值(非最终值),接受该近似,
通过允许补跑 Job 来更新历史行。
## Risks / Trade-offs
- [风险] 业务数据库按天聚合查询性能 → 确认时间字段索引;必要时加 Redis 缓存
- [取舍] 漏斗"注册用户"不关联搜索行为,统计维度是"当天注册",简化实现
## Open Questions
1. 消息打开率:业务库是否有 open tracking 记录?对应哪张表?
2. "持续跟进用户":判断条件是什么?对应哪张表的哪个字段?
specs/dashboard-overview/spec.md(需求规格)------回答"做到什么程度":
markdown
### Requirement: 五步漏斗每日数据查询
系统 SHALL 返回选定时间范围内每日的五步漏斗用户数,用于绘制趋势图。
#### Scenario: 漏斗指标某天无数据
- WHEN 某一天某个漏斗步骤无任何用户
- THEN 该日期该指标返回 0,不跳过该日期记录
#### Scenario: 跨数据源数据合并
- WHEN Service 层需要合并 user-db 的注册数与 biz-db 的其他漏斗指标
- THEN 分别查询两个数据源,以日期为 key 在内存中 merge,禁止跨库事务
这种格式直接逼迫开发者在写代码前想清楚边界场景------"数据缺失时是补 0 还是跳过?"这个问题若不在 Spec 阶段确认,后期 AI 生成的代码行为可能是任意的。
tasks.md(原子任务清单) ------由 /opsx:apply 将 spec 拆解为代码级任务:
markdown
## 3. 数据大盘(Dashboard)
- [x] 3.1 创建 DashboardService,实现按时间范围查询主 KPI(biz-db)
- [x] 3.2 实现五步漏斗每日数据查询(注册数来自 user-db,其余来自 biz-db,Service 层按日期 merge)
- [x] 3.3 实现漏斗 metrics 参数过滤逻辑(不传则全返回)
- [x] 3.4 在 DashboardController 中新增 GET /kpi 和 GET /funnel 端点
整个变更完成后,通过 /opsx:archive 归档,Spec 文件同步到顶层 specs/ 目录,代表"系统已实现了这些能力"。
第二个变更:ETL 聚合 Job
实际开发中,第一个变更(API 层)完成后发现一个架构问题:所有统计接口都在实时跨库查询原始表,聚合复杂、性能不可控。因此创建了第二个变更:每日聚合 Job,通过预写入统计表让 API 层退化为简单的单表查询。
这个变更的 proposal.md 精确描述了影响边界:
markdown
## Impact
- 新增文件:DailyStatsAggregationJob.java、DailyStatsService.java
- 不修改:现有消息通知 Job(职责独立)
- 间接影响:API 层读数据策略调整(design.md 层面,spec 行为不变)
- 无破坏性变更:现有功能保留不动
关键观察 :这里展示了 OpenSpec 的一个核心优势------"API 实现策略调整"属于 design.md 层面的变化 (How 变了),但 spec 定义的接口行为不变(What 不变)。这种区分在手搓 SDD 中很难做到,往往导致不必要的 spec 改动或者规范与代码的静默漂移。
踩坑:AI 不遵循工作流的问题
OpenSpec 最核心的一个痛点是:Skill 是提示词,不是代码锁,Claude 有时会"绕过"它------没完成 proposal 就直接写 design,或者没跑完 tasks 就跳到归档。
以下几种手段可以加强约束:
① CLAUDE.md 硬约束(最有效)
markdown
## 工作流约束(最高优先级)
- 禁止在没有对应 tasks.md checkbox 的情况下编写实现代码
- 任何功能实现前,必须确认 openspec/changes/<name>/tasks.md 存在且相关任务为 - [ ]
- 完成任务后必须立即将 tasks.md 中对应项从 - [ ] 改为 - [x]
② tasks 粒度做细
模糊任务("实现 service 层")容易被 Claude 一口气做完;细粒度任务("创建 DailyStatsService.java,只包含方法签名,不实现方法体")会强制分步确认。
③ config.yaml 注入 per-artifact 约束
yaml
rules:
tasks:
- 每个任务不超过 30 分钟工作量
- 验证类任务(4.x)必须列明验证标准
这比 CLAUDE.md 里的全局约束更精准,因为它是 per-artifact 的,CLI 在生成每个工件时都会注入。
④ 根本解法:人工把控节奏
在 propose 完成后、apply 开始前,应当明确说"我确认了,开始实现"。把节奏的控制权保留在人手里,而不是说"帮我做完整个流程"------这既是 OpenSpec 的设计意图,也是 SDD 的核心姿态。
OpenSpec vs 手搓 SDD
| 维度 | 手搓 SDD | OpenSpec |
|---|---|---|
| 规范入口 | 一个 CLAUDE.md 承载所有约束 | config.yaml + 四层工件分层约束 |
| 变更管理 | 无生命周期,直接改 Spec | changes/ → archive/ 流转,完整决策记录 |
| 需求与决策分离 | 混合在同一 Markdown | proposal / design / spec / tasks 四层独立 |
| 工作流标准化 | 完全自由,依赖经验 | /opsx:propose → /opsx:apply → /opsx:archive |
| 能力追踪 | 需人工维护文档与代码同步 | specs/ 代表系统当前状态,归档即同步 |
OpenSpec 最显著的收益是强制显式化决策。手搓 SDD 时,"为什么用内存合并而不是跨库 JOIN"这类决策往往隐性存在,散落在聊天记录里。OpenSpec 的 design.md 将它们变成有迹可查的工程文档。
4. 高阶实践:Superpowers
在 OpenSpec 探索之后,我用 Superpowers (由 obra 开发的 Claude Code 插件)进行了第三次架构实践。相比 OpenSpec 的多工件分布式模型,Superpowers 的核心设计理念更为直接:将完整的技术决策和实现蓝图预先生成,让 AI 在执行阶段最小化决策负担,通过内置的 TDD 循环保证代码质量。
两阶段工作流
Superpowers 的整个工作流分为两个阶段,文件落地在 docs/superpowers/:
bash
docs/superpowers/
├── specs/ ← 阶段一:技术设计文档
│ └── {date}-{feature}-design.md
└── plans/ ← 阶段二:实现计划(含完整代码模板)
└── {date}-{feature}.md
阶段一:生成技术设计文档
Superpowers 第一个产出的 Design Doc 相当于 OpenSpec 中 proposal + design 的融合体,但内容更聚焦于"对实现者有直接指导价值"的信息。
本次设计文档包含三个层次:
架构决策(数据访问分层):
bash
ETL 层:DailyStatsJob,每日凌晨 2 点,聚合写入 stats_daily 表
查询层:HTTP /api/biz/**
└── 大盘/漏斗/消息效果/数据质量 → 读统计表 stats_daily
└── 内容列表 → 直查原始记录表
数据层定义(含 DDL):
sql
CREATE TABLE stats_daily (
id BIGINT NOT NULL AUTO_INCREMENT,
stat_date DATE NOT NULL,
-- 即时型指标(当天汇总,Job 写入后即最终值)
active_user_count BIGINT NOT NULL DEFAULT 0,
conversion_task_count BIGINT NOT NULL DEFAULT 0,
register_user_count BIGINT NOT NULL DEFAULT 0,
...
-- 滞后型指标(送达、打开、回复等,每日 Job 回刷近 60 天)
delivered_count BIGINT NOT NULL DEFAULT 0,
reply_count BIGINT NOT NULL DEFAULT 0,
...
UNIQUE KEY uk_stat_date (stat_date)
) COMMENT = '每日统计汇总表(ETL 预聚合,供 API 层查询)';
Mapper 分工与数据源路由:
| Mapper | 数据源 | 职责 |
|---|---|---|
EtlBizMapper |
biz-db | ETL 原始聚合 SQL |
EtlUserMapper |
user-db | ETL 用户注册数统计 |
StatsQueryMapper |
biz-db | 读统计表,供 API 层使用 |
ContentListMapper |
biz-db | 内容列表动态分页查询 |
完整 API 契约(所有接口的路径、DTO 字段、业务规则、边界处理)。
这份设计文档经人工审核和局部调整后,作为阶段二计划生成的唯一输入。
阶段二:生成实现计划(含完整代码模板)
这是 Superpowers 与 OpenSpec 最核心的分叉点:生成的计划文件不只是任务清单,而是包含完整代码模板的施工蓝图。
计划文件头部声明了执行方式:
kotlin
> For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
> (recommended) or superpowers:executing-plans to implement this plan task-by-task.
> Steps use checkbox (- [ ]) syntax for tracking.
每个 Task 内嵌 TDD 红绿循环:
ini
- [ ] Step 1:创建 failing test(完整测试代码,此时实现类不存在)
- [ ] Step 2:运行测试 → 确认 RED(mvn test -Dtest=XxxTest)
- [ ] Step 3:创建实现代码(完整 Java 文件,含注解、方法签名、逻辑)
- [ ] Step 4+:创建配套文件(XML Mapper、配置项等)
- [ ] Step N-1:运行测试 → 确认 GREEN
- [ ] Step N:Commit(指定 commit message 格式)
计划文件直接给出完整的测试代码、实现代码和 shell 命令------AI 在执行阶段不需要"想"任何东西,只需按模板施工并验证测试是否通过。
阶段三:Subagent 驱动并行执行
通过 superpowers:subagent-driven-development Skill 触发执行。该 Skill 将计划中相互独立的 Task 分发给并行 subagent,各 subagent 在独立上下文中执行,完成后更新 checkbox。
本次 Task 依赖关系与并行策略:
arduino
Task 1(基础设施:拦截器、扫包配置)
└─→ Task 2(ETL Mapper 层)
└─→ Task 3(ETL Service)
└─→ Task 4(ETL Job)
└─→ Task 5(大盘 API) ┐
Task 6(消息效果 API)│ 相互独立,并行执行
Task 7(数据质量 API)│
Task 8(用户管理 API)│
Task 9(内容列表 API)┘
实际产出的 git 提交序列(每个 commit 对应计划中一个 Task):
sql
feat: add BizAuthInterceptor and extend MapperScan
feat: add ETL mapper layer (EtlBizMapper, EtlUserMapper)
feat: add DailyStatsService + DailyStatsJob
feat: add StatsQueryMapper with all stats queries
feat: add Dashboard API (GET /api/biz/dashboard/kpi and /funnel)
feat: add Msg Effect API (weekly dashboard + daily list)
feat: add Data Quality API (GET /api/biz/dataQuality/replyRate)
feat: add User Management API (list + mark test user)
feat: add Content List API (paginated list + detail)
每个 commit 均通过了对应的单元测试,全程没有出现"AI 生成代码与规范脱节"的情况。
踩坑:设计文档是执行质量的瓶颈
Superpowers 在执行阶段几乎不需要人工介入,但这以"设计文档足够准确"为前提。本次的主要返工点:
① 字段语义歧义 :设计文档对某个状态字段的描述存在歧义,导致 ETL SQL 生成后需要人工修正。教训:设计文档生成后,逐字段对照真实 DDL 是必要步骤,不能依赖 AI 从字段名推断语义。
② 跨库约束描述不够具体 :设计文档写了"Service 层内存合并",但计划生成时 AI 对路由注解的正确放置位置(Mapper 层 vs Service 层)存在误解。教训:跨库路由规则应在 CLAUDE.md 中以约束形式显式写出,不能只出现在设计文档的架构描述里。
根本原因 :Superpowers 的设计文档没有 OpenSpec design.md 那样强制要求"写出每个决策的 rationale",导致一些隐含假设在转化为计划文件时被错误展开。
5. 工作原理深度分析
OpenSpec 的三层架构
容易产生的误解是"OpenSpec = Markdown 文件 + Skill 提示词"。实际上,它是一个三层系统,CLI 是三层的枢纽,不是可选项:
yaml
┌─────────────────────────────────────────────────────────┐
│ 层 1: Skill / Command(.md 提示词文件) │ ← 告诉 Claude 每个阶段该做什么
├─────────────────────────────────────────────────────────┤
│ 层 2: openspec CLI(npm 包 @fission-ai/openspec) │ ← 状态机:依赖关系、进度追踪、模板注入
├─────────────────────────────────────────────────────────┤
│ 层 3: 文件系统(openspec/ 目录) │ ← 真相来源:工件、任务、规格、归档
└─────────────────────────────────────────────────────────┘
三层之间的数据流:
csharp
用户说 /opsx:propose
│
▼
[Skill] 告诉 Claude 调哪些 CLI 命令、以什么顺序执行
│
▼
[CLI] openspec new change → 写 .openspec.yaml 到文件系统
[CLI] openspec status --json → 读文件系统,返回工件依赖图
[CLI] openspec instructions → 读 config.yaml,返回工件模板 + 约束
│
▼
[Claude] 按模板写 proposal.md / design.md / tasks.md
│
▼
[文件系统] 工件落地,成为下一次 CLI 查询的依据
如果只有 Markdown 文件,Claude 无法知道"proposal 必须先于 design"这种依赖约束,也无法精确感知哪些任务已完成、哪些还未开始------这正是 CLI 存在的意义。
层 1(Skill):行为规范
各 Skill 各有明确职责:
explore:只允许调研,不允许写代码propose:驱动 CLI 生成完整工件集apply:按原子任务逐步实现,每完成一项必须更新 checkboxarchive:完整性检查后执行文件系统的mv操作
层 2(CLI):状态机
几个关键命令的实际返回:
bash
# 返回:工件依赖图 + applyRequires("这些工件都准备好了才能开始实现")
openspec status --change "<name>" --json
# 返回:模板 + 项目上下文(给 Claude 读,不写入文件)
# + per-artifact 规则 + 解锁条件(写完这个会解锁哪些后续工件)
openspec instructions proposal --json
# 返回:contextFiles + 进度(total/complete/remaining)
# + 尚未完成的任务列表(通过解析 checkbox 计算)
openspec instructions apply --json
openspec instructions apply 的进度计算是关键:CLI 解析 tasks.md 里的 - [x] / - [ ] 返回精确的"剩余任务列表",Claude 不会重复实现已完成的任务。
层 3(文件系统):真相来源
| 文件 | 谁写 | 谁读 | 用途 |
|---|---|---|---|
config.yaml |
用户手动配置 | CLI | schema + 项目上下文,per-artifact 约束的来源 |
.openspec.yaml |
CLI 自动创建 | CLI | 变更元数据,CLI 靠它识别"变更"目录 |
proposal.md |
Claude | Claude + CLI | Why & What,后续工件的基础 |
design.md |
Claude | Claude(apply 时读) | How,架构决策 |
specs/**/*.md |
Claude | Claude(apply 时读) | 能力规格,行为边界 |
tasks.md |
Claude | Claude + CLI | checkbox 是进度计数器,CLI 解析计算剩余任务 |
一句话总结三层分工:文件系统 = 记忆(存什么);CLI = 规则(怎么组织、依赖关系、进度追踪);Skill = 行为(Claude 在每个阶段的姿态和操作序列)。
OpenSpec 的四层工件分工
每层工件防止不同类型的错误:
arduino
proposal.md → 防止"做错方向" ── 先想清楚为什么做
design.md → 防止"做错架构" ── 先显式记录关键决策
spec.md → 防止"做错行为" ── 先定义边界场景再让 AI 生成
tasks.md → 防止"AI 发散" ── 原子任务压缩 AI 自由发挥空间
OpenSpec 与 Superpowers 解决不同层次的问题
OpenSpec 解决的是**"做什么、为什么做"的决策治理问题**:
- 强制把所有关键决策显式化、可追溯
- 每个设计选择都有记录在案的 rationale
- 适合多人协作或需要长期维护的复杂系统
Superpowers 解决的是**"怎么做得又快又对"的执行效率问题**:
- 把设计文档转化为可执行的施工蓝图
- 最大化 AI 执行阶段的确定性
- TDD 保证质量,subagent 并行提升速度
- 适合需求清晰、需要快速交付的场景
两者并非对立------实践建议见下一节。
TDD 是 Superpowers 的核心质量门控
这是 Superpowers 与手搓 SDD 和 OpenSpec 最本质的差异:质量验证不依赖人工对照规范,而是通过可运行的测试代码来保证。
OpenSpec 的 spec.md 用 WHEN/THEN 格式描述行为边界------这是人工可读的验收标准,需要人来判断实现是否符合:
markdown
#### Scenario: 跨数据源查询独立执行
- WHEN Service 采集 user-db 侧的注册数
- THEN 通过 user-db 数据源单独查询,结果以 Java 变量传递,不与 biz-db 查询混用连接
Superpowers 的 plan.md 把同样的要求写成可执行的测试:
java
@Test
public void testValidToken_allowsRequest() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Bearer test-secret");
boolean result = interceptor.preHandle(request, response, null);
assertTrue(result);
assertEquals(200, response.getStatus());
}
验收标准变成了 mvn test 的输出------GREEN = 通过,RED = 未通过,无歧义。
此外,测试先于代码存在,AI 在生成实现代码时已经"知道"期望是什么,而不是生成完之后再对照文档检查,从源头上减少了隐蔽 Bug。
三种执行模型对比
| 维度 | 手搓 SDD | OpenSpec | Superpowers |
|---|---|---|---|
| 执行单元 | 人工逐步交互 | 人工触发 /opsx:apply |
superpowers:subagent-driven-development |
| 并行能力 | 无 | 无 | Task 级并行(独立 subagent) |
| 上下文管理 | 单 session,随时间衰减 | CLI 精确注入"剩余任务列表" | 多 subagent 各自独立上下文 |
| 质量验证 | 人工 Code Review | 人工对照 WHEN/THEN spec | 自动化测试(mvn test) |
| 代码模板 | 无 | 无 | 完整代码模板内嵌在 plan |
6. 综合总结与选型建议
三种方式的全面对比
| 维度 | 手搓 SDD | OpenSpec | Superpowers |
|---|---|---|---|
| 上手成本 | 极低,零工具依赖 | 中等,需学 CLI + 工件结构 | 中等,需熟悉 Skill 体系 |
| 规范编写投入 | 低(CLAUDE.md + 若干 Feature Spec) | 中(4 层工件 × 变更数) | 高(1 精准设计文档 + 1 大型计划文件) |
| 决策可追溯性 | 差(散落在聊天记录) | 优(每变更有独立 design.md + rationale) | 中(有架构决策,无显式 rationale) |
| 执行速度 | 中(人工交互驱动) | 中(人工分步确认) | 快(subagent 并行 + 完整代码模板) |
| 质量验证 | 主观 Code Review | 对照 WHEN/THEN spec | 自动化测试(mvn test) |
| AI 幻觉风险 | 中(靠 CLAUDE.md 全局约束) | 低(CLI 状态机 + spec 场景双约束) | 低(TDD + 完整代码模板双约束) |
| 适用场景 | 快速验证 / 熟悉域开发 | 多人协作 / 长期维护系统 | 需求清晰 / 快速交付 |
选型建议
objectivec
项目/团队情况 推荐路径
──────────────────────────────────────────────────────
首次尝试 SDD,想快速感受价值 → 手搓 SDD(CLAUDE.md + Feature Spec)
需求不确定,多人协作,重长期维护 → OpenSpec
需求清晰,设计经验丰富,追求执行效率 → Superpowers
希望兼顾决策治理与执行效率 → OpenSpec 梳理设计决策 + Superpowers 执行
四条核心结论
1. 规范文件的本质是降低 AI 决策负担
三种方式的根本区别在于"规范把多少决策权从 AI 手里收回来":
- 手搓 SDD 把全局约束收回来(分层规范、禁止跨库 JOIN)
- OpenSpec 把变更决策收回来(每个 design.md 显式记录 How & Why)
- Superpowers 把实现细节也收回来(计划文件直接给出代码模板)
越往下走,AI 执行阶段的自由度越小、确定性越高,但规范编写阶段需要投入的精力越大。没有"最好的方式",只有与项目复杂度、团队规模、交付压力相匹配的方式。
2. Superpowers 顺畅的代价:设计阶段要求极高的精准性
Superpowers 在执行阶段几乎不需要人工介入,但前提是"设计文档足够准确"。实践中,设计文档里的一个字段语义歧义就导致了 ETL SQL 的返工;一处跨库约束描述不够具体,就导致路由注解被生成到了错误的层。
相比之下,OpenSpec 的 design.md 强制要求写明每个决策的 rationale,对"暴露设计文档中的隐含假设"有显著效果。
实践建议:对于不熟悉的业务域或涉及复杂跨系统交互的场景,建议先用 OpenSpec 完成设计决策梳理,再把 design.md 的内容作为 Superpowers 设计文档的输入------两者结合比单独使用任何一个都更稳健。
3. SDD 的核心价值不只是提速,而是提升代码质量的下限
回顾这三次实践,提效数字是直观的(5 天 → 2.5 天),但更重要的是:AI 在规范约束下没有写出任何违反架构规则的代码:
✓ 没有跨库 JOIN ✓ 没有在 Controller 里写业务逻辑 ✓ 没有 SQL 注入漏洞 ✓ 全局异常处理器一次生成即符合要求 ✓ 所有单元测试首次运行均通过
这些问题不是"AI 不会",而是"AI 不会主动想到"------它需要规范来约束,需要测试来验证。这正是 SDD 最值得推广的价值所在。
4. 开发者角色的根本性转变
三次实践的共同点:开发者的核心工作从"写代码"转移到了"定义规范"。无论是手搓 CLAUDE.md、写 OpenSpec 的 design.md,还是确认 Superpowers 的设计文档,最花时间、最需要人工判断的部分都是"把隐性的架构意图显式化"。
这个转变是 SDD 最值得推广的核心价值------不是某个具体工具,而是"规范先行、代码随之"的开发姿态。
以上实践基于 Spring Boot + MyBatis Plus 的 Java 后端项目,工具链为 Claude Code + OpenSpec + Superpowers。核心思路与语言和框架无关,适用于任何需要 AI 辅助编程的中等规模项目。