1. 从能跑就行到规范先行SDD 到底在解决什么第一次接触 OpenSpec 这套东西是因为团队里一个很典型的老问题需求文档、接口约定、代码实现三份东西各说各话。产品在文档里写用户下单后触发通知后端理解成同步调用前端以为要轮询测试按第三种逻辑写用例。等到联调那天三个人对着屏幕互相问你这块到底怎么设计的然后花两天时间把逻辑重新对齐一遍。这种事发生三次之后我开始认真思考一个问题有没有一种方式能让规范本身变成可执行、可校验、可追溯的东西而不是躺在文档库里吃灰的 WordSDD也就是 Spec-Driven Development规范驱动开发核心思路就是把规范从附属品提升为开发流程的一等公民。传统开发里规范是给人看的代码是给机器跑的两者之间靠人的理解来桥接而人的理解天然有偏差。SDD 想做的事是让规范具备机器可读的结构让代码生成、校验、测试用例推导都能围绕同一份规范展开。OpenSpec 就是这套理念的一个工程化落地工具它用一套约定好的目录结构和配置文件把规范变成项目里真实存在的、可以被工具链消费的资产。这里要先说清楚一个容易混淆的点SDD 不是写更详细的文档也不是用 AI 帮你写代码这么简单。它的本质是把意图显式化。你写下一段规范不只是描述要做什么还要描述边界在哪异常怎么处理验收标准是什么。这些信息在传统开发里往往散落在需求评审的会议纪要、聊天记录、某个人脑子里SDD 要求你把它固化下来。OpenSpec 则提供了承载这些规范的骨架以及一套让规范与代码保持同步的机制。那 OpenSpec 具体长什么样它的核心是一个项目根目录下的openspec文件夹里面按功能模块组织规范文件每个规范文件用结构化的格式描述行为、输入、输出、约束。配合config.yaml做项目级配置声明规范路径、校验规则、生成目标等。工具链读取这些规范后可以做的事情包括校验规范本身的完整性、根据规范生成接口骨架、比对代码实现与规范的偏差、在规范变更时提示受影响的代码范围。这套机制的价值在于它把规范与实现的一致性从依赖人的自觉变成了依赖工具的强制。适合谁来用这套东西我的判断是三类人收益最明显。第一类是多人协作的中大型项目尤其是前后端分离、跨团队对接频繁的场景规范不一致的代价被放大得很厉害。第二类是需要长期维护的项目半年后回来看代码如果规范还在且准确理解成本会低很多。第三类是正在尝试把 AI 编码工具引入工作流的团队因为 AI 生成代码的质量高度依赖输入的清晰度一份结构化的规范比一段模糊的自然语言描述效果好得多。反过来说如果是个人做的一次性小脚本或者需求天天变的探索性原型上 SDD 的收益可能覆盖不了成本这点要诚实评估。我自己的体会是SDD 和 OpenSpec 这类工具最大的门槛不在技术而在习惯。你得先接受写规范是开发的一部分不是额外负担这个前提才可能真正用起来。下面我会从环境准备、规范结构设计、配置细节、与编码工具的配合、以及实际踩过的坑几个角度把整套流程拆开讲。2. OpenSpec 项目骨架与 config.yaml 的配置逻辑2.1 目录结构不是随便定的每一层都有它的职责OpenSpec 的目录组织方式乍看只是把文件分门别类放好但实际用起来会发现这个结构本身就在约束你的思考方式。典型的骨架大致是这样project-root/ ├── openspec/ │ ├── specs/ │ │ ├── user-auth/ │ │ │ ├── spec.md │ │ │ └── cases.yaml │ │ └── order-flow/ │ │ ├── spec.md │ │ └── cases.yaml │ ├── changes/ │ │ └── 2024-xx-xx-add-refund/ │ │ └── proposal.md │ └── archive/ ├── config.yaml └── src/specs目录放的是当前生效的规范按功能模块分子目录。每个模块下通常有一个描述性的spec.md和一个结构化的cases.yaml。为什么要把描述和用例分开因为这两类信息的消费者不同。spec.md主要给人看讲清楚这个模块的意图、边界、设计取舍cases.yaml主要给工具和测试用把输入输出、预期行为写成机器可解析的形式。混在一起写会导致两边都不讨好——人读起来太啰嗦机器解析起来又太模糊。changes目录是我个人觉得设计得最巧妙的部分。它借鉴了变更提案的思路任何对规范的修改不是直接改specs里的文件而是先在changes下建一个带日期的提案目录写清楚这次改什么、为什么改、影响哪些模块。等提案评审通过、代码实现完成再把变更合并进specs提案归档到archive。这个流程强制你把规范变更当成一件需要走流程的事而不是随手一改。实测下来这个约束能挡掉很多改着改着就忘了同步文档的问题。2.2 config.yaml 里那几个关键字段配错了会很难受config.yaml是 OpenSpec 的项目级配置入口它决定了工具链去哪里找规范、按什么规则校验、生成什么产物。字段不多但每一个都有实际影响。下面这张表是我整理的核心字段和它们的实际作用字段作用常见取值配错的后果specs_path指定规范根目录./openspec/specs工具找不到规范校验直接失败changes_path指定变更提案目录./openspec/changes变更流程无法追踪schema_version规范格式版本1.0版本不匹配导致解析报错validate_on_commit提交时是否校验true/false关掉后规范容易腐化generate_targets生成产物类型interface,test,doc配错会生成无用文件ignore_patterns忽略的路径[**/draft/**]草稿被误校验噪音多specs_path和changes_path这两个基本不会配错但schema_version和validate_on_commit是新手最容易忽略的。schema_version如果不写或者写错工具在解析规范文件时会按默认版本处理遇到格式差异就报一堆看不懂的错。我的建议是显式写上当前工具链支持的版本号别依赖默认值。validate_on_commit我强烈建议开成true虽然它会让每次提交多花几秒钟做校验但这几秒钟换来的是规范不会悄悄腐化。我见过太多项目一开始规范写得好好的几个月后因为没人校验规范里的字段和实际代码完全对不上最后整套 SDD 流程名存实亡。generate_targets这个字段值得单独说。它决定了 OpenSpec 根据规范生成什么。常见的取值有interface生成接口定义骨架、test生成测试用例模板、doc生成可读文档。我的经验是不要一次全开。刚上手时只开interface让工具帮你把接口签名对齐跑顺了再逐步加test。全开的话生成物太多你反而不知道该看哪个而且生成物和手写代码混在一起容易乱。2.3 规范文件本身怎么写才算合格规范文件的质量直接决定整套流程的收益。我总结了一个判断标准一份合格的规范应该能让一个没参与过需求讨论的人仅凭它就能写出符合预期的实现。这个标准听起来高但它是可操作的。具体来说一份规范至少要覆盖四类信息意图这个模块解决什么问题为什么需要它接口输入是什么、输出是什么、数据结构长什么样行为正常流程怎么走、异常怎么处理、边界条件是什么验收怎么判断实现是对的有哪些必须通过的场景spec.md负责前两类和第三类的描述性部分cases.yaml负责第三类的结构化部分和第四类。举个具体的例子假设是用户登录这个模块cases.yaml里的一条用例大概长这样- id: login-success description: 正确账号密码登录成功 input: username: test_user password: correct_password expect: status: 200 body: token: non-empty expires_in: 3600 - id: login-wrong-password description: 密码错误返回明确错误 input: username: test_user password: wrong_password expect: status: 401 body: error_code: AUTH_INVALID_CREDENTIALS注意expect里的写法。token: non-empty表示只校验非空不校验具体值因为 token 是动态的。error_code则要求精确匹配因为错误码是契约的一部分。这种哪些字段严格校验、哪些字段宽松校验的区分是写用例时最需要拿捏的地方。写得太严测试会因为无关的字段变化而失败写得太松又起不到校验作用。我的经验是对外契约字段严格校验内部实现细节宽松处理。3. 把规范接进编码工具链Claude Code 的配合方式3.1 为什么要在 SDD 流程里引入编码助手规范写好了接下来是把它变成代码。这一步如果纯手工规范的价值会打折扣因为人还是会偷懒、会走样。引入编码助手的目的是让从规范到代码这一步有工具参与减少人为偏差。Claude Code 是这类工具里比较适合配合 SDD 的一个原因是它对结构化输入的理解能力比较强你把一份规范丢给它它能比较准确地推导出实现骨架而不是瞎编。这里要说明一点编码助手不是替代你思考而是放大你规范的质量。规范写得清楚它生成的代码就靠谱规范写得含糊它生成的代码就是看起来对但经不起推敲。所以我在实际使用中的顺序永远是先把规范打磨到位再让工具生成最后人工审查。跳过第一步直接让工具生成等于把不确定性放大了。3.2 环境准备安装与基础配置Claude Code 的安装方式根据平台不同有差异。在 macOS 和 Linux 上通常通过包管理器或者官方提供的安装脚本完成Windows 上则需要注意终端环境的兼容性建议在 WSL 或者 PowerShell 较新版本下操作。安装完成后第一次运行需要完成登录和基础配置。这里有个实际经验安装路径和项目路径尽量不要有中文和空格否则某些工具链在解析路径时会出问题这个坑我在两个项目里都踩过。配置层面Claude Code 支持通过配置文件声明模型、工作目录、忽略规则等。和 OpenSpec 配合时关键是把工作目录指向项目根让它能同时看到openspec目录和src目录。这样你在让它生成代码时它可以直接读取规范文件作为输入而不是你手动复制粘贴。忽略规则里建议把openspec/archive加进去归档的提案不需要参与生成加进去能减少噪音。关于模型选择不同模型在代码生成上的表现差异明显。我的做法是规范解析和骨架生成用理解能力强的模型细节填充和重构用代码能力强的模型。这个分工不是绝对的但比一个模型用到底效果好。另外要注意某些模型在特定地区可能不可用这个要提前确认别等到流程跑到一半才发现调不通。3.3 让工具读懂规范提示词的组织方式直接对编码助手说根据规范生成代码效果往往一般因为它不知道你关注哪些部分。我的做法是把提示词分成三段上下文、任务、约束。上下文部分明确告诉它规范文件的位置和结构比如项目规范在 openspec/specs/user-auth/ 下spec.md 描述意图cases.yaml 描述验收场景。任务部分说清楚这次要生成什么比如根据规范生成登录接口的实现骨架包括路由、参数校验、错误处理。约束部分列出必须遵守的规则比如错误码必须与 cases.yaml 中的 error_code 完全一致不要引入规范里没提到的依赖。这套提示词结构的好处是它把规范和生成之间的映射关系显式化了。工具不需要猜你的意图你也不需要反复调整。实测下来用这套结构生成的代码第一次就能通过大部分验收用例剩下的偏差集中在边界条件处理上人工补一下就行。3.4 生成之后的校验闭环生成完代码不是结束而是校验的开始。OpenSpec 的校验能力在这里派上用场把生成的代码和规范做比对看接口签名是否一致、错误码是否匹配、必填字段是否都处理了。这个比对不需要你手动做工具链可以跑。我的习惯是把这个校验挂到提交钩子上每次提交前自动跑一遍不通过就拦下来。这个闭环的价值在于它让规范和代码之间有了持续的约束关系。传统开发里规范写完就没人看了代码改着改着就和规范脱节。有了这个闭环任何脱节都会在校验时暴露出来逼着你去同步。刚开始会觉得烦但用久了会发现这种烦换来的是项目长期的可维护性。4. 实战中真正会卡住你的几个地方4.1 规范粒度的拿捏太细和太粗都是坑这是我踩得最深的一个坑。刚开始用 OpenSpec 时我恨不得把每个函数的行为都写成规范结果规范文件比代码还长维护成本高得离谱团队怨声载道。后来我调整了策略只对跨模块的接口契约和有明确验收标准的业务逻辑写规范模块内部的实现细节不写。这个粒度调整之后规范文件精简了大概六成但覆盖了九成的协作痛点。判断粒度的标准其实很简单如果这段逻辑只有一个人维护且短期内不会变就不用写规范。规范的价值在于多人协作和长期维护脱离这两个前提写规范就是纯成本。我见过一些团队为了规范而规范把简单的 CRUD 也写成厚厚的规范文档最后没人看纯粹是形式主义。4.2 规范变更的同步问题规范改了代码没改这是 SDD 流程里最危险的状态。OpenSpec 的changes机制就是为了解决这个问题但它需要你自觉走流程。实际使用中最容易出问题的是紧急修复场景线上出 bug你直接改代码忘了同步规范。等下次有人按规范理解代码时就被误导了。我的应对办法是紧急修复允许先改代码但必须在 24 小时内补一个变更提案。这个提案不需要很详细但要记录改了什么、为什么改。补提案这个动作本身就是一次规范与代码对齐的检查。另外我会在 CI 里加一个检查如果代码有变更但changes目录没有对应的提案就给出警告。这个警告不阻断合并但会提醒人补上。4.3 团队协作中的认知差异SDD 推行的最大阻力往往不是技术而是人的习惯。有人觉得写规范是浪费时间有人觉得规范约束了灵活性。我处理这个问题的方式是先在小范围试点用数据说话。选一个协作痛点最明显的模块用 SDD 流程做一遍记录下因为规范清晰而节省的沟通时间和因为校验而提前发现的偏差。等这些数据出来再向团队推广阻力会小很多。还有一个细节规范的评审要轻量化。不要搞成必须三个人签字那种重流程那样没人愿意写规范。我的做法是规范变更只需要一个相关方 review 即可重点是快让规范能跟上代码的节奏。重流程的规范体系最后都会因为太慢而被绕过。4.4 工具链的版本兼容OpenSpec 和编码助手都在快速迭代版本兼容问题时有发生。我遇到过schema_version升级后旧规范文件解析失败的情况也遇到过编码助手更新后提示词效果变化的情况。应对办法是锁定版本在项目里记录当前使用的工具版本升级前先在测试分支验证确认没问题再全量升级。别在生产分支上直接升级工具链这个教训我是用一次线上事故换来的。5. 一个完整的小案例从规范到代码的走通5.1 场景设定假设我们要实现一个优惠券核销功能。需求是用户下单时可以使用优惠券系统需要校验优惠券有效性、扣减库存、记录核销日志。这个场景涉及多个模块协作正好适合用 SDD 流程走一遍。5.2 规范编写先在openspec/specs/coupon-redeem/下建spec.md写清楚意图和边界优惠券只能核销一次、过期券不可用、库存不足时核销失败并返回明确错误码。然后写cases.yaml把关键场景结构化- id: redeem-success input: { coupon_id: C001, order_id: O001 } expect: { status: 200, remaining_stock: 99 } - id: redeem-expired input: { coupon_id: C002, order_id: O002 } expect: { status: 400, error_code: COUPON_EXPIRED } - id: redeem-out-of-stock input: { coupon_id: C003, order_id: O003 } expect: { status: 409, error_code: COUPON_OUT_OF_STOCK } - id: redeem-duplicate input: { coupon_id: C001, order_id: O004 } expect: { status: 409, error_code: COUPON_ALREADY_USED }5.3 生成与校验把这份规范作为输入让编码助手生成核销接口的骨架。生成后跑 OpenSpec 的校验重点看错误码是否和cases.yaml一致、库存扣减逻辑是否处理了并发。实测下来生成代码在正常流程上基本没问题问题集中在并发场景——两个请求同时核销同一张券时可能都通过了有效性校验。这个偏差是规范里没写清楚的我补了一条并发核销必须保证幂等的约束重新生成后问题解决。这个案例说明一个道理规范的质量决定生成的质量而规范的质量靠迭代。第一版规范不可能完美重要的是有校验闭环让偏差能被发现和修正。5.4 归档与沉淀功能上线后把changes里的提案归档到archivespecs里的规范更新为最终版本。归档不是删除而是标记为已生效。这样后续有人想了解这个功能的历史决策可以翻归档的提案看到当时的取舍。这个沉淀对长期维护的项目价值很大尤其是人员流动之后新接手的人能通过规范和历史提案快速理解设计意图。6. 我对这套流程的真实看法用 OpenSpec 和 SDD 这套东西大概一年我的整体判断是它适合对协作效率和长期可维护性有要求的项目不适合追求短期速度的场景。它的收益不是立竿见影的而是在项目跑到中期、人员开始流动、需求开始叠加的时候才显现出来。如果你现在做的项目三个月就结束或者只有你一个人维护那这套流程的成本可能大于收益不必强上。真正让我坚持用下来的是它解决了一个我一直很头疼的问题知识的沉淀。传统开发里很多设计决策只存在于当事人的记忆里人一走就断了。SDD 把这些决策固化在规范和历史提案里让项目有了记忆。这个价值在人员流动频繁的团队里尤其明显。最后分享一个实操小技巧规范文件里留一个决策记录小节专门记录为什么这么设计和考虑过但放弃的方案。这部分内容对工具生成没用但对人理解项目极其有用。我现在的习惯是每次做一个非显而易见的设计选择就顺手记一笔。半年后回头看这些记录帮我省了无数次当时为什么这么写的困惑。
阅读完成 · 觉得有帮助?