☰
规格驱动开发实战:OpenSpec如何构建可验证的行为契约
2026/11/4 3:54:40 网站建设 项目流程

今天想聊聊OpenSpec这套规格驱动开发的方法。先别急着划走,如果你已经厌倦了"需求文档写一套、代码实现另一套、最后测试又测出第三套"的循环,那这篇文章大概率对你有用。我最早接触规格驱动开发是被一个遗留系统折磨到崩溃之后——那个系统的所谓"需求"散落在四五个人的聊天记录里,每次改动都像考古。后来我在一个新项目里完整跑通了OpenSpec的工作流,效果确实超出预期。

简单说,OpenSpec把"要做什么"压缩成一份足够精确且可验证的规格文档,代码只是规格的翻译物。这套方法特别适合微服务改造、核心模块重构、以及需要多团队协作的中大型项目。如果你是独立开发者,它也能帮你戒掉"边写边改、越改越乱"的毛病,看清每个改动背后的真实成本。但要说清楚这套东西,光讲概念没用,得把它的结构、写法、踩坑点、以及和AI辅助开发的配合方式都捋一遍。

1. 整体设计与思路拆解

1.1 规格驱动开发到底解决了什么问题

规格驱动开发的核心本质,是把软件的"行为契约"从代码层面抽离出来,单独作为一等公民进行管理。我们日常见到的大多数项目,需求传递链路长且损耗高:产品经理说一版,技术负责人理解一版,主力开发再加工一版,到具体写代码的手里往往已经变形。OpenSpec的思路是,让这份"契约"变得足够精准,精准到每个功能点都有明确的验收边界。

它和传统需求文档最大的区别在于三点。

第一,规格必须聚焦行为,而不是罗列界面。传统PRD大篇幅讲布局、颜色、交互动效,但OpenSpec的规格文档只关心"输入什么、输出什么、什么时候出错、出错后状态如何变化"。这点非常关键。比如"用户点击登录按钮"这句话本身不具备可验证性,但"用户提交格式合法的手机号和密码,接口返回200且下发token,过期时间为7天"就是一段可以自动测试的规格。

第二,规格文档天然是分层的。OpenSpec把大项目拆成"变更提案",每个变更提案又拆成多个独立的规格文件,每个规格文件只描述一个功能点的行为。这种拆分方式使得审查成本极低——别人看你的规格,不需要理解整个系统的上下文,只需要看"这个功能点本身定义得是否合理"。

第三,任务清单直接来源于规格。规格文档里每一条可验证的行为,都可以映射成一条开发任务。这避免了"任务拆解靠拍脑袋"的尴尬处境,开发排期有了估算基础,进度追踪也变成了对规格覆盖率的追踪。

我用一个类比来帮助理解:规格文档之于代码,相当于菜单之于后厨。菜单上写明"辣子鸡:鸡肉丁配干辣椒、花生米、花椒,口味麻辣",后厨照着做,客人照着验收。如果菜单含糊其辞写"好吃的鸡",后厨自由发挥,客人吃到的可能就是糖醋鸡块,那责任在菜单,不在厨师。

1.2 OpenSpec工作流的核心框架

OpenSpec的完整工作流可以拆解为四个阶段:提案、规格化、实现、验证。这个流程和我们熟悉的需求评审、开发、测试流程有交集,但关键差异在于中间夹了一个"规格化"的强制环节。

提案阶段产出的是变更提案,描述"为什么做这件事、希望达成什么效果、会影响哪些现有功能"。提案不需要太细,它是一个对齐工具,让产品、开发、测试在动手前先确认方向。

规格化阶段是核心。你要把提案拆成一项项可验证的行为描述,形成规格文件。这个阶段最考验抽象能力,你既要深入理解业务,又要抵抗住"直接写代码"的诱惑。规格文件写得好,后面所有阶段都是顺水推舟。

实现阶段反而最轻松,因为规格已经把行为边界画好了。开发的任务是照着规格写代码,测试的任务是照着规格写用例,代码评审的标准也从"风格是否统一"变成了"行为是否符合规格"。

