
如果你也曾在团队里被“OpenSpec”这个词搞得有点懵别慌我跟你的感受一样。最初在技术方案评审会上看到它的时候我的第一反应是这不就是 Swagger 换了件马甲吗等我真的翻文档、写示例、跑检查之后才意识到OpenSpec 背后藏着的根本不是某个单一工具而是一整套关于“代码、接口、文档、流程怎么协作”的思考方式。这篇文章不是官方教程的复读也不是架构师的宏大叙事就是一个普通后端开发者踩了几个月的坑、翻了几十份文档之后对 OpenSpec 的理解和使用总结。适合正在写接口、被联调折磨、想引入代码检查又不知道从哪下手的开发者看也适合团队里负责推规范但总被大家无视的人参考。很多人把规范当成“束缚”我的体会恰好相反规范是团队协作的“契约”。接口文档写不清楚前端说后端不守约定提交信息乱写review 的人看不出这次改动到底干了啥代码风格不统一最受益的其实是后来接手的人。OpenSpec 这个概念真正进入我的工作流是从一个很具体的痛点开始的我接下来会把这些实践过程拆开来讲。1. 我理解的 OpenSpec从一句“把接口说清楚”开始1.1 OpenSpec 和 OpenAPI 到底什么关系先说结论在绝大多数日常开发语境里OpenSpec 指的就是 OpenAPI Specification也就是我们常挂在嘴边的 OpenAPI 规范它的前身是 Swagger Specification。你如果想在公司内部搜“OpenSpec”大概率搜到的是一个 YAML 文件里面描述了一堆路径、参数、响应体那就是 OpenAPI 3.0 格式的接口规范文件。但“OpenSpec”这个词也被很多团队拿来泛指“开放的研发规范”包括代码风格规范、Git 提交规范、接口设计规范、文档写作规范。我在实践过程中发现这两层含义并不冲突接口层用 OpenAPI 这种机器可读的规范流程层用文字性的规范文档它们合在一起才构成完整的一套 OpenSpec 实践。而我个人对 OpenSpec 最朴素的理解是把“经验”变成“约定”把“约定”变成“可执行的东西”。以前我们项目里的接口文档是 Word写的人和看的人两套理解后来迁移到 OpenAPI 格式前端直接拿 YAML 文件生成 TypeScript 的请求类型联调效率明显上了一个台阶。这就是 OpenSpec 带给普通开发者的价值它不要求你懂太多理论只需要你把接口描述清楚剩下的工具能帮你检查、生成、Mock。1.2 规范约束的不是代码是协作的边界我刚工作那会儿最反感的事情之一就是代码规范检查明明功能跑得好好的eslint 非要报一个“禁止使用 any”看着心烦。后来带我的老大说了一段话让我记到现在规范约束的不是代码本身而是人和人协作的边界。你写的代码不只是给机器执行的还是给下一个同事看的。一个函数用什么命名、一个接口报错用什么格式、一个提交信息怎么写分支这些看起来是“小事”的约定决定了一个团队能不能在互不干扰的情况下并行开发。OpenSpec 在这件事上最典型的就是响应体结构约定。我们后端团队以前每个接口的风格都不一样有人返回{code: 200, data: xxx, msg: ok}有人返回{success: true, result: xxx, message: ok}前端对接的时候每写一个接口就要看一次文档特别痛苦。后来我们在 OpenAPI 文件里统一定义了ApiResponse这个 schema所有接口都引用它前后端协作立刻清爽了很多。这件事让我真正理解了“规范即边界”的含义接口的入参和出参就是前后端合作的边界OpenSpec 就是把这个边界可视化、契约化。1.3 OpenSpec 在 AI 编码时代又多了一层身份最近半年 AI 辅助编程发展很快大家讨论的热度也很高这时候再提 OpenSpec又多了一层含义给 AI 看的“代码生成规范”。传统意义上的规范是给人写的需要人阅读、理解、遵守但在 AI 编码工作流里规范其实还承担了一个新职责——给大模型描述上下文和约束条件。同一个功能用自然语言直接问 AI和给它一份结构化的 OpenSpec 描述再让它写生成出来的代码质量差距非常大这一点我会在后面的章节专门展开。所以我的整体理解框架可以简单概括成三句话接口用 OpenAPI 描述清楚项目用规范文档约束边界AI 用结构化规范驱动产出。这三件事共享同一个底层思维——把隐性知识显性化、把口头约定文本化、把经验沉淀成可复用的资产这就是 OpenSpec 对普通开发者最大的意义。2. 接口层落地OpenAPI 规范是 RESTful 开发的“双向翻译机”2.1 为什么说 OpenAPI 是前后端之间的“合同”做后端的朋友应该都有这种经历接口写完了自己拿 Postman 测也没问题交给前端却总是对接不上。要么是字段类型对不上要么是嵌套结构跟前端预期不一致要么是错误码没有文档说明。这些问题的根源在于前后端之间的“合同”是口头约定的后端用 DTO 来表达前端用 TypeScript interface 来理解中间缺了一个对双方都公平的翻译层。OpenAPI 规范文件就是这个翻译层。它用统一的 YAML 或 JSON 格式描述接口的路径、请求参数、响应结构、认证方式、错误码后端写完接口后照着 OpenAPI 文件实现前端照着 OpenAPI 文件生成请求代码和类型定义只要这份文件是准的两边几乎不需要来回问“这个字段是啥意思”。我在实践里尝到的最大甜头就是有了 OpenAPI 文件之后接口联调前先跑一次契约检查格式对不对、字段缺不缺工具直接告诉你不用等代码跑起来才暴露问题。2.2 手写第一个 OpenAPI 3.0 文件的关键心得第一次手写 OpenAPI 文件时我踩了不少坑其中最典型的是不了解 3.0 版本和 2.0 版本的差异。Swagger 2.0 时代响应体的定义叫definitions3.0 改叫components/schemas参数定义也从in: body改成了requestBody配合content。如果你拿旧教程练手很容易照猫画虎写出一个校验不通过的文档。我这里给一个最精简但结构完整的最小示例新手可以直接抄openapi: 3.0.1 info: title: 订单服务 API version: 1.0.0 paths: /orders: get: summary: 获取订单列表 parameters: - name: status in: query required: false schema: type: string enum: [PENDING, PAID, SHIPPED] responses: 200: description: 操作成功 content: application/json: schema: type: object properties: items: type: array items: $ref: #/components/schemas/Order total: type: integer example: 100 components: schemas: Order: type: object required: - id - totalAmount properties: id: type: string format: uuid totalAmount: type: number format: double status: type: string enum: [PENDING, PAID, SHIPPED, CANCELLED]我说说几个容易忽略的细节。第一required字段一定要在schema里配合properties使用只写 properties 不写 required生成的 TS 类型所有字段都变成可选这会导致前端代码到处是空值判断。第二enum不要只写在文档描述里一定要写进 schema这样生成代码的时候枚举类型才会出现在前端里前端就能直接用常量来比较状态而不是靠猜。第三example字段不要偷懒不写它有两个作用一是给生成出来的 API 文档增加可读性二是给 Mock 工具提供默认值。这三个细节是我在三次联调事故之后才总结出来的。2.3 用规范驱动联调流程Mock 和类型生成光有一份 OpenAPI 文件还不够它得真正流动在开发流程里才有价值。我们团队的实践中最重要的一条是把 OpenAPI 文件放在一个独立的仓库管理作为契约基准。后端代码仓库和前端代码仓库都依赖这个仓库而不是互相复制文件。这样每次接口变更更新 OpenAPI 文件走一次 review两边拉最新版本改动点一目了然。这个方法虽然不是 OpenSpec 独有的但它就是 OpenSpec 思想的落地规范化管理变化让变化可控。Mock 功能也很值得展开。在没有后端代码的情况下前端可以直接用 OpenAPI 文件启动一个 Mock 服务。我们用的是 Prism它读 OpenAPI 3.0 文件后能自动 mock 出所有接口返回的数据结构和 example 一致。前端拿着这套 Mock 服务做页面联调后端继续开发真实接口两不耽误。最妙的是Mock 出来的字段结构和真实接口几乎零偏差因为两边共享同一份 schema 定义。这里我特别建议把example写得完整一些因为 Mock 数据的质量完全取决于 example 填得好不好。类型生成方面我现在的习惯是前端从 OpenAPI 文件用 openapi-typescript 生成api.d.ts后端在 Java 或 Go 里用 openapi-generator 生成服务端骨架或是在测试里做契约校验。有人可能会问这不是多此一举吗后端用自己的 DTO 不就行了我的回答是DTO 是内部实现OpenAPI 是对外契约内部怎么改都行但契约不能变。用工具把契约和实现绑定起来才能让规范真正发挥作用而不是变成一份落灰的文档。3. 流程层落地代码检查、提交规范、文档规范3.1 代码检查规范不是“把配置拷过来”就完事代码规范这一层我见过太多团队直接把知名团队的 eslint 配置拷进项目然后发现钩子跑不过、报错一堆大家怨声载道。真正的 OpenSpec 思维是先把团队的技术栈、历史包袱、人员的熟悉程度摸清楚再决定规则集。比如一个老项目里全是var声明的代码你突然上一条no-var的 error 规则全公司的提交都会崩这时候应该设成 warn分批提示而不是一步到位。我比较推荐的做法是遵循“三阶推进”。第一阶只加语法错误级别的规则比如未定义变量、重复声明、不可达代码第二阶加风格统一类规则比如缩进、引号、语句结尾分号第三阶加团队特殊约定比如禁止使用any如果是 TS 项目、禁止在循环里写console.log、禁止魔法数字散落业务代码。每一个阶段都给团队一到两周的过渡期过渡期内的违规计为 warning 而非 error。这套方法帮我们在三个项目里平稳落地没有引起反弹。还有一个关键经验是代码检查这种规范必须被集成到 CI 里而不是只靠本地 git hook。本地的 hook 可以跳过但 CI 是最后一道闸门。我们当时的配置是CI 里跑eslint和tsc --noEmit任何 error 都直接导致流水线失败warning 不阻塞但会输出在 MR 评论里。这样能最大程度减少“规范走形式”的情况。3.2 提交信息规范从“随手写”到“类型化”Git 提交信息也算 OpenSpec 的一部分在我的实践里算。因为提交信息是团队协作中最容易被忽视的显性文本之一它记录了“这次改动解决了什么问题”也是后续 git log、release notes、bug 定位的依据。我以前在公司代码库里看到过无数条提交信息光看文字你根本不知道这次改了什么写的是“修改bug”“提交代码”之类等于没有记录。后来我们把 commit message 规范成了 ConvcoConventional Commits约定式提交格式简短说就是type(scope): subject这种三段式结构。type 是本次提交的类型scope 是影响范围subject 是简要描述。下面是我常用的类型速查表类型用途示例feat新功能feat: 增加用户注册接口fix修复缺陷fix: 修复登录 token 过期未刷新docs文档相关docs: 补充 OpenAPI 分页参数说明refactor重构不改变外部行为refactor: 重构订单查询逻辑test测试相关test: 增加用户模块单元测试chore构建或辅助工具变动chore: 升级 eslint 配置规范提交信息最大的受益者其实是 release 流程。我们用该格式后发布工具的 changelog 可以自动从 git log 里捞出来哪次版本加了哪些功能、修了哪些 bug能自动生成。这相当于把一次本来要人工整理的工作自动化了规范带来的收益不只停留在“好看”。3.3 文档规范把“看得懂”当成最低要求文档规范这块对一个普通开发者来说最实用的参考是阮一峰老师那篇广为流传的《中文技术文档写作规范》。它核心观点可以浓缩成几点用词准确、句子简短、段落统一、专有名词大小写一致、列表层级不超过两层。听起来很基础但绝大多数技术文档写不好恰恰是基础出了问题。比如同一个项目里既写“前端”又写“前端工程师”又写“FE”读者会以为是三个东西。我给团队定的文档规范其实就几条硬约束第一接口文档一律以 OpenAPI 文件为准禁止另行维护 Word 文档第二内部技术方案必须包含背景、方案对比、决策理由、风险四段第三文档中的代码示例必须能直接复制运行不能有省略号戏法。定了这三条之后文档质量肉眼可见地提升了。核心原因很简单规范约束的是最容易扯皮的几种场景而不是面面俱到地限制自由表达。这让我意识到文档规范的最高境界不是文档写得有多漂亮而是废话少、信息准、能执行。一个开发者如果能把接口文档写到前端不用问问题、把提交记录写到追溯时不用看代码那他对 OpenSpec 的理解就已经超过绝大多数人了。4. AI 编码时代的 OpenSpec给大模型写一份“岗位说明书”4.1 为什么 AI 生成代码需要规范示例最近大家讨论 AI 写代码的体验普遍反应是功能能写出来但代码风格和自己的项目格格不入。AI 写出来的代码要么用了团队不用的库要么返回结构和项目约定不一致要么把逻辑全堆在一个 controller 方法里。问题出在哪出在给 AI 的“上下文”里缺少规范约束。就好比新入职一个同事你不告诉他团队的代码规范和接口约定他当然按自己的习惯写。所以我在实践里把 OpenSpec 的思想迁移到了 AI 编码上给 AI 喂一份结构化的规范相当于给新同事做了一遍入职培训。这份规范不一定很复杂可以是一段 Markdown告诉 AI 当前项目的技术栈、目录结构、RESTful 设计约定、响应格式、变量命名规则。有了这些显性约束AI 生成代码的“贴合度”会明显上升。4.2 我的“最小可用规范集”清单我花了一段时间迭代最终沉淀了一套拿来就能用的 AI 编码规范模板每次开新项目都会写一份放进.cursor/rules/或项目根目录下的AI_CODE_RULES.md里。以下是我最常用的结构分享出来供参考# 项目 AI 编码规范 ## 技术栈 框架NestJS 10.x TypeScript 5.x 数据库PostgreSQL Prisma ORM 校验class-validator ## 目录约束 - controller 只做参数接收和响应返回 - service 承载业务逻辑禁止直接操作 req/res - repository 层统一通过 Prisma 访问数据 ## 接口设计约定 - 所有路由风格为 RESTful资源名使用复数 - 错误响应统一使用 { code, message, data } - 分页接口统一使用 ?page1pageSize10 ## 命名规范 - 文件名kebab-case如 user-profile.service.ts - 类名PascalCase如 UserProfileService - 变量和函数camelCase如 getUserById你可能会觉得这份规范简单得不像技术文档但它恰恰是 AI 最容易理解、最容易执行的粒度。相比之下如果你给它一百页的完整规范模型反而会丢失重点不知道该以哪条为准。这与 OpenSpec 的理念是一脉相承的规范只有在被遵守时才有价值而最有用的规范往往是最小的那套约束集。4.3 如何把一套代码生成规范落到团队里落地最大的阻力来自“每个人对 AI 的使用习惯不同”。有人喜欢直接问窗口有人用 IDE 插件有人压根不用 AI。我的建议是不要强制要求每个人都把规范文件喂给 AI而是把规范嵌入到代码检查环节。比如在 CI 里加一条自定义 eslint 规则检查 controller 里是否直接出现了 SQL 片段或者出现prisma查询用规则去提示开发者“这不符合项目分层约定”。这比在聊天窗口里约束 AI 要可靠得多相当于用 OpenSpec 把 AI 生成结果“钳制”在团队认可的范围里。实际操作中我用得最多的方式是在源码注释里写清楚分层约束然后把注释作为上下文的一部分让 IDE 的 AI 插件读取。VS Code 里用 Cursor 或者通义灵码之类的工具时你可以先把项目规范文档链接进 Contex或者直接把规范文本粘到对话里。实测下来加上这么一段“岗位说明书”AI 生成的代码在结构上会比裸写时好很多虽然偶尔还是会有小瑕疵但至少不会歪到“整体没法看”的程度。5. 实操记录我在三个项目里落地 OpenSpec 的完整过程5.1 第一步先写文档规范还是先写代码规范很多人启动“推行 OpenSpec”时会纠结到底先写代码规范还是先写接口规范。我的经验是一个词从痛点切入。如果一个团队正被前后端联调折磨那首个规范就该是 OpenAPI 文件和响应体约定如果一个团队正被代码风格混乱的 review 折磨那就先把 eslint 规则和格式化工具统一。我们团队当时最大的痛点是接口字段命名混乱所以第一个产物是一份 OpenAPI 模板凡是新建接口都参照模板写描述。为了说明这个过程我记录一个真实案例。也是个小项目前后端并行开发后端用 Java Spring Boot前端用 Vue 3。我们的推进顺序如下第一周建一个独立的api-contract仓库放入一份最基础的 OpenAPI 模板。第二周由后端一个资深同事把两个已有核心接口写成符合模板的规范文件。第三周前端接入 openapi-typescript成功从规范文件生成 TS 类型并替换掉手写类型。第四周在 CI 上增加 OpenAPI 格式校验它的作用就是检查后端代码里的注解或 controller 是不是跟规范文件一致。这个渐进式过程很稳前两周几乎无感第三周开始有感知和收益第四周才把规范固化成强制项。团队并没有因为“推规范”而停工反而在第三周就感受到了“少问几个字段问题”的变化。5.2 一次接口联调事故让我明白契约要先于代码印象最深的一次事故是这样的。后端有个负责订单详情的接口响应里需要返回itemList字段结果后端同事在实现的时候漏了List直接写成了items。前端按照 OpenAPI 文件生成的类型里写的是itemList联调时前端拿到的数据全是 undefined页面渲染不出列表。排查了大半天最后发现只是字段名不一致。这起事故的直接原因是后端改了代码却没有同步更新 OpenAPI 文件。这让我彻底明白契约要先于代码规范文件里的定义必须和实际实现强绑定。从那以后我们加了一条硬性检查CI 在测试阶段会启动服务调用一个工具它能把运行时的真实接口响应结构和 OpenAPI 文件做对比不一致就直接失败。效率当然会有损耗但这个检查值得保留它相当于给契约上了保险。如果你还没到引入复杂工具的阶段起码要做到每一次接口改动MR 里必须包含 OpenAPI 文件的 diff否则 review 不通过。5.3 让规范自动生效的四个检查点“定了规范”和“规范被遵守”之间隔着一条巨大的鸿沟。为了让 OpenSpec 的规范自动生效我总结了四个关键检查点。第一个是 IDE 层靠 ESLint、Prettier、EditorConfig 这些插件在写代码时实时提示第二个是 Git 提交层靠 husky 和 lint-staged 在 commit 前拦截第三个是 CI 层通过流水线里的 npm scripts 或 Gradle/Maven 插件执行完整校验第四个是 MR review 层通过评审人的经验去兜底但尽量把能自动化的事情全部自动化。这四个检查点缺一不可。IDE 层靠自觉提交层靠习惯CI 层靠强制review 层靠经验。说实话如果只做了 CI 层检查开发者会在本地各种绕过如果只做了提交层检查那这些配置会绕过 CI最好的状态是让开发者“顺手就能通过”而不是“费劲才能绕开”。这才是规范落地的终极目标——体验被人性设计执行被工具兜底。6. 常见问题与排查技巧实录6.1 团队不配合怎么办这是我在后台常被问到的问题也是推行规范最现实的阻力“我们团队根本不吃这套规范文件写来没看。”遇到这种情况我的建议是不要硬推而是选一个大家共同忍受的痛点用单个最小方案解决它。比如前端一直抱怨接口文档过期那你自己先把接口规范文件写起来写着写着前端会在联调时自动切换到新文件他们尝到甜头后根本不需要你发号施令就会主动要求你“别停”。同时有一点需要说明的是“规范不是写给人看的而是写给场景用的”。如果团队里没有人因为“字段不清、格式混乱、提交信息扯皮”导致过事故那就别指望大家会热情拥抱规范。规范的真正推动力永远是“它能帮我少踩坑”。6.2 规范太多、文档太长根本看不完怎么办如果一个规范文档长到人们不愿意打开那它就已经失败了。我推荐的解法是“规范金字塔”顶部是 2~3 页的核心约定所有人都必须看中间是各模块的专项规范比如 OpenAPI 文件编写指南、eslint 配置说明、提交信息速查表按需查阅底部是具体工具链的自动执行方案。能做到让 80% 的规范“不读也能被执行”就说明自动化程度够高了。让 AI 帮你看规范也是一个思路。现在很多 IDE 插件支持把项目里的规范文档作为索引你写代码时它会自动匹配相关约定比如你写一个createOrder函数它会提示你项目规定返回结果要包一层ApiResponse。这种“嵌入式”的规范消费方式比让人去背规范有效得多也是 AI 时代对 OpenSpec 实践的又一次升级。6.3 几个我踩过之后不想你再踩的坑最后分享几个具体实操里容易踩的坑。第一个是 OpenAPI 文件的$ref用得过多层级太深导致生成器解析后产生大量嵌套类型前端看接口文档时反而看不懂。我的建议是控制在两层以内中间的通用对象抽到components/schemas里不要一个套一个。第二个是 commit 规范没有配套自动修正工具。只写了一堆文档让大家“照着写”效果很差。实际上 commitlint 这类工具可以直接拦截不合规的提交信息还能配合 commitizen 生成交互式表单让开发者在终端里选择类型、填写范围整个过程像填问卷一样。这比苦口婆心强调“大家自觉”管用一百倍。第三个是代码生成的规范文件没有版本管理。OpenSpec 也是一种代码资产它会被修改、被演进所以要建独立的仓库、走 review、留 changelog。如果认为规范文档是“写一次就固定”的那它很快会跟现实脱节久而久之就没人信它了。我的整体感受是OpenSpec 的实践从来不是三分钟能搞完的项目而是一个持续演进的过程。我自己也是从看到别人团队的高速迭代而感到羡慕到自己动手做起来中间碰了壁才有这些心得。如果你正准备在自己的项目里起步我的建议很简单从一个接口、一份 OpenAPI 文件、一条 commit 规则开始先把小的规范跑通你会慢慢体会到它带来的确定性。这种确定性无法直接用代码量衡量但它会让你和团队在未来每一次变更里都觉得踏实。