前面的文章介绍了 OpenSpec 的基本使用和核心流程,这篇文章换个方式,直接用实战案例把 OpenSpec 的工作流跑一遍。
例子是一个 Java 电商项目 my-mall-app,分两个场景:从零加一个新功能,以及对已有功能做变更,来深入了解OpenSpec工作流和各阶段产物。
OpenSpec 对 AI 编程的意义
直接让 AI 写代码,最常见的问题是:你说的和 AI 理解的不一样,AI 改的比你预期的多(或少),改完之后也没有留下任何记录说明"为什么这样改"。
OpenSpec 通过三个阶段的产物来解决这些问题:
- Proposal(提案):说清楚"为什么要做这个变更,要解决什么问题"。
- Design(设计):说清楚"打算怎么做,涉及哪些模块,技术方案是什么"。
- Task(任务):把设计拆成具体的任务清单,每一步可以单独执行和验证。
这三层是逐级约束的:AI 写代码时按 Task 列表执行,Task 的范围受 Design 限制,Design 的方向受 Proposal 限制。写代码之前先在文档层面对齐认知,同时留下变更记录。
用一张图来表示这个约束关系:
flowchart LR
P["Proposal<br/>明确意图"] --> D["Design<br/>明确范围"]
D --> T["Task<br/>明确步骤"]
T --> C["Code<br/>受控编码"]
classDef primary fill:#1A9090,stroke:#147272,color:#fff
classDef secondary fill:#0d4f4f,stroke:#1A9090,color:#e0f0f0
class P,D,T primary
class C secondary
换个角度看,这三个阶段对应了团队里的三个角色:Proposal 相当于产品经理写需求,Design 相当于架构师出方案,Task 相当于开发拆排期。OpenSpec 把这三步写成文件,AI 每一步都照着文件来。
实战说明
下面拿 Java 电商项目 my-mall-app 跑一遍完整流程。
初始化项目
先进入项目目录,执行初始化:
cd my-mall-app
openspec init
初始化过程会做两件事:
- 让你选择当前使用的 AI 工具(Claude Code、Cursor 等),OpenSpec 会据此注入对应的 Slash Command。
- 创建
openspec/目录结构:
openspec/
├── specs/ # 真实来源(系统的行为规范)
│ └── <domain>/
│ └── spec.md
├── changes/ # 变更提案(每个变更一个文件夹)
│ └── <change-name>/
│ ├── proposal.md
│ ├── design.md
│ ├── tasks.md
│ └── specs/ # 增量规范(记录变更内容)
│ └── <domain>/
│ └── spec.md
└── config.yaml # 项目配置(可选)
新功能:订单查询功能
现在需要给项目加一个订单查询接口。按照 OpenSpec 的工作流,分四步完成。
第一步:创建变更提案
在 AI 对话中输入:
/opsx:new add-order-query
告诉 AI 你想要什么:
添加订单查询功能,支持按订单号、用户ID、时间范围查询,支持分页,返回订单基本信息和商品明细。
第二步:快速生成全部文档
/opsx:ff
ff 是 Fast Forward 的缩写,一步生成 Proposal、Design 和 Task 三份文件。执行后,openspec/changes/add-order-query/ 目录下会出现:
add-order-query/
├── proposal.md # 为什么做:描述变更动机和预期效果
├── design.md # 怎么做:技术方案、涉及的模块、接口定义
├── tasks.md # 做什么:逐条的任务清单
└── specs/ # 规格变更的增量描述
来看看各文件的内容(简化示意):
proposal.md — 变更意图
## 变更目标
为 my-mall-app 添加订单查询功能,让用户和管理员能够
按多种条件检索订单信息。
## 解决的问题
当前系统没有订单查询接口,运营和客服只能直接查数据库。
## 影响范围
- 新增 OrderQueryController
- 新增 OrderQueryService
- 新增 OrderQueryRepository 查询方法
- 不涉及订单创建和支付流程的修改
design.md — 技术方案
## 接口设计
GET /api/orders/query
请求参数:
- orderId (可选)
- userId (可选)
- startTime / endTime (可选)
- page / size (分页)
响应:Result<PageResult<OrderDTO>>
## 分层设计
- Controller:参数校验 + 调用 Service
- Service:组装查询条件,调用 Repository
- Repository:JPA Specification 动态查询
tasks.md — 任务清单
- [ ] 1. 创建 OrderQueryDTO 和 OrderItemDTO
- [ ] 2. 在 OrderRepository 中添加 Specification 查询方法
- [ ] 3. 实现 OrderQueryService
- [ ] 4. 实现 OrderQueryController
- [ ] 5. 编写单元测试
- [ ] 6. 编写接口集成测试
这个阶段还没有写一行业务代码。你可以逐个检查这些文件,看看 AI 的理解对不对。发现问题直接改文件就行,改的是文档不是代码,成本很低。
第三步:执行变更
确认文档没有问题后,执行:
/opsx:apply
AI 会按 tasks.md 的清单逐个执行。每完成一个任务,在对应条目上打勾。AI 的行为被 design.md 和 specs/ 限定,它不会跑去改支付模块,也不会自己换数据库框架。因为完成了前面设计和任务的确认,实现环节不需要投入太多的注意力,可以并行去实现其他任务。
第四步:归档
功能开发完成、测试通过后:
/opsx:archive
这一步做两件事:把 changes/add-order-query/specs/ 中的规格增量合并到主 specs/ 目录,然后清理变更工作区。归档之后,specs/ 目录就反映了系统的最新状态,包括刚加上的订单查询功能。
整个新功能的开发流程可以用下图表示:
flowchart TD
A["/opsx:new<br/>创建变更提案"] --> B["/opsx:ff<br/>生成 Proposal + Design + Task"]
B --> C{"检查文档<br/>是否准确?"}
C -- 有问题 --> D["手动修改文档"]
D --> C
C -- 确认无误 --> E["/opsx:apply<br/>AI 按任务清单编码"]
E --> F{"测试是否<br/>通过?"}
F -- 不通过 --> G["修复问题"]
G --> F
F -- 通过 --> H["/opsx:archive<br/>归档,合并到 specs/"]
classDef action fill:#1A9090,stroke:#147272,color:#fff
classDef decision fill:#0d4f4f,stroke:#1A9090,color:#e0f0f0
classDef fix fill:#2a6b6b,stroke:#1A9090,color:#e0f0f0
class A,B,E,H action
class C,F decision
class D,G fix
功能变更:给已有功能加缓存
新功能的流程比较直接,但实际工作中更常见的情况是对已有功能做变更。比如订单查询接口上线后发现查询慢,需要加 Redis 缓存。
对已有功能做变更,比从零开发多了一个前置步骤:先搞清楚当前实现是什么样的。
第一步:探索现有实现
/opsx:explore
这个命令让 AI 读当前代码,把指定功能的现有实现梳理出来。AI 会遍历订单查询相关的 Controller、Service、Repository,输出一份分析报告,包括:
- 现有的接口定义和调用链路
- 数据模型和表结构
- 发现的潜在问题(比如 N+1 查询、缺少索引)
- 建议的改动点
比如 AI 的分析报告可能指出:
OrderQueryService.queryOrders() 方法中存在 N+1 查询问题,每个订单会单独查询一次商品明细。建议使用 JOIN FETCH 或者引入缓存层。
第二步:交互确认变更方向
看完分析报告,你跟 AI 确认要做的事情:
给订单查询加 Redis 缓存。缓存策略:按查询条件 hash 作为 key,过期时间 5 分钟。订单状态变更时清除相关缓存。
第三步:推进变更
确认方向后,有两种方式往下走:
-
/opsx:continue:从当前状态一步步向前推进,适合变更比较复杂、需要分阶段讨论的情况。AI 会根据 explore 阶段的分析和你的确认,依次生成 Proposal、Design、Task,每一步都可以停下来调整。 -
/opsx:new:直接创建一个新的变更提案,然后走完整的ff → apply → archive流程。适合变更内容已经想清楚、不需要再反复讨论的情况。
选哪种看实际情况。如果你对要做的事很确定,直接 /opsx:new add-order-cache 然后一路往前走。如果还需要和 AI 反复讨论方案细节,就用 /opsx:continue。
什么时候用 OpenSpec,什么时候不用
OpenSpec 管的是变更过程。但不是所有场景都需要走这套流程。
适合用 OpenSpec 的场景:
- 涉及多个文件、多个模块的功能开发或变更
- 需要修改已有业务逻辑,特别是不熟悉的模块
- 团队协作场景,需要留下变更记录供他人 review
- 复杂重构,需要先理清现状再动手
不需要 OpenSpec 的场景:
- 修一个简单 bug,比如空指针、拼写错误
- 纯样式调整或配置修改
- 写一个独立的工具脚本
- 快速验证一个想法的原型代码
判断标准很简单:如果你能用一句话说清楚要改什么、怎么改,直接让 AI 干就行。如果你需要先想一想"这个改动会影响哪些地方",那就该用 OpenSpec。
经验总结
用了一段时间 OpenSpec 之后,几点体会。
- 先写文档再写代码,做起来比想的快。很多人一听"写 Proposal、写 Design"就觉得麻烦,但
/opsx:ff一条命令就自动生成了,花几分钟看一遍确认没问题就行。这几分钟省下的是后面反复改代码的时间。 - Explore 对老项目特别好用。接手别人的代码时,先用
/opsx:explore让 AI 把相关模块梳理一遍,比自己翻代码快得多。AI 整理出来的报告,本身就是一份不错的模块文档。 - 归档容易被忽略,但值得坚持做。功能做完后执行 archive,变更说明会进入
specs/目录。过几个月回头看某个功能为什么这样设计,打开 spec 文件就有答案,不用翻 git log 猜提交意图。 - 小改动不用走 OpenSpec。修一个空指针、改一行配置,直接让 AI 干就行。判断哪些变更需要走流程、哪些不需要,这本身也是工程经验的一部分。