| 名称 | api-contract-diff |
| 描述 | 当需要比较两个版本的 OpenAPI 文档、判断改动是否破坏兼容性并给出发布策略时使用。该技能把字段级差异归为破坏性、行为性、兼容性三类,输出面向调用方的变更说明与灰度方案,并附带可直接运行的差异检测脚本。适用于 REST/HTTP API 的版本升级、微服务契约治理和对外接口评审。关键词:OpenAPI、接口契约、兼容性、破坏性变更、breaking change、API 版本、Swagger、契约测试、字段差异、废弃字段、灰度发布、微服务、SDK 兼容。 |
OpenAPI 契约差异评审
比较两版 OpenAPI 文档,回答三个问题:改了什么、会不会打断调用方、该怎么发布。
何时使用
- 服务端准备发版,需要确认是否构成 breaking change。
- 对外 API 需要给出客户可读的变更公告。
- 多团队共用网关,需要建立契约校验规则。
- 单体拆分微服务,需要对齐新旧接口语义。
不适用:GraphQL schema 差异(可参考同一套三级分类思路);纯性能优化且契约未变。
输入
- 旧版 OpenAPI(JSON 或 YAML)与新版。
- 接口的消费方清单(内部服务、SDK、第三方)。
- 版本策略:URL 版本、Header 版本还是仅追加式演进。
- 是否允许一次性强制升级。
缺少消费方清单时,按“全部视为外部调用方”从严处理。
工作流
第 1 步:字段级提取
把两份文档归一化为可比较的扁平结构:
{path} {method} -> params[{in,name,type,required}], requestBody{props,required},
responses[{status, props, required}], security, deprecated
只比较实际暴露的契约,忽略描述文案、示例值、tag 顺序等非语义字段。
第 2 步:三级分类
| 级别 | 判定 | 处理 |
|---|---|---|
| 破坏性 | 删除/重命名路径或必填字段、收窄枚举、收紧校验、改变类型、新增必填请求字段、移除响应字段、改变状态码语义 | 必须版本隔离或双写兼容期 |
| 行为性 | 放宽校验、新增可选字段、新增响应字段、默认值变化、分页上限调整 | 可同版本发布,需公告 |
| 兼容 | 新增路径、新增可选参数、文档与示例修正 | 直接发布 |
完整判定表见 references/breaking-changes.md。
第 3 步:消费方影响面
对每个破坏性变更标注:
- 受影响消费方(按调用日志或清单)。
- 最坏后果:编译失败、反序列化失败、静默取默认值、业务错误。
- 探测方式:SDK 重新生成、契约测试、网关采样。
“静默取默认值”优先级最高,因为它不会报错却会算错数。
第 4 步:发布策略
按 references/versioning-policy.md 选择:
- 追加式演进:只加可选字段,不删不改。
- 双版本并行:
/v1与/v2共存,网关按 Header 或路径路由。 - 兼容层映射:旧字段与新字段双向转换,设定下线日期。
- 强制升级:仅限内部且消费方可控的场景。
第 5 步:验证
- 运行
scripts/diff_openapi.py old.json new.json --format markdown。 - 对每条破坏性变更补一条契约测试:旧请求在新服务上必须得到明确错误而非静默成功。
- 输出变更公告,包含迁移示例(请求前后对比)。
检查清单
- [ ] 路径与方法的新增、删除、重命名均已列出。
- [ ] 请求体新增必填字段已判定为破坏性。
- [ ] 响应字段删除或类型变化已判定为破坏性。
- [ ] 枚举收窄与校验收紧已识别(最易漏)。
- [ ] 每个破坏性变更都有消费方与迁移方式。
- [ ] 旧版本下线日期与监控指标已确定。
- [ ] 变更公告包含可复制的迁移代码片段。
输出模板
## 结论
本次变更包含 N 项破坏性变更,建议采用「双版本并行」发布。
## 破坏性变更
| # | 位置 | 变更 | 影响 | 消费方 | 处理 |
| --- | --- | --- | --- | --- | --- |
## 行为性变更
| # | 位置 | 变更 | 注意事项 |
| --- | --- | --- | --- |
## 迁移指南
旧请求 → 新请求示例
## 下线计划
| 版本 | 冻结日期 | 下线日期 | 监控指标 |
| --- | --- | --- | --- |
反模式
- 认为“只是加了个字段”一定兼容——新增必填字段是典型破坏性变更。
- 用 HTTP 200 返回错误体来“避免破坏”,实际让监控与重试逻辑全部失效。
- 不做契约测试,只靠人工比对文档。
- 同一路径同时承载两种语义,靠可选字段猜调用方意图。
- 删除字段前不观察访问日志,直接下线导致长尾调用方报错。
参考文件
references/breaking-changes.md:破坏性变更判定清单。references/versioning-policy.md:版本与灰度策略选择。scripts/diff_openapi.py:两版 OpenAPI 差异检测脚本。