从需求到实现的“事实源”我用OpenSpec规范团队规格管理干这行十几年我接手过不少“代码跑得飞起、文档一塌糊涂”的项目。团队里最常听到的一句话是“这个功能当初怎么定的”然后就是一通翻聊天记录、找邮件、看代码注释的考古式排查。很多时候需求文档写了但没人看哪怕看了也跟不上代码的演进最后文档沦为摆设。有没有一种工具能把“规格”真正变成团队开发流程里不可绕过的一环让需求和实现始终对得上我花了两周时间试用了OpenSpec把一套正在开发中的内部项目全量迁移进去今天这篇就是我的完整实操记录。OpenSpec不是又一个Markdown文档生成器它是一套围绕“规格即代码”理念设计的命令行工具与工作流框架。它关心的不是怎么把文档排版得漂亮而是如何让你像管理代码一样管理需求与规格定义结构、追踪变更、用CI自动校验、把规格变更和代码变更绑定在一起。如果你和我一样受够了“文档写了没人看、看了也过期”的日子这篇教程会把从安装、初始化、编写规格到接入CI的全过程拆给你看包括我踩过的坑和最终沉淀下来的工作流习惯。1. 规格管理之痛为什么大多数团队活成了一座“文档废墟”先说一个我亲眼见过的真实案例。某个项目业务侧和研发侧对“用户权限”这个模块的理解出现了偏差。业务认为“部门管理员能看到本部门所有项目的数据”研发实现的是“部门管理员能看到自己被添加为成员的项目的数据”。上线后客户投诉两边开会各自掏出一份文档。业务那份是立项时的PRD研发那份是接口设计文档都对但都对不上。最后怎么解决的代码改了文档两边也各自更新了但三个月后同一个问题换了个人又来了一遍。这种事的根子不在“人不靠谱”而在“文档的生成方式和使用方式是反人性的”。传统文档工作流有几个结构性毛病文档和代码的演进节奏脱节。代码是每天在变的文档是“想起来才写一次”。需求变更是通过口头、IM、邮件传递的文档永远追不上现实。文档的结构是“自解释”的。每个团队有自己的模板、命名、存放位置新人来了要先搞懂“我们的XX文档放在哪”没人能说清。评审靠人肉盯。文档写完了有没有人看、看了有没有提出有效意见全靠自觉和项目经理的压迫力无法自动化。变更链条不可追溯。“需求变更了”这个事实本身经常没有记录只有结果代码改动了和情绪怎么又改了。OpenSpec的切入点就是把这些“湿”的问题用“干”的方式解决。它把规格文档强制结构化为可校验的文件集合把规格变更变成类似PRPull Request的评审单元让“文档是否与准入标准一致”成为可以自动判断的客观事实。一句话规格在OpenSpec里不是艺术品是代码的前提。刚开始你可能觉得这种“强制”自己写文档的工具是枷锁但用久了你会发现真正让人舒适的恰恰是这种确定性。要知道一个规格管理方案成功与否不在于模板漂不漂亮而在于它能不能逼着团队在每个关键节点回答“为什么这么改”。2. OpenSpec的核心设计它不只是把Markdown文件做成了结构在讲实操之前我觉得有必要先把OpenSpec的底层设计逻辑讲透。不理解这个你后面遇到校验报错、CI不通过、多人协作冲突的时候会一头雾水。2.1 规格树把“混沌文档”变成“确定性目录”OpenSpec管理的核心是一个规格树Spec Tree它规定了你项目里的规格文档应该放在哪些目录、以什么命名、内部包含哪些必需章节。初始化之后你会看到类似这样的结构specs/ ├── product-context.md ├── goals.md ├── non-goals.md ├── risk.md └── capabilities/ ├── user-management/ │ ├── spec.md │ ├── requirements.md │ ├── validation.md │ └── examples/ │ ├── create-user.md │ └── delete-user.md └── billing/ ├── spec.md ├── requirements.md └── validation.md你不一定非要照抄这个目录但工具的CLI会按约定去扫描这些路径。它的核心思想是一切规格从属于某个能力capability每个能力有明确的边界描述、验收标准、校验方式和示例。这种组织方式的优势是当你需要回答“用户删除功能具体怎么定义”时你不需要翻一个5000行的Word文档只需要去看specs/capabilities/user-management/目录下的文件。2.2 规格文件的双层结构流水账与索引OpenSpec的规格文件spec.md内部结构也很有意思。它分为两部分场景叙述Scenario用自然人话描述这个功能的存在意义、面对什么用户、解决什么问题。这部分就是给非技术读者产品、测试、老板看的。能力描述Capability Description用结构化的规则条目描述系统的技术行为通常包含输入、输出、边界条件、异常处理。这部分是给开发、QA看的。这个设计很讨巧它能避免“文档给谁看”这个千古难题。以往一份PRD要么太技术业务看不懂要么太业务开发心里骂娘。OpenSpec用同一份文件强制你分层表达业务角色看场景叙述技术角色看能力描述词汇重叠区域则用验收标准互相印证。2.3 变更追踪规格也走PR流程这是OpenSpec最“戳我痛点”的设计。它把规格变更和代码变更放在同一个审查框架下。每当你想修改某个能力规格时你新建一个change文件夹下的Markdown文件描述变更原因、涉及能力、变更前后对照、影响范围。然后这个变更文件本身也会被版本控制追踪也能发起PR供团队评审。这样带来的直接好处是需求变更不再是一句话的事它有一个实体载体变更文件可检索、可回溯。代码评审和规格评审可以同步进行。一个PR里既能看到“改了哪些代码”也能看到“为什么改这些代码”的规格依据。项目的历史脉络变成了可读的故事而不是中断的文档快照。2.4 机器可校验让文档不再依靠信仰OpenSpec内置了validate命令它不仅能检查你的文件结构是否合法缺了哪些必需章节、哪些文件没按约定命名还能基于你的规则做一定的逻辑校验。比如你可以定义“某个能力必须有至少一条验收标准”然后CI跑一下不合格直接给红灯。这个“机器可校验”的属性是把文档从“装饰物”变成“工程质量门禁”的关键。如果一份规格文档通不过自动校验代码就不应该合入主干。这样一来“文档写了没”不再是个模糊问题而是像“单元测试跑了没”一样卡在流程里躲都躲不掉。3. 从零部署安装、初始化与我的第一次踩坑纸上谈兵没意思接下来实操。我的环境是macOS Homebrew项目本身是Node.js TypeScript如果你的环境不同安装方式大同小异。3.1 安装OpenSpec CLIOpenSpec提供了多种安装方式。我使用的是Homebrewbrew install openspec如果你不用Homebrew也可以用npm或者直接下载二进制npm install -g openspec/cli安装完先看一眼版本和帮助信息确认环境OKopenspec --version openspec --help注意如果你同时用nvm切换Node版本用npm全局安装时要多留个心眼切版本后CLI可能消失。建议用brew或直接二进制安装省心一点。3.2 初始化规格树进入你要管理规格的项目根目录执行openspec init这个命令会做几件事创建specs/目录骨架、生成specs/product-context.md等前置文档模板、添加一个.openspec或openspec.config.json配置文件具体名称取决于版本。初始化后我遇到的第一个问题是它默认生成的产品上下文模板是很通用的直接往里填业务细节时我总是忍不住写成一个“简介”但模板真正想要的是“现状痛点 非目标 约束条件”。这是OpenSpec的一种非常隐性的引导它不只是让你写“项目是干嘛的”而是要求你明确“什么不在范围内”。3.3 配置文件的关键参数打开生成的配置文件核心字段如下{ projectName: my-service, specRoot: specs, capabilitiesRoot: specs/capabilities, validation: { requireRequirements: true, requireValidation: true, requireExamples: false, severity: warning } }projectName项目名影响后续变更记录里的上下文渲染。specRoot/capabilitiesRoot规格树的根路径一般不修改。validation.requireExamples我一开始设成false后来开了true。原因是示例文件对新人理解规格特别重要尤其是复杂权限、退款、超时这类场景一个有代表性的输入输出示例比三行描述都有用。severity校验不过时按“警告”还是“错误”处理。CI阶段必须设为error否则等于没门槛。4. 编写与校验核心规格以“用户管理”为例走一遍全流程纸上谈兵的框架聊完了下面我以一个完整的例子走一遍“建模-编写-校验-落地”的流程。我们假设要给一个内部系统新增“用户管理”能力。4.1 创建能力目录OpenSpec不要求你手动mkdir有现成命令openspec capability new user-management执行后会生成specs/capabilities/user-management/ ├── spec.md ├── requirements.md └── validation.md这时候如果你立刻执行openspec validate它大概率会报错因为几个文件的必填章节还是空的。4.2 填写场景叙述spec.md打开spec.md它的开头通常长这样# User Management ## Summary Provide functionality for managing user accounts in the system. ## User Story As an admin, I want to create and deactivate user accounts so that only approved people can access the system. ## Requirements ...这里最重要的是User Story部分。我的写法建议是不要只写“happy path”把权限边界谁能执行这个操作、数据保留策略删除是物理删还是逻辑删都要明确写出来。举例## User Story As an admin, I want to create user accounts and deactivate them when the employee leaves the company, so that access is revoked in a timely manner. ## Boundaries Conditions - Only users with the admin role can manage accounts. - When an account is deactivated, the users tokens must be revoked immediately. - Deleted accounts are soft-deleted and retained for 90 days for audit purposes.4.3 编写需求条目requirements.md这一部分要写“验收规则”每一句话都应该是可测试的。模板格式通常是这样## Requirement: Create user with valid email The system MUST create a user when the provided email address is valid and unique. ## Requirement: Duplicate email rejection The system MUST return a 409 Conflict error when trying to create a user with an existing email. ## Requirement: Role assignment The system MUST allow assigning at most one role per user.这里我有一条经验把每一条需求都和代码里的测试用例挂钩。OpenSpec本身不自动做这件事但我自己的习惯是在需求的描述末尾加上Tests: tests/integration/user-management/create-user.test.ts,这样验证规格是否落地时直接跑对应的测试文件即可。4.4 填验收标准validation.md这是技术团队最该花时间的文件。它可能长这样## Validations ### Create user - Given a valid email user1example.com, calling POST /v1/users SHOULD return 201 Created and include the new user ID in the response body. - Given an invalid email abc, calling POST /v1/users MUST NOT create a user, and MUST return 400 Bad Request. - Given an email already in use, calling POST /v1/users MUST return 409 Conflict. ### Deactivate user - Given an active user with ID 123, calling DELETE /v1/users/123 SHOULD return 204 No Content. - Given a nonexistent user ID, calling DELETE /v1/users/999 MUST return 404 Not Found.拍板一条规格验收标准的最好方法是让写代码的工程师在今天状态下去读看它是否足够精确到可以直接删掉“你自己在脑补的表结构”。如果写到一半开始纠结“这里该返回400还是409”恭喜这就是这一条规则该写清楚的地方。4.5 执行校验写完后运行openspec validate默认情况下它会输出通过/不通过的摘要。如果你在CI里希望输出JSON格式方便解析可以加--format jsonopenspec validate --format json我第一次跑的时候挂了两处一处是requirements.md里漏了MUST关键字OpenSpec要求需求语句必须包含RFC 2119的关键词MUST、SHOULD、MAY等另一处是validation.md里的步骤没有写“Given/When/Then”结构。检查规则非常严格但它确实能筛掉大部分“含糊其辞”的描述。5. 变更管理实操一次规格修订的完整流程规格的首次定义只是开始真正的重头戏是后续的变更。下面我走一遍“用户管理支持批量导入”这个变更的完整处理链路。5.1 创建变更单据命令openspec change new add-bulk-user-import它会生成一个changes/add-bulk-user-import.md文件。内容类似# Change Proposal: Add Bulk User Import ## Summary Allow admins to import multiple users from a CSV file. ## Motivation - Many customers need to onboard dozens of users at once. - Manual creation is error-prone and slow. ## Impacted Capabilities - capabilities/user-management ## Changes ### Requirements Added - Requirement: Bulk import from CSV - The system MUST parse a CSV file with columns email, name, role. - Requirement: Partial failure handling - The system MUST return per-row errors, and MUST NOT roll back rows that succeeded. ### Requirements Removed - (none) ### Requirements Modified - (none) ## Rollout Plan - Feature flag: bulk-import enabled for internal teams first.这一步的重点是Impacted Capabilities它建立了变更与能力规格之间的关联。后续代码PR描述里你可以直接引用这个文件让评审者知道“这个PR在规格层面想干什么”。5.2 先改规格还是先改代码我的习惯是先改规格再写代码。哪怕最后实现出来和预期有差异也要回头同步修正规格。因为规格是团队的契约代码是实现契约不清晰就动工多半会返工。具体操作上我会把这个变更文件提交成一个独立的commit然后代码改动基于它再去写。在Review时其他人能够顺着“规格变更 → 实现差异”这条线看比直接看一堆diff文件舒服多了。5.3 变更的闭环验证当代码合入、功能上线后变更文件怎么处理OpenSpec的做法是“合并”回规格树本体的最新状态类似feature branch merge到main。此时再跑一次openspec validate确保所有新需求都出现在能力目录的requirements.md中。我建议在CI脚本里加一步openspec validate --strict这个命令如果检测到“变更文件已批准但对应需求尚未合入规格树”的情况会直接报错。这就保证了任何被批准的需求变更都不会只停留在代码层而会回流到规格文档中。6. 接入CI与团队协作把规矩焊死在流程里单独一个工具在本地玩得再花哨如果没有在团队流程里变成强制门禁最后还是会变成“有人用的玩具”。所以CI集成这块我多下点笔墨。6.1 GitHub Actions 集成示例OpenSpec官方推荐用GitHub Action但本质上它就是跑两条命令所以任何CI平台都适用。我的workflow文件里核心部分是这样name: spec-validation on: [pull_request] jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Install openspec run: npm install -g openspec/cli - name: Run validation run: openspec validate --strict这里有个小坑如果你们的仓库很大跑全量校验会很慢。一个折中方案是只对变更影响的规格节点做校验git diff --name-only HEAD..origin/main | grep ^specs/ | openspec validate --files -这个命令读取变更的文件列表只校验这些文件所在的capability目录。第一次跑的时候我因为没加这个过滤CI足足跑了1分半钟后来改成增量校验后稳定在5秒内。6.2 分支保护与PR模板CI跑只是第一步更重要的是让“规格校验不过就不能合入”成为分支保护规则。在GitHub的Branch Protection里加上“spec-validation必须通过”的检查然后关键在于团队的PR模板。我给我们团队定了一个PR描述模板## 变更摘要 ... ## 关联规格变更 - [ ] 本次改动静默修bug无规格变更 - [ ] 关联规格变更文件changes/xxxx.md - [ ] 涉及能力user-management / billing / ... - [ ] openspec validate --strict 已通过一开始团队成员觉得这个是负担但坚持两周后大家发现“PR描述里写清楚规格依据”不仅帮助评审者自己几个月后回看时也能快速想起当时为什么这么改。6.3 多人协作冲突的实战处理多人同时修改同一个能力目录下的规格文件git冲突几乎是必然的。OpenSpec的规格文件是纯Markdown冲突不可避免。我遇到过一个典型场景我和同事同时改了requirements.md他加了一条关于密码强度的需求我加了一条关于多租户数据隔离的需求。git合并时其实没有实际冲突行但逻辑上这两条需求放的位置、编号顺序都不一样导致最终文件里需求号错乱。这个问题的解法是一定要执行一次完整的openspec validate并且关注它的编号提示。OpenSpec新版本似乎对需求ID有自动规范化的设计如果版本支持可以用openspec lint --fix自动重排。在我用的版本里处理冲突的标准流程是先合并代码时选择保留双方新增行。手动查看requirements.md按逻辑重新排列需求顺序。运行openspec validate --strict修复所有排序/格式报错。确认通过后提交。经验之谈不要让多个人在同一条能力规格里“大改”。如果确实需要重构能力定义最好是建一个分支把该能力的所有变更集中在一个PR里合并时再让其他同事改别的能力。压力最小冲突最少。6.4 把规格校验放进Pre-commit Hook也许你团队里的CI流程比较慢那就在本地先把规矩立起来。我们使用的是huskylint-staged在package.json里配置{ lint-staged: { specs/**/*.md: openspec validate --strict } }这样每次提交碰到specs/目录下的文件自动触发本地校验。不通过就提交不上去自然也不会把坏文档推给CI。代价是第一次配置时几个老同志的Markdown书写习惯被硬生生掰了过来——比如不再用“某某功能应支持XXX”这种没有动作主体的陈旧句式而是必须明确“The system MUST ...”。但适应之后我观察到一个明显变化文档里含有“MUST/SHOULD/MAY”这类精确约束词的语句占比大幅上升模棱两可的表述明显减少。7. 避坑指南与最终体验我从OpenSpec中得到了什么工具好不好用最终要看在真实项目里扛不扛得住。这一个月用下来我把踩过的坑和我主观感受整理一下供你参考。7.1 三个最容易踩的坑第一个坑过度建模一上来就追求完美规格树。OpenSpec的生态里其实没有“完美”一说规格树的划分粒度需要跟着组织架构和系统边界走。我刚上手时恨不得把每个函数都定义成一个能力结果写了两天就写不下去了因为需求的更新频率没那么高大量规格文件是僵尸档案。后来我把粒度调到“面向用户可见功能”级别比如用户管理、计费、报表、通知中心才找到舒适区。第二个坑只建能力不维护全局上下文。specs/product-context.md和goals.md是很容易被忽略的“门面文档”因为它们不直接产生验收标准。但真正去回答“这个能力为什么存在”时这些文件极其有用。后来我强制要求每个季度审一次这三个文件看目标是否偏移、非目标是否需要调整。这比写一堆新能力有用得多。第三个坑把校验当摆设开了CI但没人看。首次接入CI后团队里有人为了绕过红灯直接在PR描述写“spec-validation失败但本PR不影响规格”然后强行合入。我不建议在流程上硬卡这一点更好的办法是默认所有规格变更都要带上关联的change文件让“人为例外”被记录在案。流程的刚性留给机器例外的解释权留在人手里反而更不容易撕裂。7.2 用了一个月我的具体体感先说优点协作的摩擦显著降低。现在开评审会默认先打开规格树对着每个能力的validation逐条过。没有人能再说“我不知道还有这个约定”。新人上手速度变快。新同事跟我说的第一句话是“你们的文档结构好清楚我知道该去哪查了”。需求变更有了“存档”。哪怕是几个月前废掉的一个方案你都能在changes目录里找到它为什么被废弃的来龙去脉。再说缺点和局限学习曲线是存在的。团队成员刚接触时对RFC 2119关键字、Given/When/Then结构、能力边界的划分都相当陌生头一周效率下降是正常的。不适合“边做边探索”的原型阶段。如果项目还处于从0到1的快速拼凑期今天定义明天推翻强行推开OpenSpec只会让你厌恶写文档。我建议等业务边界稍微清晰了再接进来。生态还比较年轻。相比成熟的ConfluenceJira组合它在富文本排版、权限管理、阅读体验上其实很朴素。如果你团队非要一个“高大上的门户”OpenSpec给不了它的核心价值在治理流程而非展示效果。7.3 这个工具适合谁不适合谁如果你符合下面任意情况OpenSpec大概率能帮上忙团队规模在5人以上“需求口头说、文档没人写”的现象已严重影响交付质量。你的业务对合规和可追溯性有硬性要求需要明确每一次需求变更的前因后果。你已经拥抱CI、代码评审和自动化测试希望文档也像代码一样被评审、被校验。但如果你只是一个人维护一个小工具项目每天自己写自己看那用TXT文件随手记两笔就够了OpenSpec带来的结构性收益抵不上它在心智和流程上的开销。最后再说一个小技巧。我在实际使用中发现把openspec validate --strict放到CI和pre-commit里只是第一步最有价值的事情其实是在能力规格评审时喊上产品和测试一起过一遍。很多需求歧义在开发之前就能被“这句话不满足Given/When/Then结构”这种校验暴露出来——它逼着你把话说明白而这恰恰是协作中最稀缺的东西。规格工具本身再先进也替代不了人与人之间把话讲清楚的意愿但它能把“讲清楚”从美德变成底线。