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 |
架構模式
版本化策略決策樹
是否僅為新增?(新欄位、新端點)
├─ 是 → 不需版本 bump(向後相容)
└─ 否 → 是否為欄位重新命名/型態變更?
├─ 是 → 能否同時保留新舊欄位?
│ ├─ 是 → 新增新欄位,棄用舊欄位(次要版本)
│ └─ 否 → 主要版本 bump(v1 → v2)
└─ 否 → 是否為移除?
└─ 是 → 主要版本 bump,並設定淘汰期間
平行版本佈署
// 使用基於版本路由的 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`
伺服器將未指定版本的請求,固定使用帳戶的預設版本。
每個版本都是對標準內部模型的轉換層。
反模式
- 過早版本化 — 大多數變更都是新增,不需要版本 bump;應自由新增欄位
- 無淘汰時間表 — 棄用卻未設定具體停用日期,導致版本永久存在
- 複製整個控制器 — 應使用轉換/配接層,而非每個版本複製商業邏輯
- 忽略未知欄位 — API 應為寬容的讀取者;在輸入時忽略未知欄位(Postel 定律)
- 在次要版本中引入中斷性變更 — 只要使用者依賴了該功能,移除它就是中斷性變更,無論你的意圖為何
品質檢查清單
- 新增變更(新欄位、新端點)不需版本 bump
- 中斷性變更需要主要版本,並設定 6 個月以上的淘汰期間
- 在已棄用版本上設定 Sunset 標頭(RFC 8594)
- Link 標頭指向使用者的後續版本
- 監控各版本流量以追蹤遷移進度
- 每個版本發佈 OpenAPI 規格,並搭配差異比較工具(Optic/Bump.sh)
- 使用者通知計畫:更新日誌、電子郵件、儀表板警告
- 版本路由在閘道層級處理,而非應用程式程式碼中
- 整合測試驗證向後相容性(契約測試)