1. 題目與適用場景
PATCH /profiles/42 已經運行多年。成功時伺服器回傳 200 OK,回應本體固定為 { "success": true }。Web、行動端、第三方軟體開發套件(Software Development Kit,SDK)和自動化任務都在呼叫它。團隊發現這段 JSON 沒有業務資訊,準備改為 204 No Content。
難點不在修改狀態碼。舊用戶端可能對每個 2xx 回應都呼叫 response.json(),閘道可能按回應本體擷取欄位,監控也可能把 200 當成唯一成功狀態。遷移方案必須讓新舊用戶端同時運作,並能在異常擴大前立即回復原契約。
2. 面試官考察點
- 能否把狀態碼變更視為回應契約遷移,而非一行伺服器程式碼。
- 能否列出瀏覽器、行動端、SDK、代理、監控與重試器等真實消費者。
- 能否設計版本化或
Prefer協商,讓 200 與 204 在遷移期共存。 - 能否提出灰度指標、停止條件和不需用戶端回退的伺服器回復路徑。
- 是否知道 204 到回應標頭結束即終止,不能攜帶內容或 trailer。
3. 遷移前要確認什麼
- 哪些用戶端會無條件解析 JSON,哪些只檢查
response.ok或 2xx? - 成功物件是否真的無人讀取,包括日誌蒐集、閘道腳本和產生的 SDK?
- 請求是否可能被用戶端自動重試;解析失敗會不會把一次成功寫入誤判為失敗並重複送出?
- 團隊能否升級所有用戶端,還是必須長期保留雙契約?
- 回應中的 ETag、速率限制與追蹤標頭是否仍需保留?
4. 30 秒回答框架
我會先建立消費者清單,用契約測試找出所有依賴 200 JSON 的程式碼。遷移期保留 200 為預設,新用戶端透過新版本或 Prefer: return=minimal 明確選擇最小回應;伺服器採用後回傳 204,並以 Preference-Applied 表明協商結果。接著按內部呼叫、低風險用戶端和主要流量分批放量,監控解析例外、重複寫入、重試率與各用戶端成功率。回復只需關閉伺服器的 204 分支,因為產生 200 JSON 的能力在遷移結束前一直保留。
5. 分階段遷移方案
第一步:建立用戶端能力清單
按呼叫方記錄負責人、版本、請求函式庫、成功判定、回應解析和重試策略。重點搜尋 response.json()、固定 status === 200、產生 SDK 的回傳型別,以及閘道對 body.success 的讀取。對無法識別版本的呼叫方,先維持 200;沒有能力證明,就不進入 204 灰度。
第二步:讓用戶端相容兩種成功契約
用戶端先發布相容版本:接受約定的 2xx 狀態,在讀取內容前檢查 204 或內容長度,並把業務成功與 JSON 解析分開。契約測試同時送入 200 + JSON 和 204 + 空本體。這一步必須先於伺服器切換,否則一次成功寫入可能在用戶端表現為解析失敗,進而觸發重複請求。
第三步:選擇雙契約方式
如果這是明確的破壞性版本升級,可以讓新 API 版本固定回傳 204,舊版本繼續回傳 200。若路徑和語意都要維持,可以採用 RFC 7240 的偏好協商:相容用戶端送出 Prefer: return=minimal,伺服器採用後回傳 204,並回傳 Preference-Applied: return=minimal;需要資源表示的呼叫方送出 Prefer: return=representation,或繼續使用預設 200。若回應可能被快取,還要正確宣告 Vary: Prefer。
第四步:按用戶端而非隨機請求灰度
先開放測試環境與內部呼叫,再按已確認相容的用戶端版本擴大範圍。灰度單元應穩定落在用戶端或帳號層級,避免同一個用戶端一會兒收到 200、一會兒收到 204。每一檔放量都要觀察完整業務週期,再決定是否繼續,而非只看 HTTP 成功率。
第五步:監控協定變更帶來的真實故障
伺服器按用戶端版本記錄 200、204、5xx 和重試次數;用戶端回報空本體解析例外、成功請求後的錯誤提示與重複送出。業務側核對寫入成功數和請求重試數,特別關注非冪等操作。告警要能定位到用戶端版本與發布批次,只看整體 2xx 會把相容問題藏起來。
第六步:準備即時回復與最終收口
遷移期保留原 JSON 產生路徑,由伺服器開關控制 204。觸發停止條件後,統一恢復 200;已相容兩種回應的新用戶端無需回退。等所有受支援用戶端跨過最低相容版本、舊流量降為零並完成一個穩定觀察週期後,再決定是否刪除舊路徑。刪除前重新執行 SDK、代理與契約測試。
6. 高品質示範回答
我會把這次改動當成回應契約遷移。先盤點所有消費者,找出無條件解析 JSON、固定判斷 200 或依賴body.success的程式碼,並先發布能同時處理 200 JSON 與 204 空本體的用戶端。伺服器暫時保留 200 預設;新用戶端透過 API 版本或Prefer: return=minimal選擇 204,伺服器用Preference-Applied確認。灰度按用戶端版本推進,重點觀察解析例外、重複寫入和重試,而非只看 2xx。原 200 產生路徑在遷移期保留,一旦超過停止閾值就關閉 204 分支。確認所有受支援用戶端完成升級後,才移除舊契約。
7. 常見錯誤
- 直接把 200 改成 204 → 舊用戶端解析空本體失敗 → 先發布雙相容用戶端,再啟用新回應。
- 只檢查 HTTP 錯誤率 → 204 仍是成功狀態,解析例外不會進入伺服器 5xx → 增加用戶端解析、重試和重複寫入指標。
- 按請求隨機灰度 → 同一用戶端得到不穩定契約 → 按用戶端版本或穩定主體分組。
- 使用
Prefer卻不回報是否採用 → 用戶端無法確認協商結果 → 回傳Preference-Applied,並定義伺服器忽略偏好時的預設行為。 - 切換後立即刪除 JSON 產生邏輯 → 出現問題時無法快速恢復 → 等遷移窗口關閉後再清理舊路徑。
- 忽略自動重試 → 成功寫入被解析錯誤偽裝成失敗 → 檢查冪等鍵、重試器和重複送出指標。
8. 追問及應對
追問一:為什麼不能一次切換所有用戶端?
伺服器只知道請求成功,無法保證每個用戶端都能按 204 處理空本體。只要還有一個舊版本無條件解析 JSON,一次切換就會把協定成功變成使用者可見失敗。先做用戶端相容,再按已知版本切流,故障邊界才清楚。
追問二:版本化和 Prefer 該怎麼選?
新版本適合長期固定的新契約,理解成本低,但需要維護版本生命週期。Prefer 適合同一操作允許「回傳表示」與「最小回應」兩種合法結果的場景;伺服器可以忽略偏好,因此用戶端必須定義預設處理,並讀取 Preference-Applied。兩者都比隱藏式按 User-Agent 猜測可靠。
追問三:204 還能保留哪些資訊?
可以保留 ETag、追蹤識別碼和速率限制等回應標頭;不能傳送訊息內容或 trailer。用戶端也不能把「沒有 JSON」理解成「沒有中繼資料」,應分別讀取所需回應標頭。
追問四:什麼情況可以刪除 200 相容路徑?
至少滿足三項:所有受支援用戶端都已發布雙相容版本;觀測中沒有舊版本流量;SDK、代理、自動化任務和回復演練全部通過。只看應用程式商店的新版本發布完成還不夠,因為使用者可能長期停留在舊版本。