从需求拆解到 OpenSpec:为什么不要直接敲 /opsx:explore
一句话:OpenSpec 不是需求分析的起点,而是需求设计的终点。接到需求先自己做一遍「设计拆解」,再进 explore,才能少返工、少幻觉。
一、引子:两种开工方式
假设你接到一个需求:"做一个任务看板,展示团队的任务列表,能创建、能分配、能修改状态、能标记完成。"
方式 A:直接 explore
你打开 Claude Code,敲下:
text
/opsx:explore 我想做一个任务看板
AI 很热情,开始问你:
- "任务状态有几种?"
- "列表是看板还是表格?"
- "分配人是单选还是多选?"
- "已完成任务能不能删除?"
你一边聊一边发现,自己也没想清楚。于是每轮都冒出新的问题:
- "等等,'进行中'和'待评审'是不是同一个状态?"
- "任务优先级是 P0-P3 还是高中低?"
- "这个枚举值为什么没有任何列用到?"
聊了 20 轮,AI 终于给你画了一张看起来还不错的页面结构图。你很满意,觉得需求"聊清楚了"。
然后 /opsx:propose,生成四件套,进入 /opsx:apply。做到一半你发现:
- 顶部统计数字的聚合口径和后端字段对不上
- 分配任务接口原来需要
assigneeId,但 spec 里漏了 - 状态流转没定义(能不能从"已完成"回到"进行中"?)
- 已完成任务的"归档"和"删除"按钮,需求文档里根本没写清楚
你开始改 spec、改 design、改 tasks,甚至回到 explore 重新问。原本想省的时间,全花在返工上。
方式 B:先设计,再 explore
你打开 xmind,先画了一张图:
text
任务看板
├── 任务状态:PENDING / IN_PROGRESS / IN_REVIEW / DONE / ARCHIVED
├── 状态流转:PENDING → IN_PROGRESS → IN_REVIEW → DONE(单向)
├── 接口契约:GET /tasks / GET /tasks/stats / PATCH /tasks/:id/status
├── 页面结构:Header + 统计 + 筛选 + 列表 + 分页
└── 关键规则:已完成 30 天归档、仅创建者/管理员可删除
然后带着这张图进 explore,只问三句话:
- "我定的状态机有没有漏业务场景?"
- "IN_REVIEW 算进行中还是独立统计?"
- "成员被删除后,历史任务的负责人怎么展示?"
explore 一轮就补完了细节。propose 生成四件套后,你对着主体模板核对一遍 spec,直接 apply。
两种方式,同样的工具,完全不同的效率。
二、为什么要先设计,再进入 OpenSpec?
2.1 直接 explore 的问题
OpenSpec 的 explore 是个极好的工具。当你对业务已有判断时,它是澄清工具:帮你把"已经有的想法"里的缝补上。当你完全没思路时,它也可以帮你从零做初步梳理。
但这篇文章讨论的是有业务判断时的最佳实践:先自己做设计,再让 explore 补细节。因为如果一开始就把设计权交给 AI,问题域会变成无限的:
text
没有主体的 explore: 有主体的 explore:
"你到底要什么?" "状态机单向还是允许回退?"
"这个状态怎么处理?" "IN_REVIEW 算进行中吗?"
"这里要不要轮询?" "成员删除后历史负责人怎么展示?"
↓ ↓
问题域无限 问题域收窄成"细节清单"
每轮都产新问题 → 次数多 一轮问完基本就清 → 次数少
AI 不会主动拒绝回答无限问题,它会一直陪你聊。但你聊得越多,越容易被它的"合理化"带偏------它会把你的模糊需求补成一套逻辑自洽、但未必符合业务的方案。
更危险的是,explore 产出的是对话上下文 ,不是可执行的契约。等你进 propose 时,AI 要凭记忆把 20 轮对话转成 proposal/spec/design/tasks,漏掉或曲解一两个关键决策是常态。
2.2 先设计的收益
先设计,本质上是在做一件事:把问题域收窄。
你先把"AI 猜不对、拍错了代价大"的决策钉死,explore 面对的不再是"你要什么",而是"这几个决策对不对"。
| 维度 | 直接 explore | 先设计再 explore |
|---|---|---|
| 问题域 | 无限,每轮冒新问题 | 有限,只补细节 |
| AI 角色 | 设计师 + 澄清者 | 澄清者 + 挑刺者 |
| 产出 | 对话上下文 | 结构化设计 + 契约 |
| 返工概率 | 高 | 低 |
| 适用场景 | 你完全没思路时 | 你有业务判断时 |
关键洞察:explore 适合"想不清",不适合"懒得想"。 如果你自己对业务有判断,先把判断写下来,再让 AI 帮你检查。
2.3 设计的形式不重要,结构化才重要
先设计不等于必须用 xmind。你可以用:
- xmind:适合发散和树形拆解
- Confluence / Notion:适合协作和留痕
- Word / Markdown:适合写文档和 checklist
- 草稿纸:一个人快速拍板时完全够用
工具只是载体,目的是一致的:在进 explore 之前,把"AI 猜不对、拍错了代价大"的决策结构化地钉死。
所以不用纠结"我是不是应该用 xmind"。只要能回答清楚这四个问题,什么工具都可以:
- 接口契约是什么?
- 状态/枚举怎么映射?
- 页面/系统结构长什么样?
- 关键业务规则有哪些?
2.4 正确顺序
text
接到需求
↓
人做分析设计(xmind / Confluence / Word / Notion / Markdown / 草稿纸)
↓
带着主体进 explore 补细节
↓
OpenSpec propose 生成四件套
↓
apply 照单施工
最难的需求→设计,你自己做;最碎的细节补缺,交给 explore;最终的权威,落在 OpenSpec。
三、需求设计:五步拆解法
复杂项目用 AI,必须分三层:
| 层级 | 谁负责 | 回答什么问题 |
|---|---|---|
| 领域拆分 | 人 | 大需求切成哪几个有界模块? |
| 宏观编排 | 人 | 模块之间谁先谁后?何时能合? |
| 微观执行 | AI + 人 Review | 每个模块内部怎么实现? |
落到执行上,就是五步拆解:
- 顶层业务域解耦:按业务闭环切成独立模块
- 三大前置锚定:锁死硬约束、影响面、边界规则
- 模块内线性流程:接口契约 → 领域模型/数据层 → 业务逻辑 → 接入层/视图层 → 验收
- 原子任务颗粒度:每个任务单目标 + 明确输入 + 可验收
- 测试验证闭环:做完即验,不等最后
如果
CLAUDE.md/.claude/rules/或 OpenSpecconfig.yaml已经配置了全局技术规范,三大锚定里不必重复约定技术硬约束,只关注业务硬约束和系统级规则。
四、进 explore 前:先写"主体模板"
不要带着一张 xmind 或一段话进 explore。先把设计写成固定格式的四块:
4.1 接口契约
| 契约 | 内容 |
|---|---|
| 任务列表 | GET /tasks?status=&priority=&page= |
| 任务统计 | GET /tasks/stats |
| 分配任务 | PATCH /tasks/:id/assign |
| 更新状态 | PATCH /tasks/:id/status |
4.2 状态映射
text
枚举:PENDING / IN_PROGRESS / IN_REVIEW / DONE / ARCHIVED
Tab 映射:
待处理 = PENDING
进行中 = IN_PROGRESS + IN_REVIEW
已完成 = DONE
状态流转:PENDING → IN_PROGRESS → IN_REVIEW → DONE(单向)
4.3 页面结构
text
TaskList
├── Header(标题 + 新建按钮)
├── 统计区块
├── 筛选栏
├── 任务列表 / 看板
└── 分页器
4.4 关键规则
- 聚合算法:待处理 = PENDING;进行中 = IN_PROGRESS + IN_REVIEW;已完成 = DONE
- 已完成任务 30 天后自动归档
- 仅创建者 / 管理员可删除任务
这四块就是"AI 猜不对、拍错了代价大"的决策。你自己先拍板,explore 只负责补缝。
五、explore 只补二件事
带着主体模板进 explore,目标明确:
5.1 验收标准
把每个功能翻译成 GIVEN/WHEN/THEN:
- 顶部统计:GIVEN 用户打开页面 → THEN 展示待处理/进行中/已完成数字 → AND 按聚合算法计算
- 状态流转:GIVEN 任务已完成 → WHEN 用户尝试回退 → THEN 接口返回 400
5.2 边界异常
- 列表为空 → 空态展示什么
- 接口失败 → 是否重试
- 成员被删除后,历史任务负责人如何展示
主体定了,explore 从"探索"变成"对清单",往返自然少。
六、进 OpenSpec 前先拆 change
6.1 一个 change 装一个模块
一个 change 是"一次变更",不是"一个项目"。
如果你在领域拆分阶段切出了 5 个独立模块,就应该建 5 个 change,而不是把 5 个模块的 spec 和 tasks 塞进同一个 change。
text
领域拆分结果 change 拆分结果
├─ 任务列表页 ───▶ task-list/
├─ 任务详情页 ───▶ task-detail/
├─ 创建任务页 ───▶ task-create/
├─ 成员选择组件 ───▶ member-select/
└─ 共享 service/枚举 ───▶ task-base/ # 底座 change
6.2 为什么要一个模块一个 change?
核心原因:控制上下文规模。
一个 change 越大,单次执行需要加载的 spec + design + tasks 就越长。一旦超过上下文窗口,越靠后的步骤越容易丢失前置信息,AI 开始出现:
- 凭空臆造字段
- 遗忘既有约束
- 改错无关模块
- 前面定的规则后面推翻
拆成多个 change 后,每个 change 只加载自己有限的 spec 和 tasks,上下文可控,逻辑稳定(具体篇幅阈值因项目和模型而异,建议以"一次能完整复核"为准)。
6.3 什么时候必须拆?
满足以下任一条件,就应该拆:
- 需求涉及 3 个以上独立模块
- 一个模块的 spec 长到一次读不完、核不动
- 多个模块会修改共享文件(service / enum / route)
- 团队多人并行开发
- 执行到后面开始"失忆"或前后矛盾
6.4 拆分原则
原则 1:页面/模块目录各自独享
每个 change 只负责一个独立模块的代码,互不覆盖:
text
openspec/changes/
├── task-list/ # 只改 src/pages/task-list/
├── task-detail/ # 只改 src/pages/task-detail/
└── task-create/ # 只改 src/pages/task-create/
原则 2:共享文件归底座 change
被多个模块引用的共享文件,只归一个底座 change 负责:
text
openspec/changes/
├── task-base/ # 负责 src/services/taskService.ts、taskEnum.ts
├── task-list/ # 引用 taskService,但不修改它
├── task-detail/ # 引用 taskService,但不修改它
└── task-create/ # 引用 taskService,但不修改它
原则 3:命名保持业务线聚合感
用统一前缀让多个 change 看起来还是同一个业务:
text
task-list / task-detail / task-create / task-base
6.5 不拆会怎样?
| 问题 | 表现 |
|---|---|
| 上下文膨胀 | apply 到后面忘记前面的需求 |
| 合并冲突 | 多个模块同时改共享 service |
| 节奏混乱 | 一个模块卡住了,整个 change 都动不了 |
| 追溯困难 | 一个 change 里混了太多事,归档后看不懂 |
6.6 实战中的拆分步骤
拿到需求后,按这个顺序拆:
- 领域拆分:先画出有哪些独立模块
- 标出共享文件:哪些 service / enum / route 会被多个模块用
- 建底座 change :共享文件归到一个
xxx-basechange - 建业务 change :每个模块一个
xxx-模块名change - 声明依赖:业务 change 里写明"依赖 task-base 的接口契约"
这样每个 change 都小而清晰,可以独立 propose、apply、sync、archive。
七、OpenSpec:把设计翻译成契约
explore 结束后,你手里有:
- 主体模板(接口 / 状态 / 结构 / 规则)
- 细节答案(验收标准 / 疑点结论 / 边界场景)
- 拆分方案(哪些模块进哪个 change)
这时再 /opsx:propose,把已确定的决策机械翻译成四件套:
| 工件 | 回答的问题 | 内容来源 |
|---|---|---|
proposal.md |
为什么做 | 需求背景 + 模块 Scope |
specs/<能力>/spec.md |
做什么 | 主体模板 + 验收标准 |
design.md |
怎么做 | 页面结构 + 关键规则 |
tasks.md |
怎么执行 | 线性流程 + 原子任务 |
xmind 是草稿,OpenSpec 是合同。合同定了,草稿可以丢。
propose 生成后,必须人肉把关 spec:对照主体模板逐条核查接口、状态、规则是否都进了 spec。
7.1 OpenSpec 是 SSOT(唯一真源)
从 propose 完成的那一刻起,OpenSpec 里的 spec.md、design.md、tasks.md 就成为当前变更的 SSOT(Single Source of Truth)。
这意味着:
- 后续需求变更:不再去改 xmind、Confluence 或草稿纸,而是直接在 OpenSpec 的 spec/design/tasks 中更新,让变更留痕、可追溯。
- 团队协作:前后端、测试、产品对齐时,以 OpenSpec 中的契约为准,而不是各自拿着不同的文档版本争论。
- 验收追溯:验收时发现实现与需求不一致,先回查 OpenSpec,再看代码是否偏离了契约。
- 设计工具退位:xmind、Confluence、Word、Notion、Markdown、草稿纸 只是「设计阶段的工具」,它们帮助你思考、收敛、拍板;一旦进入 OpenSpec,这些工具就完成了使命,不再作为权威依据。
简单说:设计阶段的文档可以有很多份,执行的真相只存一份------在 OpenSpec 里。
把 SSOT 意识立住,才能避免「聊着聊着又回到 xmind 改需求」「代码写一半发现 spec 没更新」「测试按旧文档验收」这类反复。
八、写在最后
OpenSpec 是一个极好的"需求→代码"的翻译器,但它不是魔术师。它能把清晰的设计翻译成清晰的契约,但不能替你思考业务。
最省时间的方式,不是跳过设计,而是把设计做扎实。
接到需求,先用你顺手的工具做设计------xmind、Confluence、Word、Notion、Markdown、草稿纸都可以。把接口、状态、结构、规则钉死,带着这份设计进 explore,让 AI 帮你挑刺。拆好 change 后再 propose,让它生成四件套、照单施工。
你会发现,AI 不再是你返工的原因,而是你执行设计的最强助手。