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オーバーレイ, Optic, Bump.sh |
アーキテクチャパターン
バージョニング戦略の決定木
追加のみか?(新しいフィールド、新しいエンドポイント)
├─ YES → バージョン不要(後方互換性あり)
└─ NO → フィールド名や型の変更か?
├─ YES → 新旧両方のフィールドを維持できるか?
│ ├─ YES → 新しいフィールドを追加し、古いフィールドを非推奨に(マイナーバージョン)
│ └─ NO → メジャーバージョンアップ(v1 → v2)
└─ NO → 削除か?
└─ YES → サンセット期間を設けてメジャーバージョンアップ
並列バージョン展開
// バージョンベースのルーティングを行う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`を削除
サーバーはバージョン指定のないリクエストをアカウントのデフォルトバージョンにピン留めする。
各バージョンは、内部の正規モデルに対する変換レイヤーとして機能する。
アンチパターン
- 過剰なバージョニング — ほとんどの変更は追加的でありバージョンアップは不要。フィールドは自由に追加してよい
- サンセット期限なし — 具体的な廃止日を設定せずに非推奨化すると、バージョンが永久に残り続ける
- コントローラー全体のコピー — バージョンごとにビジネスロジックを複製するのではなく、変換/アダプターレイヤーを使用する
- 未知のフィールドを無視 — APIは寛容な読み取り手であるべき。入力の未知フィールドは無視する(Postelの法則)
- マイナーバージョンでの破壊的変更 — 利用者が依存していたものを削除することは、意図に関わらず破壊的変更である
品質チェックリスト
- 追加的変更(新しいフィールド、新しいエンドポイント)はバージョンアップなしでリリースされる
- 破壊的変更には6ヶ月以上のサンセット期間を伴うメジャーバージョンアップが必要
- 非推奨バージョンにSunsetヘッダー(RFC 8594)が設定されている
- Linkヘッダーが利用者を後継バージョンに誘導する
- バージョンごとのトラフィックを監視して移行の進捗を追跡する
- バージョンごとにOpenAPI仕様が公開され、差分ツール(Optic/Bump.sh)が使用されている
- 利用者への通知計画:チェンジログ、メール、ダッシュボード警告
- バージョンルーティングはアプリケーションコードではなくゲートウェイレベルで処理される
- 統合テストで後方互換性を検証する(コントラクトテスト)