🎯 什麼情境該想到我

分工:這張是判準卡。本例中 MQ 消費端的正確做法 → 工具-MQ消費端防禦三原則;從生產端根治 → 工具-Transactional-Outbox-Pattern。

當你在 review 一個「看起來合理」的修法(upsert、retry、try-catch、加鎖…),想判斷它是真的修好還是只是把症狀藏起來時。

⚙️ 怎麼用

  1. 先問「為什麼這裡會有這個問題?」 而不是「這個修法對不對」。
    • 例:不是問「upsert 寫得對嗎」,而是「為什麼這裡會需要 upsert」。
  2. 順著症狀跨系統往上追因果:worker 症狀 → MQ 時序 → sender 的 transaction 邊界。
  3. 判斷準則:如果這個修法讓 error 消失、但根本的錯誤狀態仍然存在(只是不再噴錯),它就是掩蓋,不是修復。
  4. 掩蓋型修法要擋下來——症狀消失後就沒人會再發現真正的問題。

★ 常見的「看起來像修復」的做法,怎麼判斷

做法什麼時候是掩蓋什麼時候是修復
upsert(查不到就新增)資料「應該已經存在」卻查不到——你在用新資料蓋掉一個時序錯誤業務上本來就允許「第一次寫入」與「後續更新」走同一條路
retry錯誤是確定性的(邏輯錯、資料錯),重試只是延後失敗錯誤是暫時性的(網路抖動、複本延遲),而且有上限與退避
try/catch 吞掉錯誤吞掉之後回傳一個「看起來正常」的值在系統邊界把錯誤轉成明確的回應,並記錄下來
加鎖用一把大鎖蓋住一個不該並發的設計保護一個確實需要互斥的共享狀態,且範圍最小
加大 timeout真正的問題是慢查詢或死結下游的正常延遲分布本來就比原設定長
重啟服務記憶體或連線洩漏,重啟只是讓它晚點再爆一次性的損壞狀態,且已經找到並修掉原因

★ 四個檢查問題

  1. 錯誤狀態還在嗎? 修法讓錯誤訊息消失了,但系統裡的資料或狀態仍然是錯的——那就是掩蓋。
  2. 能解釋為什麼會發生嗎? 講不出因果鏈(「A 在 B 之前發生,因為……」),就還沒找到根因。
  3. 下一個人還看得到問題嗎? 修法讓問題在監控、日誌、測試裡都消失了——之後就沒有人會再發現它。
  4. 換一個輸入還會發生嗎? 寫一個能重現原問題的測試;修復之後它應該通過,而且是因為對的理由通過。

★ 緊急止血時的正確做法

事故當下,掩蓋型的做法可能是對的——先讓使用者不再受影響。但要同時:

  • 在程式碼裡標清楚:// TODO(#1234): 暫時的 upsert,根因是 sender 在 commit 前發送訊息。
  • 開一張追蹤根因的工單,並在事後檢討中列為行動項(見 工具-無指責事後檢討)。
  • 讓暫時的做法可觀測:每次走到那條補丁路徑都記一筆指標,才知道根因修好後它是否真的不再被觸發。

🧪 我實際套用的紀錄

  • 2026-07-13:(待填)

⚠️ 注意 / 什麼時候不適用

  • 緊急止血時可先用掩蓋型修法擋住,但必須開票追 root cause,別讓它變成永久補丁。
  • 核心心法:不要比 AI 更會 coding,要比它更會 reasoning。

🔗 相關工具