はじめに
APIのバージョニングは「技術」より「合意形成」で失敗しがちです。
v1 v2 の数字を付けるかどうかよりも
- 互換性をどこまで守るのか
- 何を「破壊的変更」と見なすのか
- 変更の通知と移行期間をどう運用するのか
が曖昧だと、結局は毎回揉めてスピードが落ちます。
この記事は、バージョニング手法の紹介ではなく
チームで合意できる互換性ポリシーと、すぐ使えるテンプレをまとめます。
よくある失敗パターン
- 「とりあえず v2」で逃げるが、v1が永遠に残る
- v1とv2で仕様の差分が曖昧で、クライアントが移行できない
- 破壊的変更の定義がなく、レビューで毎回議論が再発する
- URLバージョンとヘッダバージョンが混在して、運用が破綻する
まず決めること 互換性ポリシー
互換性の前提を分類する
まずクライアントの性質で期待値が変わります。
- 単一フロントで同時デプロイできる
- モバイルが含まれ更新が遅い
- 外部提供で、更新時期が制御できない
後ろに行くほど「破壊しない設計」と「長い移行期間」が必要です。
破壊的変更の定義を固定する
チーム内で、以下を破壊的変更として扱うか決めます。
- レスポンスフィールドの削除
- レスポンスフィールドの型変更
- 必須入力の追加
- 列挙値の削除
- HTTPステータスやエラーコードの意味変更
逆に「非破壊」扱いでも、クライアント実装によっては壊れます。
たとえばフィールド追加は一般に安全ですが
- 厳格なスキーマバリデーション
- 未知フィールドをエラーにするクライアント
があると破壊になります。
その場合は「クライアントの実装規約」側で吸収するべきです。
バージョニング手法の選び方
SemVer(Semantic Versioning)をAPIにどう適用するか
SemVerは本来「ソフトウェアのリリース番号」の約束事ですが、API運用にもそのまま流用できます。
重要なのは「番号の付け方」よりも、
- 何を破壊的変更(= MAJOR)と見なすか
- 破壊的変更を出すまでの移行/告知をどうするか
をチームで固定できる点です。
基本ルール(X.Y.Z)
- MAJOR(X): 破壊的変更を含む
- MINOR(Y): 後方互換のある機能追加
- PATCH(Z): 後方互換のあるバグ修正
「APIはURLにv1/v2を付けるか?」とは独立の話です。
例えばURLは /api/v1 のままでも、内部の契約(スキーマ/仕様)には 1.7.3 のようなSemVerを割り当てて運用できます。
APIにおけるMAJOR/MINOR/PATCHの読み替え例
- MAJOR
- レスポンスフィールドの削除
- レスポンスフィールドの型変更
- 必須入力の追加(サーバー側でデフォルト補完できない)
- エラーコード/HTTPステータスの意味変更
- 列挙値の削除(クライアントにunknown許容がない場合)
- MINOR
- 任意フィールドの追加(クライアントが未知フィールド許容の前提)
- 列挙値の追加(クライアントがunknown許容の前提)
- 新エンドポイント追加(既存に影響なし)
- PATCH
- 仕様の明確化(ドキュメントの誤記修正)
- バグ修正(契約を変えない範囲)
ポイントは「フィールド追加は常にMINORで安全」と思い込まないことです。
未知フィールドで落ちるクライアントが存在するなら、それはあなたの互換性ポリシーではMAJOR扱いになります。
MAJORを出す前の運用(deprecate期間をSemVerに組み込む)
破壊的変更は、いきなりMAJORで切るより
- 旧要素を deprecated として残す(MINORで追加/告知)
- 移行期間を確保(例: 90日)
- 期限到来で削除(MAJORで削除)
の順にすると揉めにくいです。
0.y.z(v0系)をどう扱うか
SemVerでは 0.y.z は「互換性を保証しない」扱いです。
ただしAPIでこれをそのままやると運用が事故りやすいので、
- 外部提供やモバイルが絡むAPIは、原則
1.0.0から開始 -
0.y.zを許すなら「どの範囲は壊して良いか」を別途明文化
のどちらかをおすすめします。
URLにバージョンを入れる
例
/api/v1/users
向いている
- 外部提供で分かりやすさが最優先
- キャッシュやログでバージョンを一目で追いたい
注意
- vが増えるほどエンドポイントが増殖しやすい
- v跨ぎの共通化が難しくなる
ヘッダでバージョンを指定する
例
Accept: application/vnd.example.v1+json
向いている
- URLを安定させたい
- バージョンをメディアタイプとして扱いたい
注意
- ブラウザやデバッグ時の見えにくさ
- キャッシュ層でヘッダを考慮する必要
クエリで指定する
例
/users?api_version=1
注意
- 通常はおすすめしません
- キャッシュや仕様の一貫性が崩れやすい
まずはこれで進める実務テンプレ
テンプレ 互換性ポリシー文書
以下をそのまま README やADRに貼って埋めると、議論が早く終わります。
- 対象API
- サポートするクライアント種別
- 非破壊変更の定義
- 破壊的変更の定義
- 互換性の保証期間
- 廃止告知の手段
- 移行期間の最小日数
- v1終了の基準
SemVerを採用する場合は、追加で以下も埋めると議論が止まります。
- バージョン表記(例: X.Y.Z / 日付 / その他)
- MAJOR/MINOR/PATCHの判定基準(破壊的変更の定義との対応)
- deprecatedの通知方法と最短維持期間(例: 90日 or 2 MINORリリース)
- 「0.y.z」を使うか(使うなら互換性期待値)
テンプレ 破壊的変更チェックリスト(PR用)
まず、この変更がSemVerでどれに該当するかを明記
-
PATCH / MINOR / MAJOR のどれかを書いた
-
フィールド削除がない
-
フィールド型変更がない
-
必須入力の追加がない
-
列挙値の削除がない
-
エラーコードの意味変更がない
-
既存クライアントが未知フィールドを許容する
-
変更点がドキュメントに反映されている
-
移行手順がある
MAJOR相当の場合
- deprecated期間(移行猶予日数/期限)が明記されている
- 期限到来後の削除計画(いつ/どう消すか)がある
テンプレ 非互換を避ける設計パターン
フィールド削除の代わりに deprecated
- すぐ消すのではなく deprecated として残し
- 一定期間後に削除する
必須入力追加の代わりにデフォルト適用
- 直ちに必須化せず
- サーバー側でデフォルトを補う
- 次バージョンで必須化の準備をする
列挙値の進化
- 列挙に新しい値を追加する前提で
- クライアントは unknown を許容する
運用の落とし穴
v1とv2が長期併存して止まる
原因は「v1終了条件」がないことが多いです。
- ある割合以上のクライアントが移行
- ある日付までに移行
- 主要顧客の移行完了
など、終了条件を最初から決めます。
ドキュメントが追随せず、実装が真実になる
- 変更のたびに差分を書く
- 仕様の例(リクエスト/レスポンス)を更新する
これをPRの必須チェックに入れるのが一番効きます。
まとめ
APIバージョニングで揉めないコツは
手法選びより先に、互換性ポリシーと運用ルールを固定することです。
テンプレを埋めて合意形成のコストを下げ
破壊的変更をレビューで機械的に検知できる状態を作ると、長期的に効きます。