用 OpenSpec 把复杂后端改动变得更可控
这不是一次"AI 编程工具介绍",而是基于 创作者提现系统 等真实需求的开发方式分享:当需求、设计、实现和评审开始围绕 Spec 组织时,后端开发会发生什么变化。
项目背景
D5 Works · 后端服务
JDK 21 + Spring Boot 3.5 + MyBatis + MySQL 8.0
微服务架构,依赖 Nacos、RocketMQ、Redis
核心案例
creator-withdrawal-system
创作者收益入账 → 余额管理 → 提现申请 → 审核打款,6 天完成 6 张新表 + 15 个 API 的全流程闭环。
已归档 Changes
5 个版本迭代,覆盖大中小需求
从修排序 Bug(3 个 task)到 MAX 模型异步对接(跨服务 BREAKING 改造),都走了同一套流程。
为什么我们需要 Spec 驱动开发
在后端项目里,很多问题并不来自"代码不会写",而来自"问题没有被定义清楚"。AI 可以帮我们更快地产出代码,但如果上下文和约束不完整,它也会更快地把错误放大。
什么是 OpenSpec
一种更结构化的开发协作方式:不是直接让 AI 改代码,而是先把"要做什么、为什么这么做、边界是什么、如何验收"明确下来,再推动设计和实现。
和"直接让 AI 写代码"有什么区别
OpenSpec 核心流程
从提出变更到完成交付,经过四个阶段。每个阶段有明确的输出物和评审节点,让讨论前置、分歧显式化。
核心文档结构
四类文档分开存放,分别服务于不同阶段。让评审更聚焦,也让 AI 在每个阶段只关注正确的问题。
D5 Works 的文件组织
archive/,保留完整审计记录。全局 specs 被 change 中的 spec 引用,实现增量定义。
creator-withdrawal-system:从 Proposal 到实现
创作者在 D5 Works 销售商品后没有提现通道。我们需要在 6 个工作日 内搭建完整的提现系统:收益入账 → 等待期结算 → 提现申请 → 财务审核 → 打款标记。以下是 OpenSpec 四层文档如何串起这个需求的。
每个 capability 对应独立的 spec 文件,用 WHEN-THEN-AND 格式逐场景定义行为要求。以下是两个代表性片段:
Design 最核心的部分是 Decisions:每个技术决策都记录了"选择 + 理由 + 替代方案",而不仅仅是"我们用了 X"。
理由:查询效率高、支持负余额、便于对账
替代:实时聚合 — 查询慢且负余额复杂
理由:InnoDB 行锁序列化并发写入,无需 Redis 分布式锁
弃用:Redis 锁 + REQUIRES_NEW 编程式事务 — 三层嵌套复杂
理由:Stripe 手续费因卡类型不同,固定公式不准确
细节:当前 SGD 结算需汇率换算,后续 USD 主体可简化
理由:PayPal 不提供公开的邮箱验证 API
后续:Phase 2 接入 PayPal Login OAuth
理由:避免精度/舍入推算不一致
校验:amount - paypalFee == actualAmount
理由:余额已支持负数,能力预留
砍掉:退款涉及权益撤销等复杂逻辑
整个提现系统被拆成 8 个任务组、40+ 个子任务,每个任务带编号和 checkbox 状态。
17 个子任务 ✓
10 个子任务 ✓
15 个子任务 ✓
多个子任务 ✓
Spec 的语言规范
我们约定了统一的 Spec 写作规范,让所有文档保持一致的结构。
## ADDED Requirements
### Requirement:
#### Scenario:
- [x] 1.1 描述 已完成 ·
- [ ] 1.2 描述 待做
Spec 如何约束 AI
以提现设置为例,Spec 中定义了明确的行为约束,AI 在实现时必须遵守。
不同规模的需求都在用同一套流程
D5 Works 已有 5 个归档的 change。从修一个排序 Bug 到跨服务 BREAKING 改造,OpenSpec 的四层文档结构都适用,只是"写多少"不同。
OpenSpec CLI 常用命令
OpenSpec 提供了一个 Node.js CLI 工具,用于管理 change 的生命周期。以下所有输出均来自对 D5 Works 当前项目的实际执行结果。
openspec list
— 查看所有活跃的 change
openspec list --specs
— 查看所有全局 Spec(按业务领域)
requirements 数量反映了该领域已沉淀的行为要求条数。随着 change 不断归档,主 spec 持续积累。
openspec status --change <name>
— 查看 artifact 完成状态
applyRequires: ["tasks"] 告诉 Skill:只有 tasks artifact 完成后才能进入实现阶段。Skill 会读取这个字段来判断下一步做什么。
openspec instructions apply --change <name> --json
— 获取实现阶段的指令和任务清单
contextFiles 加载上下文,遍历 tasks 数组逐个执行,通过 state 判断是否已全部完成。
openspec validate <name>
— 校验结构完整性
--all(校验全部 change + spec)、--strict(严格模式)。
openspec schemas
— 查看可用的工作流 schema
spec-driven schema,即标准的四层文档流程。Schema 定义了 artifact 的依赖关系和创建顺序。
openspec archive <name>
— 归档已完成的 change
--json 结果传递给 AI 解析。人类更多通过 openspec list 和 openspec status 了解全局状态。
Cursor 中的 OpenSpec Skills
D5 Works 项目在 .cursor/skills/ 下配置了 4 个 OpenSpec Skill。每个 Skill 是一个 SKILL.md 文件,定义了 AI 在特定阶段应该执行的步骤、调用的 CLI 命令和输出格式。Cursor Agent 会根据用户意图自动匹配并执行对应的 Skill。
archive/。
archive/YYYY-MM-DD-<name>/,保留完整审计记录
explore propose apply archive ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 探索问题 │ ─→ │ 生成提案 │ ─→ │ 逐项实现 │ ─→ │ 归档变更 │ │ 理清思路 │ │ 四层文档 │ │ 标记完成 │ │ 同步 Spec│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ 可随时回到探索 │ 可随时更新文档 │ 遇阻塞自动暂停 └──────────────────────┴─────────────────────┘ 非线性流程:可以从任意阶段开始,随时在 Skill 之间切换
我在 Cursor 中的实际工作流
Cursor 不只是代码编辑器,更是围绕 OpenSpec 协作的工作台。以 creator-withdrawal-system 为例,我的实际使用方式是:
后端 Spec Checklist
最适合被团队复用的一页。"开始让 AI 动手前,至少先写清楚这些"的最小模板。
- 要解决什么问题(Why)
- 目标与非目标(Goals / Non-Goals)
- 成功标准 / 验收条件
- 请求与响应结构(Req / Resp)
- 错误码与兼容策略
- 对上下游的影响(Feign / MQ)
- 表结构 / 字段变更(DDL)
- 索引与性能影响
- 迁移与回滚方案(Flyway)
- 主流程与分支流程
- 边界条件(NULL / 空 / 极限值)
- 幂等与一致性策略
- 并发安全(锁 / 事务隔离)
- 性能要求
- 日志 / 监控 / 告警
- 单元测试
- 集成测试
- 回归范围与验收口径
我感受到的收益
目前的局限和边界
团队如何开始落地
不建议一上来全量推行。更现实的方式是:先试点、再统一模板、然后评估收益。