APIの変更

呼び出し元のためのAPIバージョニングのベストプラクティス

1分で読めます

APIバージョニングとは、契約を変更した後もその古い契約を動作させ続ける実践のことであり、それによって呼び出し元はあなたのスケジュールではなく自分自身のスケジュールで移行できるようになる。この一文には重要な二つの決定が含まれている。何を契約の変更とみなすか、そして古い契約をどれだけの期間動作させ続けるか、だ。バージョン番号がどこに存在するかは、多くのバージョニング論争の中心にあるものだが、三つのうち最も重要度が低く、最も正しくやりやすい部分でもある。

いつAPIをバージョン管理すべきか

変更が正しい呼び出し元を壊してしまう場合にのみ、APIをバージョン管理しよう。追加的な変更、新しいフィールド、新しいエンドポイント、新しいオプションのパラメータはバージョンを必要としない。古い契約に対して書かれた呼び出し元は動作し続け、新しい機能はただそこに存在するだけだからだ。破壊的変更はバージョンを必要とする。代替案は、呼び出し元がエラーからそれを知ることになるからだ。追加的なものも含めてすべてのリリースをバージョン管理することは、呼び出し元にバージョンがノイズであると教え込み、彼らは本当に重要な通知を読まなくなる。

実用的なテストは破壊的変更の記事にあるものと同じだ。文書化された動作だけに依存していた呼び出し元が、動作し続けるために何かを変更しなければならないなら、その変更はバージョンを必要とする。そうでなければ、現行バージョンの下で出荷し、チェンジログの項目を書こう。

どのAPIバージョニングスキームを使うべきか

呼び出し元が最も容易に見て設定できるスキームを使おう。多くの公開APIにとってそれは、URLパス内のバージョンか、日付付きのバージョンヘッダーだ。四つの一般的なスキームは能力よりも、呼び出し元に何を求めるかで違いが出るのであり、それこそが選択の正しい根拠だ。

スキーム例呼び出し元がすべきこと使っている例
URLパス/v2/invoices移行時にURLを変更するほとんどの公開RESTのAPI
バージョンヘッダーX-GitHub-Api-Version: 2022-11-28ヘッダーを送るか、デフォルトを受け入れるGitHub
日付付きアカウントバージョンStripe-Version: 2026-08-26リクエストごと、またはアカウントごとに日付を固定するStripe
クエリパラメータ/invoices?version=2パラメータを付加する古いAPI。今日ではめったに選ばれない
メディアタイプAccept: application/vnd.example.v2+jsonコンテンツタイプをネゴシエートする原理主義者。管理できる呼び出し元は少ない

URLパスは最も可視性が高く、最も柔軟性が低い。すべての呼び出し元はログの一行を読むだけでどのバージョンにいるかがわかり、バージョンの引き上げは検索置換で済む。コストは、サーフェス全体が一度に動くことだ。すべてのエンドポイントに新しいバージョンを発行せずに一つのエンドポイントの契約だけを変えることはできないため、パスバージョンはまれで、かつ大きなものになりがちだ。

バージョンヘッダーはURLを安定させたまま、何も送ってこない呼び出し元にサーバー側でデフォルトを選ばせることができる。それがGitHubのREST APIバージョンの仕組みだ。X-GitHub-Api-Version内の日付名のバージョンで、サポートされている最も古いバージョンがデフォルトになるため、バージョンを指定しない呼び出し元も壊れない。コストは、バージョンがURLの中では見えず、新しいクライアントで忘れられやすいことだ。

日付付きアカウントバージョンは、ヘッダー方式に一つの追加を加えたものだ。バージョンはアカウントに紐づけて保存されるため、何も送らなくてもすべてのリクエストがそれを受け取る。Stripeのバージョニングは、各アカウントを作成時のバージョンに固定し、リクエストはStripe-Versionでそれを上書きできる。これは最も呼び出し元に優しいスキームであり、運用する側にとっては最も手間がかかる。サポートするすべてのバージョンと現行バージョンの間をサーバーが翻訳しなければならないからだ。

クエリパラメータとメディアタイプはどちらも機能するが、それぞれ違った形で可視性のテストに失敗する。クエリパラメータはURLを組み立てる際に落としやすく、メディアタイプのバージョンは呼び出し元がデバッグに使うほぼすべてのツールから見えない。Stripeの日付ベースのスキームは、日付方式の最もよく知られた例であり、その仕組みはStripeのAPIバージョニングで順を追って説明している。

実際にはどのようにAPIバージョニングを行うか

実際には、バージョンとは名前の付いた振る舞いの集合であり、サーバーは各リクエストをそのうちの一つに対応させる。どのスキームが名前を運んでいても、手順は同じだ。

  1. バージョンはセマンティックバージョンではなく、日付か整数で名付ける。 ウェブAPIはパッケージではない。呼び出し元はURLのマイナーバージョンを固定できないため、v2や2026-08-26は呼び出し元が必要とするすべてを伝える一方、セマンティックバージョニングの番号は、このスキームが果たせない互換性の約束を暗示してしまう。
  2. 気にする必要のないコードパスからバージョンを遠ざける。 バージョンは端で変換層を選ぶべきであり、ビジネスロジックを分岐させるべきではない。コードベースを丸ごと二つ持つことこそ、バージョンが保守されなくなる道筋だ。
  3. すべてのバージョンにデフォルトとドキュメントを与える。 バージョンを送らない呼び出し元は、最新版ではなく常にサポートされている最も古いバージョンを受け取るため、固定していないクライアントはリリース当日に壊れない。それぞれのバージョンには、前のバージョンから何が変わったかを述べたページがある。
  4. サポート期間を設定し、それを公開する。 GoogleのバージョニングガイダンスであるAIP-185は、十分に周知された妥当な移行期間を求め、ベータ機能であっても180日を推奨している。期間を選び、書き留め、バージョンごとに再交渉することなくそれを適用しよう。
  5. バージョンをエンドポイントと同じように引退させる。 期間を過ぎたバージョンは、あらゆる非推奨化されたAPIと同じ扱いを受ける。告知、すべての応答に付くSunsetヘッダー(RFC 8594)、まだそこにいる呼び出し元への半分の時点でのリマインド、そして守られる削除日だ。

