iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Claude AI

Claude Code陪跑:一人開發者的訂閱制SaaS架構與十大地雷實戰記系列 第 11

# Day 11|地雷#8:錯誤訊息型別錯了,前端顯示直接壞掉那次

  • 分享至 

  • xImage
  •  

事故現場

App 使用者回報截圖:畫面上的錯誤提示顯示著 [object Object]

任何寫過 JavaScript 的人看到這串字都知道發生了什麼:一個物件被硬轉成字串顯示了。但這次的問題嚴重在——回報的使用者用的是已經上架、我無法即時更新的 App 版本。

根因解剖

後端某個 API 的錯誤回應,原本的格式是:

{ "detail": "額度已用完,升級方案可提高上限" }

某次改版時,為了讓前端能做更細緻的處理,我把它改成了結構化格式:

{ "detail": { "code": "QUOTA_EXCEEDED", "message": "額度已用完", "upgrade_url": "..." } }

網頁版同步改了解析邏輯,沒事。但已上架的 App 版本,程式碼裡寫的是直接把 detail 當字串顯示——它收到一個物件,於是使用者看到 [object Object]

核心教訓:已上架的客戶端是你甩不掉的 API 消費者

網頁版隨時可以改、可以同步部署,但 App 走商店審核、使用者更新意願不一,舊版本會在使用者手機裡存活數月甚至數年。這代表:

任何會被 App 讀取的 API 欄位,它的型別跟語意是一份長期契約。你改的不是「自己的程式碼」,是所有歷史版本共同依賴的介面。

後端工程師的直覺是「前後端一起改就好」——這個直覺在有原生 App 的架構裡是危險的。正確的心智模型是把已上架版本當成一個「你永遠無法要求它改碼的第三方消費者」。

防線怎麼蓋

第一道:欄位型別用測試釘死。 專案裡加了一支測試,明確驗證所有錯誤回應的 detail 欄位必須是字串。任何人(包括 agent、包括未來的我)改動這個欄位的型別,CI 直接紅燈,並且測試名稱會告訴你為什麼:

def test_error_detail_must_be_string():
    """已上架 App 版本直接把 detail 當字串顯示,
    塞物件會變 [object Object](地雷清單 #8)。"""
    resp = client.post("/api/some-endpoint", ...)
    assert isinstance(resp.json()["detail"], str)

第二道:擴充用加法,不用改法。 需要結構化資訊,就新增欄位(detail_meta),舊欄位保持原型別不動。舊版 App 讀舊欄位,新版讀新欄位,兩者並存。

第三道:寫進地雷清單。 條目寫的是後果而不是規則:「402 類錯誤回應的 detail 必須是字串——已上架的 App 版本直接顯示它。」

通則

介面的相容性承諾不是對「現在的消費者」,是對「所有還活著的消費者」。你控制不了消費者什麼時候死,所以承諾預設是永久的——這就是為什麼 API 設計裡「加欄位」永遠優於「改欄位」。


上一篇
# Day 10|地雷#7:圖表元件的時間顯示邏輯,8小時錯覺怎麼抓出來
下一篇
# Day 12|地雷#9:一個布林旗標,擋住了多階訂閱等級的擴充
系列文
Claude Code陪跑:一人開發者的訂閱制SaaS架構與十大地雷實戰記15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言