验证阶段则是把规格转成自动化测试断言。这一步实现了"规格不腐化"的关键闭环。传统项目里规格文档最悲哀的命运是写完就躺在wiki里生灰,但OpenSpec的规格可以自动验证,任何对行为的修改如果导致规格断言失败,问题能在代码评审阶段就暴露,而不是上线后由用户帮我们发现。

这四个阶段不是瀑布式的,更像一个旋钮。你可以对不同的变更灵活调整规格化的深度,小改动简单描述即可,大重构则必须细到每个断言的粒度。

2. OpenSpec规格结构与实操要点

2.1 规格文件系统详解

实际落地OpenSpec时,大家最关心的第一个问题往往是"文件到底怎么组织"。我推荐的结构是每个变更提案独立一个目录,目录下包含proposal.md、specs/子目录和tasks.md三个部分。

proposal.md是提案的入口,开门见山交代背景与动机。它不写具体行为,只写"现状有什么痛点、未来要变成什么样子、涉及哪些范围"。好的proposal应该在五分钟内读完,读完后读者能清楚判断"这个改动值不值得做、要不要调整优先级"。

specs/子目录存放规格文件,一个文件只聚焦一个行为维度。举个例子,如果我们要做"用户搜索历史记录"功能,可以拆成search_history_spec.md、clear_history_spec.md、search_suggestion_spec.md三个文件,分别描述记录的存取行为、清空行为、联想推荐行为。这种拆分不是越多越好,而是以"独立可验证"为准——每条规格能独立测试、独立评审、独立排期。

tasks.md是任务清单,从规格中推导而来。它不需要描述怎么做,而是描述"完成哪个规格、产生什么可检查的结果"。初看会觉得任务清单和规格内容重复,但这个重复是刻意设计的:规格是"契约",任务是"施工计划",前者重描述,后者重跟踪。

存放位置建议放在仓库根目录的openspec/文件夹下,和代码目录并级。千万不要把它塞进docs/里,那样容易在心理上和代码"隔离",没人愿意主动走进一个和自己工作无关的目录。

2.2 编写高质量规格的黄金法则

规格文件的质量直接决定这套方法的价值上限。写了这么多份规格文件,我总结了三条黄金法则。

第一条,描述行为,杜绝臆测。规格里的每一句话都必须指向一个可观测的结果。比如"系统应该校验用户输入"就是一句典型的废话,因为没有定义"校验失败"后的外部表现。改成"当手机号不满足11位数字时,接口返回错误码PARAM_INVALID,且状态码为400",这句才能进规格。写规格的时候把自己想象成一个严厉的测试工程师——你会怎么验证这句话是对是错?如果没法设计验证方案,这句话就不合格。

第二条,边界条件比正常路径更重要。多数人写规格时天然关注"正常流程跑通",但系统腐烂往往始于异常路径没人管。密码错误、请求超时、数据为空、权限不足、重复提交,这些场景必须和主流程一样写进规格里。一条经验准则是:每条正常行为的规格旁边,至少要配套两条异常行为的规格。

第三条,可量化的指标必须量化。涉及性能、容量、时间的要求,不要写"响应要快",要写"95%请求响应时间低于200ms"。涉及到列表返回,不要写"返回所有数据",要写清楚分页参数上下限。规格写得模糊,实现的时候就会争论;规格写得精确,争论发生在差异出现之前,这才是我们想要的结果。

这两条是我在实践中反复吃亏后总结的。早期我写的规格太像"伪代码描述",本质上只是把实现思路换了个形式又写了一遍,既限制了实现灵活性,又没提供可验证性。后来意识到,规格的抽象层级应该停留在"行为",而不是"实现"。规定"系统返回按时间倒序排列的历史记录",而不是"用列表的reverse操作完成排序",这才是规格文件该有的样子。

2.3 规格文件的"主菜":场景与验收标准怎么搭

