論點聽起來很合理:程式碼行數越少,bug 就越少。更容易審查、更容易推理、缺陷面也更小。聽起來很棒。這是真的。但這也不完整。

問題從後端開發者把正式環境系統當成作業題目時就開始了。在單程序應用程式中:

你能控制執行流程。你知道順序。執行緒可能會競爭,但至少它們共享同一份記憶體與時鐘。

一旦你的 API 開始跟資料庫溝通、Webhook 在午夜觸發、非同步工作在佇列上排程,以及負載平衡器後面有三個副本,失敗模式就會倍增:連線中斷、訊息亂序到達、時鐘彼此不一致,還有部分失敗會在週二凌晨 3 點冒出來。

刪減程式碼不會讓這些問題消失。它只會把複雜度藏起來,直到某天出事。

當極簡程式碼遇上正式環境

想想看,當你的極簡傑作碰上現實會發生什麼:

你的服務暫時與資料庫中斷連線 30 秒。你的程式沒有 timeout 邏輯。請求卡住。使用者重新整理。更多請求堆積起來。最後某個地方壞掉了。

你以為「那種事大概不會發生」,所以兩個執行個體處理了同一個 Webhook。沒有 idempotency key,所以扣款執行了兩次。你的資產負債表現在多了 50,000 美元。你的會計很困惑。你的主管比較不困惑。

某個 worker 在操作中途當機。沒有復原機制。交易被放棄在不一致的狀態。你的資料現在處於違反所有你對它應該長成什麼樣子的假設的狀態。

某個下游短暫異常後引發重試風暴,因為沒有任何機制去退避或去重,結果把你的 API 轟炸到超出負載。Rate limit 被觸發。正常流量被丟棄。你正在排查一場因為程式碼「有處理錯誤」——只是記錄後回傳——而造成的當機。

這些都不是靠少寫程式碼就能避免的。它們是靠你原本跳過、覺得多餘的那些無聊防護機制來避免的。

正式環境真正需要什麼

現代後端系統需要一些簡單應用程式從沒想過要處理的防護機制:

冪等性。 每個操作都必須能安全重試。付款 Webhook 被重新送達、佇列訊息被處理兩次、或客戶端在 timeout 後重試——這些都需要某種方式辨識「已經做過了」。操作 ID、版本號、去重 key。不好看,但必需。

Timeout。 對其他服務的請求需要有期限。沒有它們,連鎖故障會悄悄發生,並逐漸吃掉你所有資源。你的程式只會一直等下去,就像永遠接不通的電話。

補償邏輯。 當多步驟操作在中途失敗時,必須有人撤銷已經完成的工作。你不能把一個半完成的 saga 丟著不管,然後希望沒人發現。這比假設一切成功要多寫不少程式碼。大家還是照樣跳過。

衝突偵測。 當兩個寫入者同時碰到同一筆資料——兩個 API 執行個體、或重試與原始請求重疊——你需要版本檢查、時間戳記,或樂觀鎖。假裝衝突不存在,直到兩個更新以錯誤順序落地為止。

可觀測性。 當事情出錯時,能讓你重建發生了什麼的日誌、指標與追蹤。在凌晨 3 點,你會希望它存在。當事情壞掉而你沒有任何 logs 時,你就會明白這有多重要。

你不能把這些刪掉,然後稱之為簡化。你只是把複雜度從編輯器移到你的值班輪班表上而已。

少程式碼 vs. 少雜訊

刪掉多餘的抽象、死碼與投機性的框架。這是好習慣。

但因為 retry 包裝器、驗證、熔斷器或冪等性檢查「增加雜訊」就把它們刪掉,這是另一回事。

你是在把穩定性押在你無法控制的相依性上。當資料庫短暫抖動、合作夥伴 API timeout,或 Kubernetes 在請求進行到一半時重新排程 Pod,系統並不會變得更簡單。它只會變得錯誤。

測試

如果你的應用程式會跑超過一個執行個體、會跟其他服務溝通,或會非同步處理工作,這些問題最終都會變得很重要:

  1. 如果某個程序在操作中途死亡,系統能否偵測並正確復原?
  2. 如果某個訊息延遲了幾秒,實際會發生什麼事?
  3. 如果兩個 worker 同時嘗試執行同一個操作,結果是可預測的,還是靠擲硬幣決定?

如果你無法用具體機制回答這三題——不是憑感覺,不是「我們會在正式環境修」——那這個程式碼庫就不簡單,而是脆弱。

把防護機制寫上去,處理那些失敗模式。目標不是為了多寫程式碼而多寫;而是在正式環境替你揭露之前,先把隱藏的複雜度攤在陽光下。


原文出處:https://dev.to/adamthedeveloper/minimal-code-doesnt-mean-stable-code-4mbd


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝21   💬4   ❤️1
617
🥈
alicec
📝1   ❤️2
86
#4
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登