
1. 从“规格散落一地”说起OpenSpec 到底想解决什么问题如果你参与过稍微有点规模的软件项目大概率见过这样的场景需求文档在飞书里、接口定义在 Swagger 里、数据库字段在某个 Excel 里、前端同学手里的字段名和后端返回的对不上、测试同学拿着三个月前的用例在跑、上线前夜才发现某个边界条件谁都没写清楚。这不是某一个人的锅而是“规格Spec”这件事在传统研发流程里天然就是碎片化的——它散落在各种工具、各种角色、各种时间点里没有一个统一的、可被机器读取的“真相源”。OpenSpec 这个词最近被讨论得越来越多它本质上指向的是一类实践把软件系统的规格用结构化、可版本化、可校验的方式描述出来并让它成为研发流程里真正被消费的产物而不是写完就归档的文档。你可以把它理解成“给规格本身做工程化”——就像当年我们用代码取代手工部署一样OpenSpec 想用结构化的规格取代散落各处的口头约定和一次性文档。我第一次认真接触这个概念是因为一个前后端联调反复返工的项目。当时后端改了字段名没同步前端照着旧文档写测试照着更旧的用例测三方各说各话。后来我们尝试把接口规格抽出来做成一份可校验的定义任何一方改动都要先改这份定义CI 里跑一遍校验不一致直接挂掉。那一版之后联调返工率肉眼可见地下降。这就是 OpenSpec 思路的朴素价值让规格成为流程里的“硬约束”而不是“参考建议”。这篇文章适合几类人看正在被需求碎片化折磨的技术负责人、想推动团队规范化但不知道从哪下手的一线工程师、对“规格驱动开发”感兴趣但没实操过的同学以及单纯想搞清楚 OpenSpec 到底是不是又一个概念炒作的人。我会从它背后的核心逻辑讲起拆到具体怎么落地、工具怎么选、坑在哪尽量让你看完能直接在自己项目里试一把。需要先说明一点OpenSpec 目前并不是某一个官方标准组织定义的唯一规范它更像是一个正在形成的实践方向不同团队、不同工具链对它的理解会有差异。所以下面讲的内容一部分来自公开讨论中的共识一部分是我基于常见工程实践做的合理补全你在落地时要结合自己团队情况调整。2. 拆开 OpenSpec 的内核规格为什么必须“可执行”2.1 规格的三个层次描述、约束、契约很多人一听到“规格”就想到需求文档其实规格至少分三层理解这三层是理解 OpenSpec 的前提。第一层是描述性规格回答“这个系统是做什么的”。比如“用户可以下单并支付”这是给人看的天然有歧义不同人理解不一样。第二层是约束性规格回答“这个系统必须满足什么条件”。比如“订单金额必须大于 0”“支付超时时间不超过 15 分钟”这类规格开始有边界可以被检查。第三层是契约性规格回答“系统之间如何交互、字段长什么样、错误码是什么”。这一层最硬因为它直接决定了两个模块能不能对接上。传统文档大多停留在第一层偶尔到第二层第三层往往靠口头约定或者代码里“事实上的实现”。OpenSpec 的核心主张就是把第二层和第三层尽可能结构化、机器可读让它们能被校验、能被生成、能被追踪。描述性规格仍然需要但它不再是唯一真相源。这个分层很重要因为它决定了你落地时的优先级。如果你一上来就想把所有需求都结构化大概率会累死且没人配合。正确的做法是先从契约层切入——接口定义、数据结构、状态机这些地方收益最直接也最容易用工具校验。2.2 为什么“可执行”比“写得漂亮”重要一百倍我见过太多团队花大力气写了一份漂亮的规格文档然后就没有然后了。文档写完那一刻就是它过时的开始因为代码在动、需求在变而文档没人维护。这不是态度问题是机制问题没有被流程消费的产物注定会腐烂。OpenSpec 思路里最关键的一个转变是让规格“可执行”。什么叫可执行就是它能被工具读取、被校验、被用来生成代码或测试、被 CI 拦截。举几个具体形态接口规格用 OpenAPI 或类似格式描述CI 里校验实现和定义是否一致数据模型用 Schema 描述数据库迁移和代码模型都从它生成状态机用结构化定义描述测试用例从状态迁移路径自动生成业务规则用可判定的表达式描述单元测试直接引用这些规则。一旦规格进入 CI它就从“文档”变成了“代码的一部分”。改规格和改代码一样要走评审、要跑流水线这时候它才真正有了生命力。这也是为什么我一直强调OpenSpec 不是写作规范是工程实践。2.3 一个反直觉的结论规格越“少”越好新手容易犯的错是把规格写得越全越好恨不得把每个字段的中文含义、每个按钮的点击效果都写进去。但实操下来你会发现规格的维护成本随规模非线性上升写太多反而没人看、没人改、最后整体失效。我的经验是只把那些“一旦不一致就会出问题”的东西结构化。接口字段、枚举值、必填校验、状态流转、错误码这些必须硬约束至于“这个页面长什么样”“文案怎么措辞”交给设计稿和产品文档就行别硬塞进规格里。OpenSpec 的落地原则之一是最小可执行集先覆盖最容易出错的 20%拿到收益后再逐步扩展。3. 落地路径从零开始把规格接进研发流程3.1 第一步不是选工具是找“痛点场景”很多人一上来就问“用什么工具”这是本末倒置。工具是最后一步第一步是找到团队里最痛的那个场景。常见的高价值切入点有这么几个痛点场景典型症状适合的规格形态前后端接口对不齐字段名、类型、必填项反复扯皮接口契约OpenAPI 类数据库和代码模型不一致迁移脚本和实体类各写各的数据模型 Schema状态流转出 bug订单状态出现非法跳转状态机定义业务规则散落各处同一个折扣规则三处实现不一样规则表达式测试用例覆盖不全边界条件靠人想从规格生成用例选一个你团队当下最痛的先做透。不要贪多一个场景跑通闭环比五个场景都半途而废强得多。我一般建议从接口契约切入因为前后端联调是绝大多数团队的共同痛点收益立竿见影而且工具生态最成熟。3.2 规格的“单一真相源”怎么建选定场景后核心问题是这份规格放在哪、谁来维护、怎么保证它和代码同步。我的做法是把规格文件放进代码仓库和代码同源管理。比如在项目根目录建一个specs/目录接口定义放specs/api/数据模型放specs/models/。这样做有几个好处规格的变更走和代码一样的 PR 流程有评审、有历史、可回滚CI 能直接读到它新人 clone 下来就能看到全貌。维护责任要明确。接口规格通常由后端主导、前端参与评审数据模型由后端或 DBA 主导业务规则由产品和技术共同确认。关键是不能没有 owner否则又变成没人管的文档。我们当时的做法是每个规格文件头部标注 ownerPR 里必须 owner approve 才能合并。提示规格文件和代码放在同一个仓库但不要和业务代码混在一个目录里。清晰的目录边界能让工具配置和权限管理都简单很多。3.3 让 CI 成为规格的“守门人”规格进了仓库下一步就是让 CI 校验它。这是 OpenSpec 从“文档”变成“约束”的关键一步。具体校验什么取决于你的规格形态接口规格校验实现代码是否覆盖了所有定义的接口、字段类型是否匹配、必填项是否被处理数据模型校验迁移脚本和 Schema 是否一致、实体类字段是否对齐状态机校验代码里是否存在规格未定义的迁移路径规则表达式校验实现逻辑和规则定义是否等价。校验失败就阻断合并。一开始团队可能会觉得“太严了”但正是这种严格让规格有了权威性。我踩过的坑是校验规则不要一次上太猛先做“定义存在性校验”比如接口是否都实现了再做“语义一致性校验”比如字段类型是否匹配循序渐进给团队适应期。3.4 从规格反向生成产物省下的都是真金白银规格可执行之后一个巨大的红利是反向生成。同一份接口规格可以生成前端调用 SDK 或类型定义后端接口骨架代码接口测试用例接口文档站点Mock 服务。这意味着什么意味着你写一遍规格多个环节自动受益而且天然一致。我们当时用接口规格自动生成前端 TypeScript 类型和后端 Controller 骨架联调时字段对不上的问题基本消失了。数据模型规格则用来生成迁移脚本和实体类DBA 和开发不再各写各的。反向生成的价值不只是省时间更重要的是消除人为转录错误。人手工把规格翻译成代码一定会出错工具翻译只要规格对产物就对。这是 OpenSpec 思路里投入产出比最高的部分。4. 工具选型与实操别被“全家桶”绑架4.1 工具选型的三个判断维度市面上和 OpenSpec 相关的工具很多选型时我一般看三个维度第一是否支持你选定的规格形态。如果你主攻接口契约那 OpenAPI 生态的工具是首选如果主攻数据模型JSON Schema 或类似方案更合适。不要为了用一个“全能工具”去迁就它支持的形态。第二是否能无缝接入现有 CI。工具再好如果接入 CI 很麻烦落地就会打折。优先选有命令行接口、能输出标准退出码、配置简单的工具。第三社区活跃度和可迁移性。规格文件最好是开放格式不要被某个厂商的私有格式锁死。今天用的工具明天可能换但规格文件应该能跟着你走。4.2 一个最小可用的落地配置假设你从接口契约切入用 OpenAPI 作为规格格式一个最小可用的落地配置大概是这样# 目录结构 project/ specs/ api/ openapi.yaml # 接口规格单一真相源 src/ ... .ci/ validate-spec.sh # CI 校验脚本CI 脚本的核心逻辑#!/bin/bash set -e # 1. 校验规格文件本身是否合法 openapi-generator validate -i specs/api/openapi.yaml # 2. 校验实现是否覆盖规格定义 # 这里用具体工具比如校验路由和定义是否一致 node scripts/check-routes.js specs/api/openapi.yaml src/routes/ # 3. 生成前端类型可选也可在构建时生成 openapi-generator generate -i specs/api/openapi.yaml -g typescript-fetch -o src/generated/api echo 规格校验通过这个配置不复杂但已经能拦住大部分“规格和实现不一致”的问题。关键是先跑起来再优化不要一开始就追求完美。4.3 规格文件的组织方式单文件还是拆分一个实操中经常纠结的问题规格是放一个大文件还是按模块拆成多个文件。我的经验是项目初期、接口少单文件更简单一眼看全接口多了之后按业务域拆分成多个文件用$ref互相引用公共的数据结构比如分页、错误响应抽成独立文件多处复用。拆分的边界建议和团队的业务域划分对齐这样每个域一个规格文件owner 清晰评审范围也清晰。不要按技术层次拆比如所有 request 一个文件、所有 response 一个文件那样反而难维护。注意拆分之后要有一个“入口文件”把所有片段组装起来工具和文档站点都从这个入口读避免出现“改了片段但入口没更新”的情况。4.4 版本管理规格的兼容性比代码更敏感规格一旦被多方消费它的兼容性就变得极其敏感。接口字段删一个、改个类型可能同时影响前端、测试、文档、Mock。所以规格的版本管理要比代码更谨慎。我的做法是规格的破坏性变更必须显式标记并走单独的评审。比如在规格文件里维护一个变更日志破坏性变更单独列出评审时重点看影响面。同时规格的版本号要和 API 版本号对齐前端消费时明确知道自己对接的是哪个版本。另外规格的废弃字段不要直接删先标记deprecated给消费方迁移时间等确认没人用了再删。这个习惯能避免很多“上线才发现前端还在用旧字段”的事故。5. 踩坑实录那些让我返工三次的教训5.1 坑一规格和代码“双写”最后两边都不对最早我们犯的错是规格文件写一份代码里再写一份靠人保证一致。结果就是规格改了代码没改或者代码改了规格忘了更新两边逐渐漂移最后谁都不信规格。根因是没有建立“单一真相源”。修复方案是明确规格是源代码里和规格重复的部分要么生成、要么校验绝不允许手工双写。比如接口的字段定义只在规格里写代码里的类型从规格生成如果某些地方必须手写就加校验确保和规格一致。这个坑的教训是任何需要人手工同步两份数据的设计长期一定会失败。要么合并成一份要么用工具自动同步。5.2 坑二校验规则太严团队直接绕过第二个坑是矫枉过正。我们一开始把校验规则设得很严连注释格式、字段顺序都要管结果就是大家觉得“这玩意儿太烦了”开始想办法绕过——比如把校验脚本注释掉、或者在 PR 里强行合并。根因是没区分“必须一致”和“最好一致”。修复方案是把校验分成阻断性和提示性两类影响功能一致性的字段类型、必填项、状态流转设为阻断风格类的命名、注释、顺序设为提示不阻断合并。这样既守住了底线又不至于让人反感。这个坑让我明白规范的推行是渐进式的一上来就追求完美会适得其反。先让大家尝到甜头再逐步加严。5.3 坑三规格写得太细维护成本爆炸第三个坑是规格粒度过细。我们曾经把每个字段的中文含义、每个错误码的文案都写进规格结果规格文件膨胀到几千行改一个小需求要动好几处规格维护成本高到没人愿意碰。根因是没分清“规格”和“文档”的边界。规格应该只包含影响系统行为一致性的内容文案、说明、示例这些属于文档应该从规格生成而不是写进规格。修复方案是把规格瘦身只留结构化的核心定义说明性内容放到单独的文档里从规格引用。这个坑的教训是规格的每一行都要有存在的理由如果某行内容不会影响任何校验或生成那它就不该在规格里。5.4 坑四没有 owner规格变成“公共荒地”最后一个坑是责任不清。规格文件建起来之后谁都能改但没人负责。结果就是有人随手改了个字段没通知任何人下游炸了才发现。根因是缺少 ownership。修复方案是每个规格文件明确 ownerPR 必须 owner 评审。同时建立变更通知机制规格变更自动通知下游消费方。这个机制看起来麻烦但比起事故后的排查成本这点麻烦完全值得。6. 把 OpenSpec 用出长期价值几个进阶思路6.1 规格驱动测试让用例从规格长出来规格可执行之后一个自然的延伸是从规格生成测试用例。接口规格可以生成接口测试的骨架数据模型规格可以生成边界值测试状态机规格可以生成状态迁移的覆盖用例。这样测试的覆盖度直接和规格的完整度挂钩规格写得越全测试覆盖越广。实操上我们当时用接口规格生成了基础的请求响应测试然后人工补充业务逻辑相关的用例。生成的部分保证了“接口层面不出错”人工的部分聚焦“业务逻辑对不对”分工清晰效率提升明显。6.2 规格作为沟通媒介减少会议增加共识规格结构化之后一个意外的好处是会议变少了。以前前后端对接口要开会对现在直接看规格文件有疑问在 PR 里评论。规格成了沟通的媒介讨论围绕具体定义展开而不是各说各话。这背后的逻辑是结构化的东西比自然语言更容易达成共识。自然语言有歧义结构化定义没有。当大家对着同一份规格讨论时分歧会快速收敛到具体点上。6.3 规格的度量怎么知道落地效果好不好落地一段时间后怎么判断 OpenSpec 实践有没有效果我一般看几个指标因规格不一致导致的返工次数应该下降联调阶段发现的字段问题数量应该下降规格变更到下游感知的平均时间应该缩短新人上手理解系统的时间应该缩短。这些指标不需要很精确趋势对了就说明方向对了。如果某个指标一直没改善就要回头看看是不是规格覆盖的场景不对或者校验没起作用。6.4 从小团队到大团队规格的扩展性小团队里规格可能就是一个文件、一个人维护。团队大了之后规格要能扩展按业务域拆分、按团队划分 owner、建立跨团队的规格评审机制。关键是规格的组织结构要和团队的组织结构对齐否则会出现“改一个规格要协调五个团队”的窘境。我见过做得比较好的团队是把规格当成内部“接口协议”来管理有专门的接口人负责跨团队协调规格变更走类似 RFC 的流程。这套机制听起来重但对于多团队协作的大型系统它能避免大量扯皮。7. 我个人的一点体会折腾 OpenSpec 这套东西几年下来最大的感受是它的难点从来不在技术而在习惯。工具再好如果团队没有“规格先行”的意识最后还是会退回到“代码即规格”的老路。反过来只要团队认这个理哪怕一开始只用最简单的校验脚本也能慢慢滚起来。另一个体会是不要追求一步到位。我见过太多团队想一次性把所有规格都结构化结果铺得太大半年后一地鸡毛。正确的姿势是选一个痛点跑通闭环拿到收益再复制到下一个场景。规格化是个长期工程慢就是快。最后分享一个我一直在用的小技巧每次规格变更的 PR 描述里强制写清楚“这次变更影响哪些下游”。这个习惯逼着改动者去想影响面也方便评审者判断风险。看起来是个小事但坚持下来因为规格变更导致的事故会少很多。