API 迁移
版本化并迁移 API 不破坏现有客户端 — 弃用、影子、再切换。
为什么
你的 API 有你不可控的客户端 — 旧版移动应用、第三方集成、内部服务。单端点破坏性变更可同时破坏数千客户端。有纪律的方式是版本化 + 弃用 + 迁移窗口:新版与旧版并存、标旧弃用(带 Sunset 头)、给客户端截止日、监控采用、然后仅在采用达 100% 或截止过且沟通延期后删旧。这比大爆炸重命名多工,但它是零客户端事件与多日宕机之差。API 是契约;破契约是破信任。
何时使用
需破坏性 API 变更时。非破坏变更(加字段)不需版本升。
如何做
1. 版本化新 API:/v2/orders 与 /v1/orders 并存(URL 版本对客户端最清晰) 2. 发 v2 带新行为;v1 不变继续工作 3. 给 v1 加弃用信号:Sunset: Sat, 31 Dec 2024 GMT + Deprecation: true 头 + 指向 v2 的 Link 4. 文档化迁移:changelog 含确切 diff 和客户端迁移指南 5. 监控 v1 用量:记每个调用者(按 API key / user-agent);直接联系 top 调用者 6. 设 sunset 日期(>=6 个月后)并在弃用头 + 文档 + 邮件中沟通 7. sunset 后(或 v1 流量 0 持续 30 天)删 v1 — 再留一周期带迁移链接的 404
示例
# v1 与 v2 并存(Express)
app.get('/v1/orders', (req, res) => {
res.set('Deprecation', 'true');
res.set('Sunset', 'Sat, 31 Dec 2024 GMT');
res.set('Link', '</v2/orders>; rel="successor-version"');
// ... 旧行为
});
app.get('/v2/orders', (req, res) => {
// ... 新行为(如返回数组而非 {items:[]})
});
# 监控 v1 调用者(v1 上的日志中间件)
app.use('/v1/*', (req, res, next) => {
logger.warn({ path: req.path, key: req.apiKey, ua: req.get('user-agent') }, 'v1 弃用');
next();
});
# 迁移指南(docs/api-migration-v1-to-v2.md)
## 破坏:GET /orders 响应形状
- v1:{ "items": [...], "total": 5 }
- v2:[...] (直接数组;total 在头 X-Total-Count)
- 客户端改:把 res.items 换成 res 直接;从头读 total
# Sunset 时间线
- 2024-06-01:v2 发,v1 弃用
- 2024-12-31:v1 返 410 Gone 带 Link 到 v2
- 2025-03-31:v1 路由完全删常见问题
- ⚠v2 发当天删 v1 — 旧版移动客户端瞬间坏且无追索。
- ⚠无 Sunset/Deprecation 头 — 客户端无编程信号,直到坏才知迁移。
- ⚠不监控 v1 流量 — 删 v1 发现合作集成仍用,破坏收入流水线。
最佳实践
- ✓v2 与 v1 并存带弃用头(Sunset + Deprecation + Link)— 给客户端编程信号和截止日。
- ✓按调用者监控 v1 流量并直接联系 top 消费者 — 别靠客户端读文档。
- ✓v2 发后至少保 v1 6 个月;仅 v1 流量连续 30 天为 0 时删。