一份完整的规格文件,建议按照"概述、前置条件、场景列表、验收标准"四段来组织。这里重点说场景与验收标准的设计。

场景部分我习惯使用"Given-When-Then"三段式描述。Given描述初始状态,When描述操作触发,Then描述预期的可观测结果。它的好处是逼着你把隐藏假设显式化。比如描述搜索功能,不要写"用户在搜索框输入关键词",这只是操作,没有状态基础。改成"Given用户已登录且有历史搜索记录,When用户在搜索框输入'OpenSpec'关键词,Then返回结果按最近搜索时间排序,且每条结果附带完整搜索时间"。这个描述里就有了状态、操作、响应三层结构。

验收标准部分需要做到"一读就懂,一测就过"。我的标准是:找个没参与这个功能开发的同事,把验收标准发给他,他能直接写出测试用例而不需要追问任何细节,这份验收标准才算合格。

在规格版本管理上,也有一条最重要的操作建议:文件命名以含义命名,不要在文件名里加日期或版本号。版本追踪交给git,文件名保持稳定,这样才能保证引用关系的可靠性。你在任务清单里引用了search_history_spec.md,三个月后这个文件还叫这个名字。如果文件名带了v2字样,引用就乱套了。

3. 实操过程与核心环节实现

3.1 从零到一:一个"搜索历史记录"功能的完整演示

理论说太多容易飘,我拿一个具体功能走完OpenSpec的完整流程。假设我们正在维护一个内容管理后台,需要新增"搜索历史记录"功能。

第一步,创建变更提案目录。目录结构命名为openspec/changes/2025-04-search-history/,命名规约为"日期-英文短横线描述",简洁且按时间排序后清晰。proposal.md内容只需定位问题:运营人员每天在后台反复输入相同搜索词查找内容,期望系统自动记住最近搜索词,方便一键复用。涉及范围是"搜索模块"和"个人偏好设置模块",不涉及权限体系改动。

第二步,拆解规格文件。搜索历史这个功能从行为维度看,至少包含三个规格:record_search_spec.md描述"什么情况下搜索词会被记录";list_search_history_spec.md描述"历史记录如何展示,含分页和排序规则";clear_search_history_spec.md描述"用户如何清空历史"。其中record_search_spec.md是最容易掉坑的,因为这里有个隐含的边界问题——用户从历史记录里点击搜索词再次发起搜索,这条搜索行为是否要再次记录?如果不定义清楚,实现者就会做出不同选择。

第三步,编写验收标准。这里我直接给出一个实际可用的典型验收标准集合,方便大家感受"验收标准"的颗粒度:

编号规格文件场景验收断言
A1record_search_spec用户主动输入并提交搜索词"关键词"被写入用户的历史记录列表,时间戳为当前时间
A2record_search_spec用户通过历史记录发起二次搜索该记录时间戳刷新,但不新增重复记录
A3record_search_spec超长关键词(>50字符)直接截断前50字符,记录写入
A4list_search_history_spec用户打开历史记录面板返回按时间倒序排列的前20条记录
A5list_search_history_spec历史记录池为空界面显示空态文案"暂无搜索历史",不报错
A6clear_search_history_spec用户确认清空历史该用户的历史记录全部删除,再次打开列表为空

这些验收标准直接喂给AI辅助编程工具,它生成的代码基本能一次命中需求;喂给测试工程师,他可以直接转换成自动化用例。这比写几十页PRD然后让前后端各自理解要高效得多。

3.2 任务清单拆解与执行节奏

规格定了,任务清单就好拆了。按"每条验收标准对应一个或多个开发任务"的原则拆解,tasks.md内容大致是:

  • [ ] 实现SearchRecordService记录逻辑,支持关键词标准化处理(长度截断、首尾空格去除)
  • [ ] 实现搜索记录仓储层,key为用户ID+搜索词,value为最后搜索时间
  • [ ] 实现历史记录查询接口,支持分页参数page与size,默认按lastSearchTime倒序排序
  • [ ] 实现清空历史接口,操作前校验用户身份并二次确认
  • [ ] 补充A1-A6对应测试用例,完成自测

