進化と保守 ─ Versioning / Deprecation / Migration
共通基盤は 長く生きる。3 年後、5 年後、10 年後にも動いている前提で API を設計する必要がある。だが要件は変わるし、技術も古びる。変えながら壊さない 作法が要る。
この章では Stripe を主役に進化の 3 段階 ── Versioning / Deprecation / Migration ── を扱う。
Versioning:日付ベースか path ベースか
第 5 章でも触れた、API のバージョニング戦略。
Path ベース(一般的)
POST /v1/payments
POST /v2/payments # 互換性 break するときに新版
pros: シンプル、URL で分かる
cons: client が手動で /v2 に移行する必要、互換性のない 2 つの実装を保守
日付ベース(Stripe 流)
POST /payments
Stripe-Version: 2024-04-10
- 各 version は backward-incompatible だが increment は 小さい
- 初回 API call で 自動 pin される(client は明示しなくていい)
- Compatibility Layer が version 間を変換する
Stripe の Compatibility Layer
Stripe Engineering “APIs as infrastructure” の解説:
内部のビジネスロジックは 常にモダンな format で書く。version 別の処理は entry/exit のレイヤーに隔離する。
graph TB
C1[Client v2024-04-10]
C2[Client v2024-08-01]
C3[Client v2025-12-01]
C1 --> R[Request Layer]
C2 --> R
C3 --> R
R -->|変換: 古い → モダン| Core[Modern Core Logic]
Core -->|変換: モダン → 古い| Resp[Response Layer]
Resp --> C1
Resp --> C2
Resp --> C3
style Core fill:#e1f5ff
style R fill:#fff4e1
style Resp fill:#fff4e1
これにより:
- コアロジックは 1 つ、version の差分は薄いレイヤー
- 新 version 追加は 薄いレイヤーを 1 枚足す だけ
- 古い version の維持コストは増えるが、コアは綺麗
どちらを選ぶか
| 場面 | 推奨 |
|---|---|
| 短命な API(数年) | Path-based v1 / v2 |
| 長命な API(5 年〜) | Date-based |
| 内部基盤(社内利用) | Path-based で十分 |
| 外部公開(Stripe / Twilio など) | Date-based |
後方互換性の “聖戦”
守るべきルール(再確認)
第 5 章で挙げたが、進化の文脈で再確認:
| 操作 | 互換性 |
|---|---|
| Optional field 追加 | OK |
| Required field 追加 | NG |
| Field 削除 | NG |
| 型変更 | NG |
| Enum 値追加 | 注意(client が unknown を扱える前提) |
| Enum 値削除 | NG |
| Endpoint 追加 | OK |
| Endpoint 削除 | 段階的 deprecate |
「足すは OK、引くは NG」が原則。
Schema Registry の活用
Avro / Protobuf を使う基盤(Kafka 等)では Schema Registry で機械的に互換性を検証:
# Compatibility check
- BACKWARD: 新 schema で書いた message を古い schema で読める
- FORWARD: 古い schema で書いた message を新 schema で読める
- FULL: 両方
CI で互換性違反を検出して push 前に止める。
Deprecation の作法
機能を削除したいとき、いきなり消さない。段階的なプロセスを踏む。
業界標準のフロー
graph LR
A[Stage 1: Deprecation 通知] --> B[Stage 2: 警告ヘッダー]
B --> C[Stage 3: Brownout]
C --> D[Stage 4: Sunset]
Note1[最低 1 年は維持]
style A fill:#e1ffe1
style D fill:#ffe1e1
Stage 1: Deprecation 通知
HTTP/1.1 200 OK
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Deprecation: Sat, 9 May 2026 00:00:00 GMT
Link: <https://platform.example.com/docs/migration>; rel="deprecation"
Sunset / Deprecation ヘッダー(RFC 9745)で予告。
メール / Slack / 管理画面でも通知。気付かせる のが目的。
Stage 2: 警告ヘッダー(数ヶ月)
HTTP/1.1 200 OK
Warning: 299 - "This endpoint is deprecated, will be removed on 2026-12-31"
利用者の中で意識が広がる時期。
Stage 3: Brownout(一時停止)
Day 1: 1 分間だけ 410 Gone を返す
Week 2: 1 時間だけ 410
Week 4: 1 日だけ 410
最終週: 半分の時間 410
完全停止前に “予告停電” を入れる。利用者が「あ、ヤバい、移行しなきゃ」と気付く効果。Stripe / GitHub が実際に使う手法。
Stage 4: Sunset(停止)
HTTP/1.1 410 Gone
{
"error": {
"type": "endpoint_removed",
"code": "ENDPOINT_REMOVED",
"message": "This endpoint was removed on 2026-12-31. Migration: ..."
}
}
完全停止。それでも一定期間は 410 Gone を返す ことで、利用者が原因を分かるようにする。
Deprecation 期間の目安
| 利用者規模 | 期間 |
|---|---|
| 内部利用(10 サービス未満) | 3-6 ヶ月 |
| 内部基盤(数十サービス) | 6 ヶ月-1 年 |
| 外部公開(数千 client) | 1-2 年 |
| Critical(金融など) | 2-3 年 |
「短すぎる」と利用者が間に合わない。「長すぎる」と保守コストが膨らむ。バランスは利用者の規模で決まる。
Migration の Heroic Effort
「自動移行」が理想だが、現実は難しい。
自動移行できる場合
Brandur “Why Doesn’t Stripe Automatically Upgrade API Versions?” の論点:
利用者が呼んでいる endpoint と、新 version で変わる endpoint が 完全に重ならない 場合は、自動的に上げて良い。
例:「v2 では /payments/refund が変わるが、利用者は /payments しか叩いていない」→ 自動的に v2 に pin する。
自動移行できない場合
ほとんどの基盤は client コードの 書き換えが必要。これを支援するツール:
- Codemod(jscodeshift など):自動コード書き換え
- Migration guide:step-by-step ドキュメント
- Migration Office Hours:チャット / 会議で支援
- Lint rule:旧 API を warning で検知
Stripe の “Migration Tool”
Stripe API upgrades は Web UI 上で:
- 自分が pin している version
- 最新 version との差分
- 各差分が自分のコードに影響するか
- 推奨 migration step
「機械的に決まることは機械にやらせる」のが共通基盤の責任。
Sunset Policy ─ 何年で消すか
# Stripe の例(実際)
- Stripe-Version より古い 1 つは維持
- 1 年以上経つと automatic upgrade を提案
- 数年で完全 sunset
# 内部基盤の例
- 旧 version は最低 1 年維持
- 利用者数 < 5 なら個別に migrate 支援
- > 100 利用者がいれば 2 年維持
Sunset policy は文書化 して公開する。利用者が長期計画を立てられるようにする。
進化のアンチパターン
❌ Big Bang Rewrite
「v2 で全部作り直す」と決める → 利用者の移行が間に合わない / 新版にもバグ → 1-2 年で破綻
❌ Forever Backward Compat
「絶対互換性を保つ」と決める → 進化できない / コードが負債で埋まる / 新規利用者が辛い
❌ Silent Removal
通知なく機能削除 → 利用者の信頼を失う
❌ 説明のない Deprecation
「これは deprecated です」とだけ書いて代替を示さない → 利用者が困惑
進化のチェックリスト
graph TB
Q1[Versioning 戦略] --> Q2[後方互換ルール]
Q2 --> Q3[Schema 自動検証]
Q3 --> Q4[Deprecation プロセス]
Q4 --> Q5[Migration 支援]
Q5 --> Q6[Sunset Policy 文書化]
Q6 --> OK[進化を続けられる]
style OK fill:#e1ffe1
この章の要点
- Versioning:日付ベース(Stripe 流)vs path ベース、長命なら日付
- Stripe の Compatibility Layer:内部はモダン、変換は entry/exit に隔離
- 後方互換性:足すは OK、引くは NG
- Schema Registry で機械的に互換性検証
- Deprecation は 4 段階:通知 / 警告ヘッダー / Brownout / Sunset
- 期間目安:内部 3-6 ヶ月、外部 1-2 年
- Migration 支援:codemod / guide / office hours / lint
- Sunset Policy を文書化、信頼の根拠
- アンチパターン:Big Bang / Forever Compat / Silent Removal / 説明なし Deprecate
次章への問いかけ
進化を支えるのは 利用者の協力。だが利用者は文書を読まない、移行を後回しにする。
次章で 利用者の認知負荷を下げる ── Golden Path / SDK / Self-service / Documentation。