OpenSpec 实战指南:让 AI 写代码前,先把"图纸"画到能验收
上周有个做后端的朋友跟我吐槽:"我让 Claude Code 给订单模块加个优惠券功能,它写得飞快,可越写越不对劲——第一版漏了过期判断,我补了一句;第二版又把库存扣减搞错了,我再补一句。来回五轮,最后那坨代码我自己都看不懂了。"
我问他:"你一开始有没有跟它说清楚,'优惠券过期了该返回什么错误码'、'库存不足时先扣券还是先扣货'?"
他愣了一下:"没有……我以为它会自己想到。"
这就是问题所在。你以为你在跟一个程序员协作,其实你是在跟一个记性只有一次对话长的实习生协作。你说过的需求散落在聊天记录里,它读一句忘一句,每次都在"猜"你到底想要什么。猜对了是运气,猜错了你来擦屁股。
那有没有一种办法,让"要建什么"这件事,在写第一行代码之前就白纸黑字地定下来、还能被机器检查?有。它叫 OpenSpec——一个由 Fission-AI 开源、专门给 AI 编程助手用的"规格层"。今天我就带你从零跑通它,并告诉你它最容易被忽略、却最值钱的那个设计。
一、它到底解决了什么问题?
先说一句可能有点扎心的话:AI 写代码的瓶颈,从来不是"写得不够快",而是"不知道该写什么"。
你想想,Cursor、Claude Code 这些工具补全代码的速度,早就甩开人类几条街了。可为什么真上手干活,还是磕磕绊绊?因为需求这东西,活在你脑子里、活在产品经理的口头描述里、活在三天前那条已经被刷走的聊天记录里。AI 看不到全貌,只能边写边猜。
OpenSpec 的思路特别朴素:既然口头需求靠不住,那就把它写成文件,跟代码放在同一个仓库里。
这个文件不是写给人看完就扔的 PRD,而是一份结构化的、AI 每次干活前都会去读的"规格"。人和 AI 先就"建什么"达成一致,签字画押,然后才开工。代码写到一半 AI 忘了?没关系,它回头读一眼 spec 就想起来了。新人入职看不懂系统?没关系,把 spec 目录翻一遍就懂了。
这套打法有个正经名字,叫 SDD(Spec-Driven Development,规格驱动开发)。它不是 OpenSpec 发明的——GitHub 官方今年也推了类似的工具链。但 OpenSpec 把它做得足够轻:没有 API key,没有云服务,没有一堆要配的环境变量。装个 npm 包,跑一条 init,它就在你项目里铺好了一套目录和给 AI 用的斜杠命令。
而且它有个我特别欣赏的定位——brownfield first,先伺候老项目。市面上一堆工具只会做 0 到 1 的全新项目演示,可你我手里真正的活,90% 是在十万行屎山上加需求。OpenSpec 的 change(变更)机制天生就是为"在已有系统上做增量"设计的,这点后面会细讲。
一句话总结这一章:OpenSpec 不让 AI 写得更快,它让 AI 在动手前先搞清楚"要什么"——而这恰恰是返工的根源。
二、五分钟跑通:从 init 到 archive
讲再多概念,不如手把手跑一遍。OpenSpec 是个 npm 包,需要 Node.js 20.19 以上。先全局装上,再到你的项目里初始化:
# 全局安装 OpenSpec CLI
npm install -g @fission-ai/openspec@latest
# 进入你的项目目录,初始化
cd your-project
openspec init
关键在 openspec init 这一步。它会问你用的是哪个 AI 工具(Claude Code、Cursor、Copilot……官方支持 20 多个),然后干两件事:在仓库里建一个 openspec/ 目录,再把一组斜杠命令塞进你 AI 工具的配置里。从此你就能在对话框里直接喊 /opsx:propose 这种命令了。
装好之后,一个完整的需求走一遍,是这样四步:
第一步,提案。你在 Claude Code 里敲:
/opsx:propose 给订单加优惠券功能,支持满减和过期校验
AI 不会立刻写代码。它会先在 openspec/changes/ 下建一个变更文件夹,里面生成几份文档:为什么做、改什么、技术怎么选、要分几步干。这一步的产出全是"纸面"的,一行业务代码都没动。
第二步,评审。这是整套流程里唯一真正需要你动脑的环节。你打开 AI 生成的 spec,逐条看:满减的门槛字段叫什么?过期了返回 410 还是 422?这些你拍板,改进 spec 里。这个环节花十分钟,能省后面两小时的返工。
第三步,实现。规格定稿后,喊一句 /opsx:apply。AI 这才开始照着 tasks.md 的清单一项项写代码,写完一项勾掉一项。因为它手里攥着确定的规格,几乎不会再"自由发挥"。
第四步,归档。功能验收通过,跑 /opsx:archive(或命令行 openspec archive <change-id>)。这一步有个魔法:它会把这次变更里的规格改动,自动合并到项目主规格目录 openspec/specs/ 里,然后把整个变更文件夹挪进 archive/ 存档。你的"系统当前长什么样"这份事实,就这么悄悄更新了。
openspec list 看当前有哪些进行中的变更,openspec show <change> 看某个变更的细节,openspec validate <change> 检查格式合不合规,openspec view 开一个交互式看板。记不住没关系,AI 会替你调。
看明白了吗?从头到尾,你只在"评审"那一步真正参与,其余三步 AI 自己跑。你的角色,从"写代码的人"变成了"批图纸的人"。这就是它最舒服的地方。
三、读懂那四个文件:spec 和 change 的分工
跑通流程只是入门。想真正用好 OpenSpec,你得理解它目录里那套结构。别慌,核心就两个概念:specs(规格)和 changes(变更)。
打个比方。openspec/specs/ 是你这套房子的竣工图纸——它永远描述"系统现在实际长什么样",是唯一的事实来源。而 openspec/changes/ 是一张张装修变更单——每次你想改点什么,先开一张单子,单子上写清楚"哪面墙要拆、哪里要加个柜子",单子审批通过、施工完成后,才更新到竣工图纸上。
这么设计的妙处在于:变更单是隔离的。你可以同时开三张单子(三个并行的 change),互不干扰;每张单子在合并前都能被单独评审;合并的时候,它的改动会干净地叠加到主图纸上。这就是为什么 OpenSpec 特别适合老项目——它从不要求你一次性把整个系统的规格写全,只要求你把"这次要改的部分"说清楚。
一个 change 文件夹里,通常躺着四个文件,各司其职:
| 文件 | 回答的问题 | 谁主要看它 |
|---|---|---|
proposal.md | 为什么做、改什么(意图与范围) | 人 + AI |
design.md | 技术上怎么实现(方案与取舍) | 人 + AI |
tasks.md | 分几步干(可勾选的清单) | AI 执行 |
specs/ | 这次对规格做了哪些增删改(Delta) | 归档时合并 |
这里我要重点圈一个词:specs/ 子目录里装的不是完整规格,而是 Delta(增量)。它不写"系统全貌",只写"相对于现状,这次加了什么、改了什么、删了什么"。
这个 Delta 机制,才是 OpenSpec 真正的灵魂。它值得单开一章细讲。
四、Delta 才是 OpenSpec 的灵魂
为什么我说 Delta 是灵魂?因为它一举解决了规格驱动开发最头疼的两个老大难:规格会过期、规格写不全。
你想,如果每次改需求都要重写整份系统规格,那这份文档很快就会变成没人维护的"祖传文件",跟代码越漂越远。OpenSpec 反其道而行:每次变更只描述差异,归档时由工具自动把差异合并进主规格。规格的更新不再靠人手抄,而是流程的副产品。它想不准都难。
那 Delta 长什么样?它就是一份 Markdown,用三个标准段落把改动分类清楚:
## ADDED Requirements
### Requirement: 优惠券过期校验
系统必须在下单时校验优惠券有效期,过期则拒绝使用。
#### Scenario: 使用已过期的优惠券
- **WHEN** 用户在结算时选择一张已过期的优惠券
- **THEN** 系统返回 422 错误,提示"优惠券已过期"
- **AND** 订单金额不发生任何抵扣
## MODIFIED Requirements
### Requirement: 满减门槛判断
(原:满 100 减 10)现调整为可配置门槛,由营销后台下发。
## REMOVED Requirements
### Requirement: 固定立减券
该券种已下线,相关逻辑整体移除。
看到那个 #### Scenario 了吗?这是整份规格里最金贵的部分。它用 WHEN / THEN 的句式,把一条抽象需求翻译成了具体到能直接当测试用例的场景。
这一笔,把"规格"从一句正确的废话,变成了可验收的合同。
我举个对比你就懂了。如果需求只写"要校验优惠券过期",AI 完全可以理解成"过期就当没填优惠券、静默下单"——技术上它没违反需求,可这跟你想的完全是两回事。但如果规格里写明 WHEN 选了过期券 THEN 返回 422 且不抵扣,那就没有歧义了:AI 照着实现,你照着验收,对得上就是对,对不上就是错。
这也是为什么 OpenSpec 给了你一条 openspec validate 命令。它会检查你的 Delta 文件结构合不合规:ADDED/MODIFIED/REMOVED 的层级对不对,每条 Requirement 有没有挂 Scenario。它不保证你的需求"对",但它逼着你把需求"说完整"。这两件事不一样,但后者已经能挡掉一大半的低级返工。
一句话收这一章:OpenSpec 真正的护城河不是那套目录,而是 Delta + Scenario 这套"让需求可验收、让规格自动保鲜"的机制。
五、真实工作流:我每天怎么用它
概念讲完,说点实在的——它到底怎么嵌进日常开发?我自己摸出来的节奏是这样的,分享给你。
小需求,别上 OpenSpec。改个文案、调个样式、修个一眼能看明白的 bug,直接让 AI 干就行。给这种活套一整套提案流程,纯属杀鸡用牛刀,反而拖慢你。OpenSpec 的价值在"需求有歧义、改动有牵连"的活上才显现。
中等以上需求,先 propose,再人工把关。这是核心。我会先 /opsx:propose 让 AI 出一版规格,然后把 90% 的注意力花在读它生成的 Scenario 上。AI 列出的场景往往覆盖了主干,但容易漏边界——并发下单同一张券会不会超用?退款后券退不退回?这些 AI 想不全的洞,正是我作为人最该补的地方。补完场景,再放它去 apply。
并行的活,开多个 change。前面说过,change 是隔离的。所以我可以让一个变更在做支付重构、另一个变更在加优惠券,两条线各有各的提案和规格,互不打架。等各自验收完,分别 archive,主规格依次更新。这点对多人协作尤其香。
OpenSpec 还提供了一套"展开式"工作流给重活用——把 /opsx:propose 这一步拆得更细:/opsx:new 先搭个空架子,再用 /opsx:continue 或 /opsx:ff 逐步生成各份文档,/opsx:apply 之后还能 /opsx:verify 做一道核对。需求越大、越想分阶段控制,这套展开流越有用;小活用默认的精简流就够了。
AI coding assistants are powerful but unpredictable when requirements live only in chat history.
(AI 编程助手很强,但当需求只活在聊天记录里时,它就变得不可预测。)
—— OpenSpec 官方仓库
这句话我特别认同。OpenSpec 干的事,本质就是把需求从"易失的聊天记录"搬到"持久的仓库文件"里。一个存在内存里、关掉对话就没了;一个存在硬盘上、还能 git diff、还能 code review。可靠性完全不在一个量级。
openspec/ 规格原封不动地继续用。在工具半年一换的当下,这种"把约定沉淀在中立格式里"的选择,比绑死某一家要稳得多。
说到底,用好 OpenSpec 的诀窍就一句:把你的判断力,从"逐行审代码"前移到"逐条审场景"。前者累且容易漏,后者省力且抓得住根本。
六、踩坑清单与今天的挑战
聊了这么多,把最容易踩的几个坑给你拎出来,再留个今天就能做的小挑战。
② 给每个小改动都开 change——流程是有成本的,鸡毛蒜皮的活直接干。
③ 写了 Requirement 不写 Scenario——没有 WHEN/THEN 的需求等于没说清,返工照旧。
④ 忘了 archive——不归档,主规格就不更新,下次 AI 读到的还是旧事实,越用越乱。
把这套东西浓缩成几句你能记住的话:
- 定位:OpenSpec 不让 AI 写得快,让它写之前先搞清"要什么"。
- 四步:propose 提案 → 人工评审 → apply 实现 → archive 归档,你只重点管第二步。
- 两概念:specs 是竣工图纸(现状事实),changes 是装修变更单(隔离的增量)。
- 真灵魂:Delta + Scenario——让规格自动保鲜,让需求可验收。
我的看法:随着 AI 写代码越来越快,开发的瓶颈正在从"实现"整体迁移到"说清楚要什么"。在这个迁移里,谁能把需求表达得精确、可验收,谁就能让 AI 真正发挥十倍生产力。OpenSpec 不是唯一的答案,但它把这件事的门槛压得足够低——一个 npm 包就能开始。这种"轻",在我看来比功能多寡更重要。
今天回去试一件事:挑一个你手头中等大小、需求有点模糊的功能,别急着让 AI 写代码。先在脑子里(或纸上)逼自己写出三条 WHEN... THEN... 的验收场景,特别是边界场景——并发会怎样?出错返回什么?为空怎么办?
如果你能轻松写出来——恭喜,你已经具备了用好 OpenSpec 的核心能力,剩下的只是把它落进文件。如果你卡住了、写不全——那正好说明,你之前丢给 AI 的需求,本来就没说清楚。而这,就是你所有返工的真正源头。