这种任务拆解方式有一个显著好处:开发过程中每完成一个任务,都可以和验收标准对照,确认"这个任务对应哪个验收点"。任务清单的完成比例,本质上就是功能完成度的量化指标,而不是凭感觉说"快好了"。

执行节奏方面,我强烈建议规格文件提交和代码实现提交严格分离,不要混在同一个commit里。规格变更走一次review,代码实现走一次review,前者确认"我们要做的是对的",后者确认"我们把对的做出来了"。如果混在一起,reviewer的注意力很难兼顾两个维度。实操中我会先在本地把规格文件写好,提交并推送分支,在分支描述里注明"spec only,未开始实现",然后等人review通过后,再在这个分支上继续堆代码。

3.3 与AI辅助编程的高效协作姿势

现在的开发环境里,AI辅助编程已经是绕不开的话题。很多团队的困惑是"AI生成的代码质量不稳定"。基于OpenSpec的工作流,AI辅助的质量反而显著提升。

关键在于,AI生成代码的质量上限取决于输入描述的精确度。OpenSpec的规格文件天然就是一份出色的AI提示词。我在实际编码时,直接把规格文件的验收标准复制进AI对话框,附一句"请按照以上验收标准实现对应接口和逻辑,不要增加未提及的附加功能"。AI输出的代码和规格期望的吻合度极高。

这里有个技巧值得分享:AI编程工具通常有"当前文件上下文"的限制,建议把相关的规格文件内容也作为上下文资产放进去,用注释形式把规格标注在代码文件顶部,这样AI在生成代码时能感知到约束边界。我自己会在文件头部用一段注释:# Spec: openspec/changes/xxx/specs/record_search_spec.md,既是团队协作的指引,也方便AI工具自动读取。

当然规格也不是万能的。AI依然会在一些隐含边界条件上翻车,但只要验收标准写清楚了,测试阶段能快速抓到问题。相比传统模式"AI生成了代码但缺乏评判依据",OpenSpec给了一个确定性答复:行为符合规格就是对的,不符合就是错的,没有中间态。

4. 常见问题与排查技巧实录

4.1 规格驱动开发失败的模式图鉴

没有任何方法论是银弹。我见过团队引入OpenSpec后一两个月就放弃的,原因五花八门,但核心模式就那么几种。

第一种叫"规格膨胀症"。事无巨细都写进规格,一个30行的功能写出了200行的规格描述,光写规格就花了三天。这其实违背了规格的本意——规格聚焦风险高、协作复杂的边界,不是流水账。我的判断标准是"控制规模":一个变更提案的规格文件数量控制在1到5个,如果多于5个,说明这个变更拆得太粗,应该拆成多个提案分阶段执行。

第二种叫"规格与实现脱节"。规格写好了,代码实现时没照着做,也没有任何自动化检查兜底。这比没有规格更糟,因为它制造了虚假的安全感。要避免这个问题,必须在团队层面把"规格验收标准覆盖率"当作一个硬指标,要求提测前必须先自查规格覆盖情况。没有对应规格的实现,再好也要打回去补充规格。

第三种叫"规格冻结论"。认为规格一旦写成文字就不能再动,这是对规格驱动开发的最大误解。规格是活文档,随着业务理解和架构约束的深入,它需要持续迭代。关键是迭代过程要有记录——修改了哪个验收标准、为什么修改、影响了哪些任务,这些过程本身就是项目历史的重要组成部分。我见过最健康的团队,规格文档的改动频率和代码改动频率几乎持平。

第四种叫"忽略人的协作成本"。规格驱动开发本质上是把沟通成本前移,击鼓传花的"花"变成了规格文档。有些团队期望规格写完就能大幅降低沟通成本,这个期望要打折扣。规格写得好,沟通次数确实会减少,但关键的几次沟通反而更重要——提案评审会、规格评审会的质量决定了后续一切。规格文档是工具,不是甩手掌柜。

