API 版本管理与向后兼容性 是做什么的?
设计 API 版本管理策略,优雅地演进而不破坏现有消费者。
激活触发器
激活于: "API 版本管理"、"向后兼容性"、"弃用"、"破坏性变更"、"API 迁移"、"v1 v2"、"sunset 标头"、"API 演进"、"非破坏性变更"
不适用于: 数据模式演进 → schema-evolution-manager | 网关版本路由 → api-gateway-reverse-proxy-expert | GraphQL 弃用 → graphql-server-architect
快速开始
- 分类变更 — 新增(安全)、修改(可能破坏)、删除(破坏性)
- 选择策略 — URL 路径(
/v2/)、标头(API-Version)或内容协商 - 实现 Sunset 标头 — RFC 8594 告知消费者旧版本何时消亡
- 并行运行版本 — 主版本至少保持 6 个月重叠
- 监控采纳情况 — 追踪每版本的流量以知道何时退役
核心能力
| 领域 | 技术 |
|---|---|
| URL 版本管理 | /api/v1/、/api/v2/ 基于路径的路由 |
| 标头版本管理 | API-Version: 2024-01-15、Accept-Version |
| 内容协商 | Accept: application/vnd.myapi.v2+json |
| 弃用 | Sunset 标头(RFC 8594)、Deprecation 标头 |
| 工具 | OpenAPI 3.1 overlays、Optic、Bump.sh |
架构模式
版本管理策略决策树
是纯新增吗?(新字段、新端点)
├─ 是 → 无需版本升级(向后兼容)
└─ 否 → 是字段重命名/类型变更吗?
├─ 是 → 能否同时保留新旧字段?
│ ├─ 是 → 新增字段,弃用旧字段(小版本)
│ └─ 否 → 主版本升级(v1 → v2)
└─ 否 → 是删除操作吗?
└─ 是 → 主版本升级并附带 sunset 期限
并行版本部署
// 基于版本的路由的 Express 路由器
import { Router } from 'express';
const v1Router = Router();
const v2Router = Router();
// v1:返回 { name: string }
v1Router.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
res.json({ name: user.fullName }); // 遗留格式
});
// v2:返回 { firstName, lastName, displayName }
v2Router.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
res.json({
firstName: user.firstName,
lastName: user.lastName,
displayName: user.fullName,
});
});
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
// v1 的 Sunset 标头中间件
v1Router.use((req, res, next) => {
res.set('Sunset', 'Sat, 01 Nov 2026 00:00:00 GMT');
res.set('Deprecation', 'true');
res.set('Link', '</api/v2>; rel="successor-version"');
next();
});
基于日期的版本管理(Stripe 模型)
API-Version: 2026-03-15
变更与日期绑定,而非整数:
2026-01-01 → 基线
2026-03-15 → 将 `name` 重命名为 `display_name`
2026-06-01 → 移除 `legacy_field`
服务器将对无版本请求固定到账户的默认版本。
每个版本是规范内部模型之上的一个转换层。
反模式
- 过度版本化 — 大多数变更是附加性的,不需要版本升级;自由添加字段
- 没有 sunset 时间线 — 弃用而没有具体的关闭日期会导致版本永远存活
- 复制整个控制器 — 使用转换/适配器层,而不是每个版本复制业务逻辑
- 忽略未知字段 — API 应容忍读取;在输入中忽略未知字段(Postel 定律)
- 在小版本中引入破坏性变更 — 如果消费者依赖了某个内容,移除它就是破坏性的,无论你的意图如何
质量检查清单
- 附加性变更(新字段、新端点)无需版本升级即可发布
- 破坏性变更需要主版本升级并附带 6 个月以上的 sunset 期限
- 在已弃用版本上设置 Sunset 标头(RFC 8594)
- Link 标头指向后续版本
- 按版本监控流量以跟踪迁移进度
- 每个版本发布 OpenAPI 规范,并配套差异工具(Optic/Bump.sh)
- 消费者通知计划:变更日志、邮件、仪表盘警告
- 版本路由在网关层处理,而非应用代码中
- 集成测试验证向后兼容性(契约测试)