RESTのAPIにおけるv1とv2とは何か

v1とv2は、同じサーバーが同時にサポートしている二つの契約の名前だ。v2が存在するのは、v1の中の何かがその呼び出し元を壊さずには変更できなかったからであり、その変更は新しい契約の中に入り、古いものは動作し続けた。番号そのものは、v2が完成しているとかv1が死んでいるとかを何も意味しない。両方ともドキュメントがそう述べているときにのみ真になる。四半期ごとにv3が現れるのは、追加的な変更がバージョン管理されているか、契約がそもそも変化を吸収するように設計されていなかった兆候だ。

これはURLパスによるバージョン管理のモデルであり、バージョン番号は呼び出し元がダイヤルする セグメントだ。gRPCサービスは通常、同じ問題を別の方法で解決する。バージョンは.protoファイル 自体の中のパッケージ名に宿る。gRPCとProtobufはこの違い と、そこではワイヤー互換性がURLの形ではなくフィールド番号によって定義される理由を扱っている。

バージョンの変更は何を告知すべきか

バージョンの変更は、何が壊れるか、誰に影響するか、どう移行するか、そして前のバージョンがどれだけの期間動作し続けるかを告知すべきだ。この項目は、他の破壊的変更の項目と同じ形に、サポート期間を述べる一行を加えたものになる。以下はヘッダーでバージョン管理されたAPIのための例だ。

APIバージョン2026-11-01が利用可能になりました。バージョン2025-06-15は2027年11月1日までサポートされます。 2026-11-01での変更点:GET /invoicesはamountを小数の文字列ではなく、最小単位の整数として返すようになりました。非推奨だったcustomer_nameフィールドは削除され、customerオブジェクトに置き換わりました。2025-06-15を使っていてamountを文字列としてパースしている呼び出し元に影響します。これは2025年6月より前に作成された、バージョンを固定していないクライアントのデフォルトです。移行方法:amountを整数としてパースし、名前はcustomer.nameから読み取ってください。準備ができたらX-Api-Version: 2026-11-01を固定してください。バージョンを固定していない呼び出し元には何も変わりません。

最後の一文が、ほとんどの読者をそこで読み終えさせる文であり、すべてのバージョン告知に含まれるべきものだ。チェンジログの例のページには、このようにバージョン管理しているAPIの項目が集められており、良いものとそれ以外の違いは、たいていこの最後の一行にある。

バージョンが変わったとき、誰に知らされるか

古いバージョンにいる全員に個別に、そしてそれ以外の全員にはチェンジログで。バージョンの変更は、「投稿しておいた」が確実に重要な呼び出し元を取りこぼすケースだ。つまり、二年前にバージョンを固定してから一度もリリースノートを読んでいない人たちだ。彼らが誰であるかは利用データが答えてくれる。通知は、彼らのコードがある場所、つまりレスポンスヘッダーとアカウント所有者へのメッセージに届かなければならない。

私たちが実行しているループでは、バージョンを告知する項目はそれを出荷するpull requestから下書きされ、人間がレビューし、フィードとウィジェットに公開され、そこではバージョン管理されたクライアントがそれをJSONとして読める。その変更を求めた、あるいはそれが修正するバグを報告したウィジェットのフィードバックが、pull requestがクローズするGitHub issueになった人は誰でも、項目が公開されたときにそのissueで知らされる。仕組みはどの項目でも同じであり、バージョンの引き上げは単に最も影響の大きい項目にすぎない。

FAQ

すべてのAPIの変更は新しいバージョンを取得すべきか? いいえ。破壊的変更だけだ。追加的な変更は現行バージョンの下でチェンジログの項目とともに出荷される。追加的な変更をバージョン管理することは、呼び出し元にバージョンを無視するよう教え込む。

URLバージョニングとヘッダーバージョニング、どちらが優れているか? URLバージョニングは呼び出し元にとって見やすく、あなたにとっては少しずつ進化させにくい。ヘッダーバージョニングはその逆だ。多くの小さなクライアントを持つ公開APIでは、URLバージョニングの方が失敗が少ない。変換層を持つ大規模なAPIでは、日付付きヘッダーの方がスケールする。

同時にいくつのバージョンをサポートすべきか? サポート期間が許す限り少なく、決して無制限にはしない。二つか三つの並行バージョンが普通であり、それを超える場合は通常、バージョンが引退させられていないことを意味する。

バージョンを指定しないリクエストは何を受け取るべきか? サポートされている最も古いバージョンだ。既存の固定していないクライアントが動作し続けるようにするためであり、どのバージョンを受け取ったかを伝えるレスポンスヘッダーとともに返される。


この記事の技術的な記述は第三者による確認を受けていません。誤りがあればお知らせください。修正します。

changeloopの関連ページ: 開発者向けドキュメント, changelogの例

changeloop
ループを閉じるchangelogを作っているチームです。ユーザーが何かを求め、あなたのチームがそれを届け、求めた人がそれを知る。