4.2 排障记录:规格没问题,但测试就是过不了

实际排障中经常会遇到一个迷惑场景:规格写得很清楚,代码逻辑看起来也对,但自动化测试就是过不了。这时候问题多半出在"隐性规格"上。

举一个真实案例。有个团队实现了分页查询接口,规格写"每页返回size条记录,默认size=20,最大size=100"。代码在本地自测时一切正常,但CI环境就是报错。排查后发现测试环境的共享数据库里,有一个脏数据记录的用户ID为空,导致按用户查询时返回了不该返回的记录。问题根源不在规格描述本身,而在于规格里没有定义"数据异常时的行为",而测试环境恰好暴露了这个空档。

这类问题的最佳解法是:在规格里增加一条"防御性验收标准",比如"当数据出现异常缺失字段时,接口应该跳过该记录并继续返回正常记录,或直接抛出指定错误码,不允许返回部分畸形数据"。这类规格的价值在开发时容易被忽视,在生产故障出现前也不会被修复。

另外,强烈建议在设计自动化测试框架时,让每个规格验收标准对应至少一个独立的测试用例,并且测试用例的名称直接引用验收标准编号。这样做调试成本极低——测试报错时你不需要猜"这个用例测什么",看一眼测试名称就知道对应哪条规格,顺着定位是规格写得不对还是实现写得不对,效率天差地别。

4.3 一个"最不合理的"成功案例:单体老系统也用上了

前面聊的大多应用场景是新项目、微服务团队,但你可能会问我手里那些技术债拉满的单体系统怎么办。我必须说,单体老系统恰恰是规格驱动开发收益最大的地方。

我做过一个改造项目,一个十年历史的PHP订单系统,核心的订单状态流转逻辑散落在各种Controller和Service里,加一个字段都要排查半天会不会影响其他流程。我们用OpenSpec重新梳理,把订单状态机相关的行为全部规格化。"已支付订单取消后库存回滚"、"退款中的订单禁止再次取消"这些散落在代码各处的规则,先整理进规格,再对照实现逐步修正。

这个过程不轻松,总共梳理出两百多条行为规格,耗时将近三周。但这三周的价值如此之大,改造期间线上问题暴增的现象几乎消失,因为很多历史遗留逻辑的错误在规格化阶段就被发现了——实现和期望的行为之间早有偏差,只是以前没有文档可以对照。老系统的"不可靠"很大程度来自行为的不可知性,规格化把行为变成了知识,知识带来了控制感。

5. 团队落地与习惯养成

5.1 团队落地OpenSpec的路径建议

任何方法论落地,最大的挑战永远是习惯改变。引入OpenSpec时团队成员的最大抗拒点通常是"又要多写文档"。实际落地经验告诉我,别指望一步到位,应该分阶段推进。

第一阶段,挑一个中等粒度的真实需求做试点,从提案到规格到实现全程走一遍,让团队体验"规格写清楚后开发的顺滑感"。这个阶段最容易让人转变态度——只有切身体会到"写得越细、改得越少",团队才会真正接受规格驱动开发的理念。

第二阶段,建立规格评审机制。评审不需要长,聚焦在"验收标准是否完备、是否可验证"两个维度上。我发现很多团队对旧式需求评审深恶痛绝,但对规格评审的接受度很高,因为讨论的是"怎么算做完"这个硬核问题,而不是"我觉得按钮应该放左边还是右边"。

第三阶段,把规格覆盖率纳入项目健康度指标。看板上的任务不仅要标记"开发完成",还要标记"对应的验收标准已通过"。这两个标签之间的差值,就是项目里隐藏的风险。我在实际项目中观察到的规律很直接:规格覆盖率低的任务,返工概率和线上Bug率呈明显上升趋势。

5.2 规矩、工具与文档的三角支撑

