回到文章列表
    疑難雜症·2026-07-24

    那個 Bug 只在星期一早上出現

    詭異的 bug 往往不是程式碼寫錯,而是環境、時間、快取或第三方服務在特定條件下露出破綻。與其猜,不如建立可重現的觀察路徑,讓 bug 自己現形。

    那個 Bug 只在星期一早上出現

    「這個 bug 只在星期一早上出現」——當工程師講出這句話,八成不是他們在推卸,而是系統真的在某個特定條件下才會壞。而這種 bug,通常比 NullPointerException 難處理十倍。

    這類疑難雜症的共同點是:它不是程式邏輯錯,而是環境、時間、狀態或第三方服務的某個組合出了問題。你在本機跑一百次都對,一上正式環境就壞;或者上線三個月都好好的,某天忽然開始間歇性 500。這時候一味 code review 是沒用的,得換一套思路。

    先建立「可重現」,再談「修好」

    很多團隊面對詭異 bug 的第一反應是:翻 code、猜原因、丟一版上去試。這種做法的問題在於,如果 bug 本身無法穩定重現,你根本不知道下一次它不出現是因為修好了,還是只是條件沒湊齊。

    更務實的順序是:

    1. 先鎖定觸發條件。收集所有出問題時的 log、時間戳、使用者、輸入資料、上游服務狀態。列一張表比對,找出交集。
    2. 在測試環境重現。如果重現不出來,代表你對觸發條件的理解還不夠。這時候與其硬修,不如加更多觀測點(tracing、結構化 log、metric),等它下次出現時把現場保留下來。
    3. 確認假設後才動 code。改一行程式的成本不高,但改錯方向、讓 bug 潛得更深的成本很高。

    舉個例子(假設情境):一個電商 API 偶爾回傳空的商品清單,但重打就正常。如果直接猜是資料庫連線問題然後加 retry,可能真正的原因其實是快取層在 key 過期的瞬間、與後端更新之間有 race condition,retry 只是剛好避開那個窗口,bug 還在,只是更難被抓到。

    幾個常見的「元兇類別」

    實務上,難搞的 bug 大多不脫這幾類:

    • 時間與時區:跨時區的 job、日光節約、UTC 與本地時間混用。程式在午夜、月底、閏年、週一凌晨忽然壞掉,先往這邊查。
    • 快取一致性:多層快取(CDN、應用層、DB 層)之間狀態不同步。特徵是「重新整理就好了」或「換一台機器就正常」。
    • 第三方 API 的默默變更:對方沒發公告改了回傳格式、加了 rate limit、或某個欄位從必填變選填。你的 log 只會看到解析失敗,追不到根因。
    • 併發與資源競爭:連線池、檔案 lock、資料庫 transaction isolation。在流量低時完全看不到,一到尖峰才炸。
    • 設定漂移:正式環境和測試環境的環境變數、feature flag、憑證版本不一致。

    面對這幾類,加 log 的位置很關鍵。與其在每個函式進出都印,不如在「跨邊界」的地方——呼叫第三方前後、快取讀寫前後、transaction 開始與結束——加上結構化的 trace id。這樣一份 log 撈出來就能看到整條路徑,而不是一堆片段。

    # 示意:跨邊界處加上 trace,實務請搭配 OpenTelemetry 等工具
    logger.info("cache.read", key=key, hit=hit, trace_id=ctx.trace_id)
    resp = external_api.call(payload)
    logger.info("external.call", status=resp.status, latency_ms=elapsed, trace_id=ctx.trace_id)
    

    我們的觀察

    難搞的 bug 幾乎都在提醒你一件事:這個系統對自己的狀態沒有足夠的觀測能力。與其把時間花在事後猜謎,不如平時就在關鍵邊界埋好 trace 與 metric。真正省時間的除錯,是讓下一個 bug 出現時,log 已經幫你寫好了半份報告。

    #除錯#工程實務#效能調校

    下一步

    需要把這裡的觀點落地到自家系統?

    協和數位專做客製化電商、AI Agent 與雙平台 APP。第一次諮詢免費,會幫你拆解可行性與優先順序——即使最後沒合作也沒關係。

    寫信給我們 →