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

    那些看起來像 bug、其實是設定的鬼故事

    很多讓人抓狂的 bug 最後都不是程式碼寫錯,而是環境、時區、快取、連線池這些「以為預設值沒問題」的地方在作怪。這篇整理幾個常見排查方向,幫助你在下次遇到詭異現象時,少走幾條冤枉路。

    那些看起來像 bug、其實是設定的鬼故事

    「程式在我電腦上是好的」這句話背後,通常藏著三種鬼。 一種是環境變數不一致、一種是時區與編碼、還有一種是那些沒人動過但預設值不對的設定檔。真正難查的 bug,很少是演算法錯,多半是這類「以為理所當然」的地方。

    以下整理幾個實務上反覆出現的排查方向,不是完整清單,只是想提醒:遇到怪現象時,先別急著改程式碼。

    訊號往往在邊界,不在邏輯裡

    舉個例子,假設一個電商後台在測試環境跑得好好的,一上線就偶發性地把訂單建立時間存成前一天。第一直覺會想是不是排序寫錯、或是資料庫欄位型別不對。但如果多看幾筆資料,會發現「異常訂單」都集中在深夜到清晨那段時間——這時候就要懷疑時區。

    常見的坑包括:

    • 應用程式用 UTC,資料庫用本地時區,ORM 幫你「貼心地」轉了一次。
    • Docker 容器沒設 TZ,預設就是 UTC。
    • 前端用瀏覽器時區顯示,後端用伺服器時區寫入,兩邊對不齊。

    類似的邊界問題還有字元編碼(客戶資料含中文姓名或特殊符號時特別容易爆)、換行符(Windows 開發、Linux 部署)、以及浮點數精度(金額千萬別用 float)。這些都不是「bug」,而是「你沒設定,系統就幫你選一個」的結果。

    排查訣竅:當現象與時間、地區、特定字元、特定數值範圍相關時,先去看設定和環境,不要先看邏輯。

    效能問題:慢的通常不是你以為的那段

    另一個經典情境:某個 API 反應變慢,工程師直覺去優化最複雜的那段 SQL,改了半天沒改善。這時候如果願意加上 tracing,很可能發現真正的瓶頸在別處——例如連線池被打滿、外部服務 timeout 重試、或是某個中介層做了不必要的序列化。

    幾個值得優先檢查的方向:

    • N+1 查詢:ORM 用得太順手時最容易發生,看似一行程式碼,實際打了大量 query。
    • 連線池大小:預設值通常偏保守,高併發時會變成排隊瓶頸。
    • 同步呼叫外部 API:一個慢的第三方服務可以拖垮整條請求鏈路。
    • 日誌與監控本身:同步寫入大量 log 或送 metrics 到遠端,也可能是元凶。

    下面是個示意用的最小骨架,說明如何把可疑區段用時間標記包起來,不用先導入完整 APM:

    import time, logging
    
    def timed(label):
        def deco(fn):
            def wrap(*a, **kw):
                t = time.perf_counter()
                try:
                    return fn(*a, **kw)
                finally:
                    logging.info(f"{label} took {time.perf_counter()-t:.3f}s")
            return wrap
        return deco
    
    # 示意:實務上建議接 OpenTelemetry 或現成 APM
    @timed("fetch_orders")
    def fetch_orders(user_id):
        ...
    

    把可疑的幾個區段都包起來,跑一次真實流量,你會很快看到哪一段吃掉最多時間——通常和你事前的假設不一樣。

    我們的觀察

    累積下來的經驗法則是這樣:遇到詭異 bug 時,先問「什麼變了」——部署、設定、依賴版本、資料量、使用時段。真正純粹的邏輯錯誤反而是少數,多數的疑難雜症都躲在環境與邊界條件裡。願意花時間讀 log、看 trace、對照設定的人,通常比會寫演算法的人更早找到答案。

    #除錯經驗#系統整合#效能調校#後端開發

    下一步

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

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

    寫信給我們 →