在前 13 天,我們已經讓專案具備了環境一致性、型別契約、測試藍圖,以及結構化日誌。
專案「能跑、能測、能記錄」。
但現實世界告訴我們一件事:再完美的程式,也不可能零錯誤。
網路 API 可能 timeout、資料庫連線可能失敗、雲端服務可能短暫掛掉。
這不是「例外狀況」,而是日常。
因此,工程化的下一步,就是要讓專案能帶著「失敗」繼續運作。
沒有設計的錯誤處理,往往只有:
try:
call_api()
except Exception:
print("Something went wrong")
問題是:
要真正「工程化」,我們需要一套完整的策略:例外分層 → 重試 → 降級。
良好的錯誤處理,第一步是「分層」。
常見的分類方式:
可恢復錯誤(Retryable)
例如:HTTP 503、資料庫連線中斷、暫時性網路抖動。
不可恢復錯誤(Fatal)
例如:程式碼 bug(KeyError、TypeError)、資料遷移失敗、無法解析配置。
業務錯誤(Business Logic Error)
例如:使用者餘額不足、權限驗證失敗。
設計時應避免一律用 except Exception
,而是針對不同層級自訂例外:
class ExternalServiceError(Exception): ...
class TemporaryNetworkError(ExternalServiceError): ...
class FatalSystemError(Exception): ...
這樣在 log 與監控時,就能區分「需要重試」還是「立即告警」。
tenacity 是 Python 社群常用的「重試框架」。
它透過裝飾器,把複雜的 retry/backoff/jitter 策略包裝好。
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def fetch_data():
print("calling API ...")
raise TimeoutError("service not responding")
fetch_data()
執行時會自動:
👉 好處:
即使重試,還是可能失敗。
這時候要問自己:如果外部服務不可用,系統能怎麼「降級」?
常見做法:
範例:
from tenacity import retry, stop_after_attempt, RetryError
@retry(stop=stop_after_attempt(3))
def call_payment_api():
raise TimeoutError("payment service down")
def safe_call_payment():
try:
return call_payment_api()
except RetryError:
return {"status": "fallback", "message": "Payment service unavailable"}
👉 降級不是偷懶,而是讓「非核心」失敗不會拖垮整個系統。
延續 Day 11(測試策略)與 Day 13(日誌),我們可以把錯誤處理整合進去。
import structlog
from tenacity import retry, stop_after_attempt, before_sleep_log
logger = structlog.get_logger()
@retry(stop=stop_after_attempt(3), before_sleep=before_sleep_log(logger, "warning"))
def fetch_user():
logger.info("fetching user")
raise TimeoutError("service timeout")
輸出 JSON 日誌時,會記錄:
def test_payment_fallback():
result = safe_call_payment()
assert result["status"] == "fallback"
這樣能保證 fallback 策略不會被遺漏。
錯誤不是例外,而是常態。
工程化的 Python 專案,必須在設計上把 「失敗」當成一等公民,才能在不穩定的世界裡保持穩健。
透過 例外分層、重試與降級,我們不只是「寫能跑的程式」,而是「寫能帶著錯誤活下去的系統」。
明天 Day 15,我們將進一步探討 序列化與設定格式:orjson、YAML、TOML 實務,看看如何在資料交換與設定管理上,做到效能與工程化的平衡。 🚀