规格驱动开发这个词最近在团队里被反复提起,起因很简单:我们用 AI 编码助手写代码,写得越快,返工越多。需求在脑子里,提示词随手一写,AI 生成一大坨代码,跑起来看着像那么回事,一对接真实业务就崩。问题不在于模型不够强,而在于我们从来没把"规格"当成一等公民。OpenSpec 就是冲着这个痛点来的——它把规格文档变成开发流程的中枢,让 AI 助手围绕一份可执行、可校验的规格去写代码,而不是围绕一段模糊的自然语言提示。这篇内容适合已经在用 Claude Code、Codex CLI 这类命令行 AI 编码工具,但被"生成快、返工也快"折磨过的开发者,也适合想给团队建立一套可复用 AI 开发规范的技术负责人。我会从它解决的问题讲起,一路拆到目录结构、工作流、和 CLI 工具的配合方式,以及我自己踩过的那些坑。
1. 为什么"先写规格"这件事在 AI 编码时代突然变得关键
1.1 提示词工程的瓶颈到底卡在哪
过去两年大家谈 AI 编码,焦点几乎都在提示词上:怎么写得更清楚、怎么加 few-shot 示例、怎么让模型理解上下文。但真正上手做项目的人会发现,提示词工程有个绕不过去的天花板——它是一次性的、易失的、不可累积的。你今天写了一段精妙的提示词让 AI 生成了一个用户认证模块,明天换个会话、换个人,这段提示词就消失了,没人知道当时约束了什么、边界在哪、为什么这么设计。
更麻烦的是,提示词天然是"对话式"的。你跟 AI 说"帮我写个登录功能",它会自己脑补一堆假设:用 JWT 还是 Session?密码怎么存?错误码怎么定义?这些假设它不会告诉你,直接写进代码里。等你发现不对,改起来就是连锁反应。我见过最夸张的一次,一个团队用 AI 生成了整套订单系统,结果发现 AI 默认用了软删除,而他们的数据库规范要求物理删除加归档表,最后重构花了三天。
规格驱动开发的核心洞察是:与其反复优化提示词,不如把约束沉淀成一份结构化的规格文档。这份文档既是给人看的,也是给 AI 看的。它不随会话消失,可以进版本控制,可以 review,可以增量修改。OpenSpec 做的就是把这件事工程化。
1.2 OpenSpec 把"规格"放在了什么位置
理解 OpenSpec 的关键,是搞清楚它在整个开发链路里的位置。传统的 AI 编码流程是"人 → 提示词 → AI → 代码",规格是隐式的、散落在提示词里的。OpenSpec 把它改成"人 → 规格文档 → AI → 代码 → 校验 → 回写规格"。
这里有个很重要的设计取向:OpenSpec 不是又一个代码生成器,它更像是一个规格的容器和调度层。它定义了一套目录约定和文件格式,让规格、变更提案、任务清单、实现代码之间有明确的对应关系。你可以把它理解成"给 AI 编码加了一层项目管理",只不过管理对象不是人,而是 AI 的工作上下文。
它和 Claude Code、Codex CLI 这类工具的关系是互补的。CLI 工具负责"执行"——读文件、改代码、跑命令;OpenSpec 负责"定义做什么、为什么做、做到什么程度算完成"。没有 OpenSpec,CLI 工具就是个执行力很强但没方向的实习生;有了 OpenSpec,它才像个知道验收标准的工程师。
1.3 规格驱动和测试驱动、文档驱动的区别
很多人第一反应是:这不就是文档驱动开发吗?或者测试驱动开发的变体?其实差别挺大。
测试驱动开发(TDD)的核心是"先写测试,测试即规格"。但测试有个问题——它描述的是"结果对不对",不描述"为什么这么做"。AI 拿到一堆测试,能写出通过的代码,但可能写出一个完全违背架构意图的实现。而且测试对"还没实现的功能"表达能力很弱,你很难用测试描述一个交互流程的设计意图。
文档驱动开发的问题是文档和代码容易脱节,写完就没人看了,变成摆设。OpenSpec 的规格不是纯文档,它是带结构的、可被工具解析的、和代码目录有映射关系的。规格里的每个需求、每个场景,都能对应到具体的实现文件和验证方式。这就避免了"文档写完就腐烂"的老问题。
规格驱动开发更像是把"需求分析"和"验收标准"这两件事,从人的脑子里、从会议记录里,搬到一个 AI 和人都能读的中间层。它不替代测试,也不替代文档,而是给这两者提供一个共同的源头。
2. OpenSpec 的目录结构与文件约定拆解
2.1 核心目录布局长什么样
OpenSpec 的目录设计是它最值得学的地方,因为它直接决定了 AI 能不能"找得到上下文"。一个典型的 OpenSpec 项目结构大致是这样:
project-root/ ├── openspec/ │ ├── specs/ # 已确认的规格,按能力域拆分 │ │ ├── auth/ │ │ │ └── spec.md │ │ └── order/ │ │ └── spec.md │ ├── changes/ # 进行中的变更提案 │ │ └── add-password-reset/ │ │ ├── proposal.md │ │ ├── tasks.md │ │ └── design.md │ └── project.md # 项目级约定与背景 ├── src/ └── ...这个结构里,specs/存的是"当前系统是什么样",changes/存的是"我们打算改成什么样"。这个区分非常关键,它对应了软件工程里"现状"和"变更"的分离。AI 在处理一个任务时,先读specs/了解现状,再读changes/里对应的提案了解目标,最后才动手写代码。
project.md是全局约定,比如技术栈、代码风格、命名规范、禁止使用的库等等。我建议把团队所有"口头约定"都塞进这里,因为 AI 不会读你的群聊记录,但它会读这个文件。
2.2 规格文件里到底该写什么
规格文件(spec.md)不是随便写写需求就完事。它有一套推荐的结构,核心是**需求(Requirement)和场景(Scenario)**两层。
一个需求描述"系统应该具备什么能力",一个场景描述"在什么条件下,系统应该有什么行为"。举个认证模块的例子:
## Requirement: 用户密码登录 系统应支持用户通过邮箱和密码登录。 ### Scenario: 正确的凭据 - Given 一个已注册且状态正常的用户 - When 用户提交正确的邮箱和密码 - Then 系统返回访问令牌 - And 令牌有效期不超过 24 小时 ### Scenario: 密码错误 - Given 一个已注册用户 - When 用户提交错误的密码 - Then 系统返回 401 错误 - And 不泄露"用户是否存在"的信息这种 Given-When-Then 的写法不是 OpenSpec 独创的,但它在这里的作用变了——它不只是给人读的验收标准,更是给 AI 的行为约束。AI 读到"不泄露用户是否存在的信息",就知道错误信息不能区分"用户不存在"和"密码错误"。这种细节,如果你只在提示词里说"写个登录",AI 百分之百会漏掉。
2.3 变更提案为什么单独放一个目录
changes/目录是 OpenSpec 里我觉得最聪明的设计。每个变更是一个独立文件夹,里面有提案、任务清单、设计说明。这样做的好处是:变更是有生命周期的。
一个变更从提出到完成,会经历"提案 → 实现 → 归档"三个阶段。完成后,这个变更文件夹会被合并进specs/,然后从changes/里移除。这意味着changes/目录永远只包含"正在进行的工作",AI 读它的时候不会被历史包袱干扰。
tasks.md是任务清单,把一个大变更拆成可执行的小步骤。这个文件对 AI 特别有用,因为它给了 AI 一个明确的"进度条"。你可以让 AI 读tasks.md,完成一项就勾掉一项,这样即使中途换会话,AI 也能从上次停下的地方继续。
design.md是可选的,用来记录技术决策和权衡。比如"为什么选 Redis 而不是数据库存 session",这种决策写下来,AI 在后续实现里就不会自作主张换方案。
3. 把 OpenSpec 接进 Claude Code 和 Codex CLI 的实际做法
3.1 为什么不能只靠 CLI 工具的默认行为
Claude Code 和 Codex CLI 这类工具默认的工作方式是"你给指令,它读代码,它改代码"。它们很聪明,但有个共同问题:它们倾向于在局部做最优解,而不是在全局做一致解。
举个例子,你让 Codex CLI 给订单模块加个折扣功能,它会去看订单相关的代码,然后加一个折扣字段、一段计算逻辑。但它不会主动去检查:这个折扣规则和现有的促销模块冲突吗?数据库迁移脚本写了吗?API 文档更新了吗?因为这些信息不在它读到的文件里。
OpenSpec 的作用就是把这些"它读不到的信息"变成"它能读到的文件"。你不需要改变 CLI 工具的行为,只需要在项目里放好 OpenSpec 目录,然后在提示里告诉 AI:"先读 openspec/specs/order/spec.md 和 openspec/changes/add-discount/proposal.md,再动手。"
3.2 给 AI 的提示词该怎么组织
接入 OpenSpec 之后,提示词的组织方式会发生根本变化。以前你写的是"帮我实现 X 功能",现在你写的是"按照 openspec/changes/xxx 的提案实现,遵守 openspec/project.md 的约定"。
我常用的提示词模板大概是这样:
请先阅读以下文件: 1. openspec/project.md(项目约定) 2. openspec/specs/<相关域>/spec.md(现状) 3. openspec/changes/<当前变更>/proposal.md 和 tasks.md(目标) 然后: - 按 tasks.md 的顺序逐项实现 - 每完成一项,在 tasks.md 里标记完成 - 实现过程中如果发现规格有歧义,先停下来问我,不要自行假设 - 实现完成后,对照 spec.md 里的 Scenario 逐条自检这个模板的关键在于最后两条。"发现歧义先问"能避免 AI 自作主张,"对照 Scenario 自检"能让它自己发现遗漏。实测下来,加了这两条之后,AI 一次生成就能跑通的比例明显上升。
3.3 不同 CLI 工具的适配差异
Claude Code 和 Codex CLI 在配合 OpenSpec 时,行为上有一些差异,值得注意。
Claude Code 对长上下文和文件结构的理解更好,它能一次性读多个文件并建立关联。所以你可以让它一次读整个changes/目录。但它有时候会"过度解读",把规格里没写的东西也脑补进去,所以约束要写死。
Codex CLI 更偏向"精确执行",你让它改哪个文件它就改哪个,不太会主动扩展。这在配合 OpenSpec 时反而是优点——规格说什么它做什么。但缺点是它不太会主动去读你没明确提到的文件,所以提示里必须把要读的文件列全。
我的做法是:用 Claude Code 做"规格梳理和方案设计",用 Codex CLI 做"按规格实现"。前者负责把模糊需求变成清晰规格,后者负责把清晰规格变成代码。分工明确之后,两边都不容易跑偏。
4. 规格驱动开发里最容易踩的几个坑
4.1 规格写太细,反而把 AI 框死
刚开始用 OpenSpec 的时候,我犯过一个典型错误:把规格写得像伪代码。每个函数签名、每个字段类型都写进去,结果 AI 变成了"翻译器",完全没有发挥空间,生成的代码死板且冗余。
后来我调整了粒度:规格写"行为和约束",不写"实现细节"。比如"密码必须用 bcrypt 加盐哈希存储"这是约束,要写;"密码哈希函数叫 hashPassword,接收 string 返回 Promise"这是实现细节,不写。前者是 AI 必须遵守的,后者是 AI 可以自己决定的。
判断标准很简单:如果这个细节变了,会不会影响其他模块或违反业务规则?会,就写进规格;不会,就留给实现。
4.2 变更和规格不同步导致的"规格腐烂"
这是最隐蔽的坑。团队用着用着,有人直接改代码不改规格,几次之后specs/里的内容和实际代码就对不上了。AI 读到过时的规格,生成的代码自然也是错的。
解决办法有两个层面。流程上,把"更新规格"作为代码合并的必选项,就像写测试一样。工具上,可以在 CI 里加一个检查,对比规格里声明的接口和实际代码的接口是否一致。OpenSpec 本身不强制这个,但它的结构让这种校验变得可行。
我自己的习惯是:每次变更归档时,强制自己花五分钟过一遍specs/,确认没有遗漏。这五分钟能省掉后面几小时的排查。
4.3 把 OpenSpec 当成"必须完整"的负担
还有一种坑是心态上的。有人觉得既然用了规格驱动,那每个功能都得写完整规格,结果小改动也要走一遍完整流程,效率反而低了。
实际上 OpenSpec 是分层的。大变更走完整流程:提案、设计、任务、规格。小改动(比如改个文案、调个样式)完全可以跳过changes/,直接改代码。规格的价值在于约束那些"改错了代价很大"的部分,不是约束所有部分。
我一般这么判断:这个改动如果做错了,会不会影响数据、安全、或者多个模块的交互?会,就写规格;不会,就直接干。
5. 从零搭一套 OpenSpec 工作流的完整步骤
5.1 初始化项目结构
第一步是把目录建起来。不需要什么脚手架,手动建就行:
mkdir -p openspec/specs openspec/changes touch openspec/project.md然后往project.md里写项目级约定。这个文件我建议包含这几块:技术栈和版本、代码风格(缩进、命名、注释语言)、目录约定、禁止事项(比如"不要引入新的第三方库除非明确要求")、以及 AI 协作时的通用指令。
project.md不用一次写全,边用边补。每次发现 AI 犯了同类错误,就把对应的约束加进去。用久了它会变成团队的"AI 协作手册"。
5.2 写第一份规格
选一个你熟悉的、边界清晰的模块开始,比如用户资料管理。在specs/下建对应目录,写spec.md。
写的时候记住几个原则:用 Given-When-Then 描述场景;每个需求独立成节;约束要具体可验证("响应时间小于 200ms"比"响应要快"好);不要写实现细节。
第一份规格不用追求完美,它的作用是让你熟悉这套表达方式。写完可以拿给 AI 读一遍,问它"这份规格有没有歧义或遗漏",AI 往往能指出一些你自己没注意到的模糊点。
5.3 走一遍完整的变更流程
建一个changes/下的变更文件夹,写proposal.md(为什么改、改成什么样)、tasks.md(拆成可执行步骤)、可选的design.md(技术决策)。
然后让 AI 按tasks.md实现。实现过程中,你会遇到两种情况:一种是规格清晰,AI 顺利做完;另一种是规格有歧义,AI 停下来问。后者其实是好事,说明规格在起作用——它把"隐式假设"变成了"显式问题"。
变更完成后,把proposal.md里的内容合并进对应的spec.md,然后删掉changes/下的这个文件夹。这一步别偷懒,它是保持规格新鲜的关键。
5.4 和版本控制、CI 的配合
OpenSpec 目录应该进 Git,和代码一起管理。这样每次变更都有迹可循,review 的时候也能看到"规格改了什么、代码改了什么"。
CI 层面,可以加一些轻量检查:规格文件是否符合格式约定、changes/里有没有长期未归档的变更、规格里提到的接口是否在代码里存在。这些检查不用很复杂,一个脚本就能搞定,但能有效防止规格腐烂。
6. 规格驱动开发在团队协作中的真实收益与边界
6.1 收益:从"人盯人"到"规格盯人"
用了一段时间之后,最大的感受是协作方式变了。以前 AI 生成的代码要 review,review 的人得先理解需求,再判断代码对不对,很累。现在 review 的对象变成了规格——规格对不对、全不全,这个判断比看代码快得多。规格确认了,代码只要符合规格,基本就没大问题。
另一个收益是新人上手快了。新成员进来,不用先读一堆代码,先读specs/就能理解系统在做什么。规格是比代码更高层的抽象,理解成本低很多。
6.2 边界:什么场景不适合规格驱动
规格驱动不是万能的。探索性开发、原型验证、一次性脚本,这些场景写规格就是浪费时间。规格的价值在于"约束",而探索阶段恰恰需要的是"自由"。
还有一种情况是需求极度不稳定,今天定了明天就变。这种时候写规格也是白写,不如先让 AI 快速出个能跑的东西,等需求稳定了再补规格。
我的判断标准是:这个功能会不会长期存在、会不会被多人维护、改错了代价大不大。三个都是"是",就值得写规格;有一个是"否",就可以考虑跳过。
6.3 和现有工程实践的融合
OpenSpec 不需要推翻你现有的流程,它可以叠加在 Scrum、看板、或者任何你已经在用的方法上。它管的是"AI 协作这一层",不替代项目管理工具。
实际落地时,我建议先在一个小项目或一个模块上试点,跑通完整流程,积累几份规格样本,再推广到团队。直接全团队铺开,很容易因为不熟悉而流于形式。
7. 一些实操中总结的细节技巧
7.1 规格里的"反例"比"正例"更有价值
写场景的时候,大家习惯写"正常情况应该怎样"。但 AI 最容易出错的地方恰恰是异常路径。所以每个需求,我至少写一个异常场景:参数缺失、权限不足、并发冲突、外部依赖失败。这些场景写进去,AI 生成的代码健壮性会明显提升。
7.2 用规格文件当"AI 的记忆锚点"
长会话里 AI 会"忘事",这是通病。解决办法是定期让它重读规格。我一般在每个大任务开始前,都让 AI 重新读一遍project.md和相关的spec.md,相当于给它"刷新记忆"。这个动作花不了几秒,但能显著减少跑偏。
7.3 规格的版本要和代码版本对齐
如果项目有多个长期维护的版本分支,规格也要跟着分支走。别让所有分支共用一份规格,那样迟早对不上。简单做法是规格文件跟着代码一起 cherry-pick,虽然麻烦点,但比事后排查强。
7.4 定期做"规格体检"
每隔一段时间(比如每个迭代结束),花半小时过一遍specs/,删掉过时的、合并重复的、补充遗漏的。规格和代码一样,不维护就会腐烂。这个体检不用很正式,但要坚持。
7.5 别让规格变成"写给领导看的文档"
这是心态问题。规格是给 AI 和开发者用的工具,不是汇报材料。所以它应该务实、具体、可执行,而不是漂亮、全面、政治正确。如果一份规格写出来主要是为了展示,那它大概率对实际开发没帮助。
8. 关于工具选型和落地节奏的一点个人看法
工具层面,OpenSpec 本身很轻,没有强依赖,你甚至可以用纯文本加约定来实现类似效果。它的价值不在工具本身,而在那套"规格先行、变更分离、场景驱动"的方法论。所以别纠结于"是不是要用 OpenSpec 这个工具",而要关注"有没有把规格当成一等公民"。
落地节奏上,我的建议是"小步快跑"。先在一个模块上试,跑通"写规格 → AI 实现 → 归档"这个闭环,感受一下它带来的变化。如果确实减少了返工、提升了代码一致性,再逐步扩大范围。如果发现某个场景下它反而拖慢效率,就果断跳过,别为了流程而流程。
最后说个我自己的体会:规格驱动开发真正改变的不是 AI 的能力,而是我们对"需求"这件事的严肃程度。以前写代码前脑子里过一遍就动手,现在被迫把想法写清楚。这个过程本身就会暴露很多原本被忽略的问题。AI 只是个放大器,它把"想清楚"和"没想清楚"的差距放大了。规格驱动,本质上是逼着我们先想清楚。