落地过程中还需要三个支点的配合:规矩、工具、文档。规矩是指团队约定,比如"没有规格变更不允许动代码""验收标准未通过不允许标记完成"。这些规则一开始可能是隐性约定,但我建议用一份简短的CONTRIBUTING文档固定下来,放在仓库根部,让新成员入职时就能看到。

工具方面,我建议优先保证规格文件和git流程的整合。至少需要配置一套CI检查——当规格文件发生变化时自动触发评审提醒,或者当代码提交信息里没有引用对应的变更提案编号时自动警告。很多团队用OpenSpec失败,不是工具不好,而是流程没有形成闭环,规格写完了就等于是写完了。

最后是文档本身。这里想强调一个反常识的建议:不要为OpenSpec单独建"方法论文档"仓库。它应该和代码仓库保持同一个生命周期,同一次提交,同一套评审流程。方法论一旦变成了另一个文档仓库,就会失去和代码的同步性,最终沦为幽灵文档。让规格和代码在一个仓库里共生腐烂、共用仪表盘,才算真正吸收了这套理念。

5.3 一个可持续的自我迭代机制

规格驱动开发能不能持续产生价值,最终取决于是否有"规格的规格"——也就是元规格。一个团队需要定期坐下来审查自己的规格文档体系:我们的规格文件是否过大?验收标准是否过于模糊?场景描述是否覆盖了足够的边界条件?任务清单是否总能从规格直接推导?

这个审查建议每季度做一次。不一定需要消耗大量时间,找一个半天看完所有变更提案的文件列表,统计规格文件数量和验收标准数量,标记出最长、最模糊的几份,重点看看问题出在哪。我做了几个季度后发现,一开始规格写得又臭又长,后来逐渐精炼到"节点明确、边界清晰、标准可验",团队每个成员的规格写作水平都有了肉眼可见的提升。

这个过程本质上就是团队的业务建模能力成长史。规格驱动开发的终点不是"每行代码都有规格",而是团队每个人都养成了"先定义行为再动手实现"的思维惯性。到那时候,OpenSpec是不是还叫这个名字已经不重要了,因为它已经内化成为团队的工作方式。

6. 关于工具链的补充与建议

6.1 规格文件的管理工具选择

讨论一个可能被忽略但实际高频率使用的问题:规格文件用什么工具来编辑和维护。理论上纯Markdown就够用,但实际项目里大家的需求不止于"能写"。

我建议直接把规格文件放在IDE里和代码一起编辑,这样切换到工作上下文时不需要频繁换工具。配合IDE的Markdown预览插件,写规格的体验能接近更专业的建模工具。

如果你有整理思维导图的习惯,可以先用思维导图工具梳理行为维度和边界场景,确认结构合理后再迁移到规格文件。但注意最终产物一定是Markdown文件,因为只有文本文件才能纳入git管理和自动化检查流程。任何只能在线查看的文档工具都会在这套工作流里成为断层。

6.2 规格自动化检查的轻量方案

为了让规格验收标准"可编程化",我建议在项目里维护一个简单的测试映射表,用YAML格式记录验收标准编号与自动化测试用例的映射关系。

specs: - id: A1 test: test_search_history_record_success.py::test_record_valid_keyword - id: A2 test: test_search_history_record_success.py::test_record_existing_keyword_update_time - id: A3 test: test_search_history_record_success.py::test_keyword_truncated

这份映射表的价值在于,CI跑完测试后可以自动生成规格覆盖率报告。覆盖率低时说明实现尚未完整落地或测试存在盲区。不需要引入重的需求管理平台,一个YAML文件加一段十几行的小脚本就能跑起来。我在多个项目里验证过,这套轻量方案已经能拦住绝大部分"规格和实现脱节"的问题。

针对自动化生成规格的第一版,可以借助AI帮助起草,但审查职责必须由人承担。AI起草的规格往往能覆盖主流路径,却容易忽略系统特有约束,比如权限模型、部署拓扑、历史脏数据。建议把AI生成的规格当作"初稿素材",最终版本一定是人工梳理确认的产物。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询