嗨~ 歡迎閱讀第 41 期 ExplainThis 全端雙週報。在開始之前想與讀者們分享,最近我們把 E+ 每個主題挑了一篇主題文開放免費試閱,同時也把一些資源的整理在一起。
目前有免費開放的主題文包含
主要因為先前有讀者詢問 E+ 成長計畫的每週深度主題文,有沒有可以試閱的內容,想說整理出來,讓讀者們可以更了解 E+ 每週主題文具體的內容長什麼樣。如果想了解 E+ 內容與雙週報內容的區別,推薦可以參考。
以上,讓我們進到本期的主題文吧。
[後端] 如何維持 API 的冪等與向後兼容
API 的冪等 (idempotent) 與向後兼容 (backward compatible),是在設計 API 時,非常重要的要點。因此在這期的雙週報,我們將會針對這兩點來討論。之所以說這兩點特別重要,是因為當沒有處理好,會導致後續很多問題。
讓 API 穩定可預測
在前後端的世界中,因為溝通不會隨時都是穩定的,例如客戶端的裝置可能出問題、網路可能中間突然斷掉一小段時間,所以在前後端用 API 溝通時,不能只考慮正常的情境 (俗稱 happy path),而是要進一步去考慮當出特別極端的問題時該怎麼處理 (俗稱 edge case)。當能夠去處理各類極端狀況,API 與整個系統將能夠更穩定。
舉例來說,當今天你用一個電子信箱產品,在編寫完草稿後,按下儲存按鈕,不管按一次,還是按十次,都是執行儲存當下的狀態,不會因為你按了十次,中間網路不穩,就存成十份不同的草稿。如果真的是這樣的話,使用者體驗會不太好。
又或者今天假如使用者在電商網站結帳時,中間網路有一度不穩,使用者等了一下看頁面沒反應,以為自己沒按成功,所以多按了一次結帳,這時不會要讓使用者付兩次款。如果變得要付兩次款,那肯定沒有使用者會想用這個電商。
要能夠做到穩定,冪等 (idempotent) 是很關鍵的要點。所謂的冪等性,是指 API 的呼叫或者操作,不論做多少次,都會是相同的結果;或者換個角度說要做到不論請求幾次,API 都不會產生副作用。當能夠做到冪等性,就能夠確保在重試時,不論重試幾次,都能確保有相同結果,這樣能避免當遇到各類狀況,導致的不必要重複。
事實上,過去業界就有因為沒有處理好 API 冪等,導致造成重大事故的案例。具體來說,先前 Uber 支付組的工程經理 Gergely Orosz 就曾公開分享,當年 Uber Eats 在印度有個重大事故,是某段時間內,綁定印度最大支付商之一 Paytm 的使用者,即使 Paytm 帳戶沒有餘額,也可以無限地在 Uber Eats 上下單。
Gergely 在分享中談到,會出現這個事故,是當年 Paytm 的 API 做了改動。在改動前,Paytm 的 API 一直是維持冪等的,所以 Uber 的支付團隊在串接時,就預設 API 是冪等的,沒有多做處理。
然而 Paytm 在那次看似無害的改動中,沒有維持 API 的冪等性,這造成的問題是,當 Uber 呼叫 Paytm 的 API 時,第一次因為使用者的餘額不足所以回傳原本預期的錯誤,但這時如果使用者再下一次單,Paytm 會回傳另一種錯誤訊息。
兩次回傳不同的錯誤訊息,看似很無害,但是偏偏因為第二種錯誤訊息原本 Uber 團隊不知道,所以沒處理,因此在 Uber 端就讓這種下單通過。而當使用者發現沒餘額時,只要按兩次就變得能下單成功,當時印度各大學迅速傳開,讓 Uber Eats 在短時間被大量下免費的單,而這造成的商業損失非常可觀。
如何讓 API 有冪等性?
相信看完上面的故事,讀者們已經意識到冪等性的重要。如果 Paytm 的 API 在遇到餘額不足,是穩定回傳 Uber 端可以處理的錯誤訊息,意即如果 API 維持冪等就不會出這個事故。
這時下個問題會是如何讓 API 有冪等性?
以 RESTful API 來說,有些請求相對不用擔心冪等性問題。舉例來說,GET
請求就是,因為假如某個資料存在伺服器,不論請求幾次,資料沒變的狀況下,就會都拿到一樣的資料。
PUT
也是,因為 PUT
是一次修改整個資源,假如有多個請求送來,就以最後送到的請求即可。同樣地 DELETE
刪除某個資源後,就沒有該資源,多發幾個過來的結果都是該資源被刪除,所以也是冪等。
然而,我們很常用的 POST
請求會是相對需要特別處理的。就像電商下訂單時的支付,通常會是用 POST
請求。而最常見的冪等處理方式會是加上冪等鑰 (idempotent key)。所謂的冪等鑰,是一個獨特的 id
,讓伺服器端知道這個請求已經被處理過了。
所以如果有網路中斷,或者使用者快速連擊,當同一個請求帶著相同的冪等鑰,伺服器端就知道不用再處理該請求。在系統設計中,遇到追問如何在分散式系統中,避免請求被重複處理,冪等鑰是最基本一定要想到的解法。
舉例來說,全球支付 API 龍頭之一的 Stripe,在 API 設計中,就有冪等鑰的欄位。具體來說,假如要呼叫 CreatePayment
的 API,客戶端可以先產生一組冪等鑰 (例如用 uuid 來產生),這時如果使用者重複點擊,因為帶著的是同一組冪等鑰,所以伺服器端知道已經處理過,就不會重新處理這個支付請求。
先前 Stripe 有一篇《Designing robust and predictable APIs with idempotency》技術文,深入淺出地談了如何透過冪等鑰來提高 API 可預測性,非常推薦一讀。
閱讀更多
在了解完冪等性後,接著我們要談向後兼容 (backward compatability)。由於篇幅有限,這篇在 E+ 的完整內容目前有免費公開,感興趣的讀者,歡迎點此連結閱讀。
[本期推薦]
先前 Google 資深主任工程師 Addy Osmani 曾發文分享,他在 Google 十多年的過程中,協助面試無數優秀的候選人,而其中能夠脫穎而出的,不只是有問題解決能力,還要有「展示」問題解決能力的能力。但是該如何有效具體做好溝通? 我們寫了一篇短文解說 (連結)
在選工作前,推薦先了解該公司如何做事故檢討,因為如何檢討透露了公司真實的文化。Li 先前寫了一篇分享他的個人經驗 (連結)
對於想要邁向資深的人來說,從團隊的視角出發看事情,是非常重要的。舉例來說,資深工程師不僅要做 Code Review,還需要協助團隊建立、優化 Code Review 的規範與流程,以及協助塑造良好的 Code Review 文化。在《如何為團隊建立更好的 Code Review 原則與規範?》談了如何做這件事 (連結)
在現代網頁應用開發中,SPA 是經常會被談到的方式,但什麼是 SPA 呢? 在《What's a Single-Page App?》有很不錯的說明 (連結)
現代前端架構中,微前端是業界普遍會用的架構,而其中透過模組聯邦 (module federation) 實作也是業界趨勢之一《Module Federation with Vite》談了 Vite 的模組聯邦 (連結)。對這個主題感興趣的讀者,也可以讀我們先前寫過的《什麼是微前端架構? 為什麼要用微前端架構?》(連結)
許多程式語言都有 async 與 await 的語法,來處理非同步的程式。但你知道 async 與 await 的運作機制嗎? 《The Mechanics of Async Await》寫得非常詳盡 (連結)
先前看到這個視覺化比較 CPU Cache 與 RAM 在延遲度上的差異,讓人看了一目瞭然。非常推薦這種視覺化,對理解很有幫助(連結)