プロンプトとコンテキスト
オンラインAPIのリソースパスを /v1/orders から /v2/orders へ恒久的に移動する必要があります。呼び出し元にはブラウザのフォーム、モバイルクライアント、サードパーティSDK、非同期ワーカーが含まれ、リクエストは大きなJSONボディと冪等性キー(idempotency key)を伴うPOSTである可能性があります。この移行に向けたステータスコード、クライアントの挙動、可観測性(observability)、ロールバックパスを設計してください。
この質問では、リダイレクトセマンティクスを正確に適用し、APIの移行中に重複した副作用(side effect)を回避できるかが試されます。RFC 9110 では 308 Permanent Redirect が定義されており、MDN では新しいロケーションでリクエストを再送信する際に、クライアントが元のメソッドやリクエストボディを変更してはならないと説明されています。301 も恒久的(permanent)ですが、レガシーなクライアントでは非GETリクエストに対する互換性の差異があり、メソッドを変更してしまう可能性があります。
面接官が評価しているポイント
面接官は、恒久的な移行と一時的な移行を区別した上で、メソッドとボディの保持が必要かどうかを問いかける姿勢を求めています。優れた回答では、301、302、307、308を比較し、副作用を伴うPOSTにおいてクライアントのリトライ方法の推測に依存できない理由を説明し、冪等性キー、認証、タイムアウト、ロールバックまで網羅します。
また、Locationヘッダーの信頼境界、クロスオリジンクレデンシャル、キャッシュの伝播、SDKのリダイレクト上限、308をサポートしていないクライアントへの考慮も期待されています。移行テスト計画なしに「308は301にPOST保持を加えたもの」と暗記した内容を述べるだけでは不十分です。
最初に確認すべき明確化事項
恒久性とスコープ
新しいURLが安定しているか、また移行対象が単一のリソースなのか、APIプレフィックスなのか、異なるオリジンなのかを確認します。ターゲットがまだ変更される可能性がある場合は、クライアントや中間サーバーに恒久的なキャッシュセマンティクスを時期尚早に学習させるのではなく、一時的な移行を表す307を使用します。
クライアントの機能と副作用
ブラウザ、モバイルのバージョン、SDK、キューコンシューマー、パートナーをリストアップします。POSTが注文の作成、決済、メッセージの発行を行うのか、呼び出し元が冪等性キーを送信しているかを確認します。再実行(リプレイ)が安全であるという確証がなければ、リダイレクトは無条件のリトライ指示にはなり得ません。
クレデンシャル、キャッシュ、ロールバック
両方のホストについて、認証スコープ、CORS、プロキシ、CDNの挙動を確認します。古いエンドポイントがトラフィックを処理し続けられるか、またロールバックがリダイレクトの解除、ルーティングの切り替え、古いハンドラーの復元のどれを意味するのかを確認します。
30秒で答える要約
「まず移行が恒久的かどうかを確認し、すべてのクライアントを洗い出します。POSTメソッドとボディを保持する必要がある恒久的な移行には308を使用し、一時的な移行には307を使用します。301は、レガシーなクライアント全体で非GETセマンティクスを保持するための厳格な規約にはなりません。初期フェーズでは古いエンドポイントから新しいハンドラーへプロキシし、すべての副作用に対して単一の冪等性キーを再利用し、Locationホストとクレデンシャルの転送を制限します。カナリアリリース中は、3xxの追跡率、重複作成、4xx/5xx、ボディサイズ、SDKバージョンを追跡します。シグナルが悪化した場合は308の送信を停止し、古いエンドポイントでリクエストを処理できるように維持します」
ステップ別の詳細解説
ステップ1:ステータスコードを正確に選択する
308は恒久的な移動を意味し、リクエストメソッドとボディを保持します。307は一時的であり、同様にこれらを保持します。301は恒久的ですが、すべてのレガシーなクライアントがPOSTなどのメソッドを保持するわけではないため、POST移行の厳格な規約にはなりません。302もその保証を持つべきではありません。
ステップ2:リプレイを最大の制約として扱う
308によってクライアントがボディ全体を再送信する可能性があるため、両方のエンドポイントが同一の冪等性キーによって同じビジネスコマンドを認識する必要があります。副作用を実行する前に、サーバーはリクエストダイジェストに対してキーを検証します。異なるパラメータで同じキーが送られた場合は、2回目の注文を作成するのではなくコンフリクト(競合)とします。クライアントはタイムアウト後も自身のリトライバジェットに従い、308を無限に追跡してはなりません。
ステップ3:段階的にリリースして観測する
古いアドレスを監視可能なプロキシまたは308レスポンス経由でルーティングする前に、新しいアドレスが直接機能することを確認します。SDKのバージョン、送信元オリジン、メソッドごとにカナリアリリースを実施します。リダイレクトチェーンの長さ、追跡失敗、重複した副作用、ターゲットのレイテンシ、認証失敗、ボディサイズを記録します。308をサポートしていないクライアントに対しては、暗黙のうちに301へ切り替えて同等の挙動を期待するのではなく、短期間のサーバーサイドプロキシを維持します。
ステップ4:セキュリティ、キャッシング、ロールバックを処理する
Locationヘッダーは、許可リスト(allow-list)に登録されたターゲットのみを指すようにします。クレデンシャルが信頼できないホストに到達しないよう、クロスオリジンのホップ前にCookie、Authorization、CORSを再評価します。CDNおよびクライアントのキャッシュ期間を明示し、検証中は短く設定して徐々に延長します。ロールバック時は、新しいリダイレクトの発行を停止し、古いエンドポイントが同じ冪等性キーを受け入れられるようにします。新しいエンドポイントですでに書き込まれた結果は、単にルーティングを元に戻すだけでは『取り消す』ことができません。
質の高い模範解答
私はこれをステータスコード当てではなく、プロトコルと移行の設計課題として捉えます。POSTメソッドとボディを保持する必要がある恒久的な移行には308を選択し、一時的なトラフィックシフトには307を選択します。301は多くのWebページURLの移動には適していますが、非GET APIに必要な厳格なメソッド保持の保証は得られません。
ロールアウト前に、両方のURLで同じ認証、リクエスト検証、冪等性キーの規約をサポートします。古いエンドポイントはまず完全なログを伴って新しいハンドラーへプロキシし、同一のキーとリクエストダイジェストからは1つの副作用のみが生成されるようにします。その後、クライアントバージョンごとに308をカナリア展開し、ターゲットホストを制限し、クロスオリジンクレデンシャルを再確認し、CDN、SDK、キューコンシューマーの挙動を検証します。
リダイレクト追跡率、チェーンの長さ、重複作成、ターゲットエラー、認証失敗、ボディサイズをモニタリングします。古いクライアントが308を理解できない場合は、暗黙的に301へダウングレードするのではなく、古いエンドポイントのプロキシを維持します。インシデント発生時は308の出力を停止し、古いエンドポイントと冪等性レコードを保持し、ルーティングを復元した上で、完了した処理結果をリクエストIDによって突合(reconcile)します。
よくある間違い
- すべての移行で301を返す → レガシーなクライアントがPOSTを別のメソッドに変更したり、ボディを破棄したりする可能性がある → 保持が必要な恒久的なAPI移行には308を使用し、クライアントを検証する。
- 308を受け取った後、副作用を再度実行する → リダイレクト、タイムアウト、クライアントのリトライによって作成リクエストが増殖する可能性がある → 冪等性キー、リクエストダイジェスト、制限付きリトライバジェットを使用する。
- 307を恒久的なものとして扱う → 一時的な状態がキャッシュやSDK設定に長く残ってしまい、ロールバックが困難になる → 一時的なシフトには307を使用し、安定化を確認した後にのみ308を決定する。
- クロスオリジンにおけるLocationのリスクを無視する → CookieやAuthorizationが信頼できないターゲットに届く可能性がある → 許可リスト、クレデンシャルポリシー、CORSを併せて検証する。
- 3xxのカウントのみを監視する → 古いSDKの追跡失敗や重複書き込みが見えなくなる → クライアントバージョンごとに追跡率、エラー、副作用の結果を結合して分析する。
フォローアップ質問
フォローアップ1:なぜ古いURLから200を返し、ボディ内で新しいURLを伝えないのですか?
それでは一般的なクライアント、キャッシュ、SDKが自動的に移行できず、リソースが恒久的に移動したことを表現できません。移行期間中にサーバーサイドプロキシを残すことは可能ですが、移行規約としては明示的なステータスとLocationヘッダーを提供し、呼び出し元が実際に移行したかを記録する必要があります。
フォローアップ2:308はAuthorizationヘッダーを変更せずに新しいホストへ転送できますか?
デフォルトでは転送されません。まず両方のホストが信頼境界を共有していることを確認する必要があります。そうでない場合は、クライアントがターゲットホスト用のクレデンシャルを取得するか明示的に送信するようにします。オープンリダイレクトやクレデンシャル漏洩を防ぐため、サーバーはユーザーが制御可能なLocationの値を拒否しなければなりません。
フォローアップ3:古いクライアントが308をまったくサポートしていない場合はどうしますか?
古いバージョンが廃止されるまで、古いエンドポイントのサーバーサイドプロキシを維持するか、判明しているクライアント機能に基づいて互換性レスポンスを返します。プロキシはリクエストの冪等性キーを再利用し、廃止日を設定する必要があります。確証なしにすべてのクライアントが308を301と同様に処理すると決めつけることはできません。
フォローアップ4:ロールバックは単に308を200に戻すだけで済みますか?
新しいエンドポイントによってすでに書き込まれた注文、イベント、監査ログの突合も必要です。新規リダイレクトを停止して古いエントリーポイントを復元し、書き込みが分岐したり重複したりしないように両方のパスが同じ信頼できる情報源(Single Source of Truth)を参照するようにします。ルーティング復元後、リクエストIDと冪等性キーを用いて完了した副作用を検証します。