第三部到 Day 23 結束,今天正式進入第四部:實戰案例與總結。接下來幾天,會用幾個真實踩過的坑,具體示範前面 23 天講的原則在實戰裡怎麼運作。
第一個案例的教訓很簡單,但很容易踩:AI 第一直覺的診斷方向,只要能解釋「目前查到的現象」,就很容易被當成「正確答案」——但「能解釋部分現象」跟「就是正確答案」,中間還有一大段距離。
問題最初的樣貌是這樣:某個寫入流程,偶爾會出現「應該產生兩筆獨立紀錄,卻只剩一筆」的情況,而且沒有固定的重現方式——同樣的操作,重跑十次可能只有一兩次會出問題。
這種「不穩定重現」的症狀,很容易讓人(不管是 AI 還是人類工程師)第一時間往「時序問題」的方向想:兩個請求幾乎同時抵達,會不會是某個地方沒有做好鎖定,導致其中一個寫入被另一個蓋過去了?這個猜測看起來合理,也確實能解釋「不穩定重現」這個現象——時序問題本來就是這種若隱若現的樣子。
AI 沿著這個方向查下去,很快就能舉出「這裡確實存在請求交錯的可能性」,並且準備了一個對應的修法:在寫入前加一道鎖,確保同一時間只有一個請求能進行這段邏輯。這個修法邏輯自洽,也確實能讓症狀出現的頻率降低——因為加鎖本身會讓請求變成排隊處理,本來就會減少某些時序敏感的問題。
但「症狀出現頻率降低」不等於「根因被解決了」。 這正是這個案例最容易讓人掉進去的陷阱:加鎖之後,問題確實變少了,如果就此停手,會誤以為診斷是對的、修法是有效的——但實際上,問題只是變得更難重現而已,根因完全沒有被觸碰到。
問題在於,即使加了鎖,極少數情況下同樣的症狀還是會出現——這是一個「診斷沒有辦法解釋全部現象」的訊號,只是因為頻率降到很低,很容易被當成殘留的邊界情況,而不是「根本診斷方向錯了」的證據。
用一組對照來看這兩種處理方式:
❌ 在錯誤方向上加鎖:
「症狀像時序問題,加一道鎖限制併發,
症狀頻率確實降低了,看起來有效,先這樣。」
→ 「頻率降低」被誤認為「根因解決」,
但少數殘留案例其實在告訴你:診斷方向可能不對
✅ 往回查,直到能解釋全部現象:
「加鎖之後症狀降低但沒有消失,
代表這不是純粹的併發時序問題,
回頭檢查寫入邏輯本身:這兩筆本該獨立的紀錄,
資料庫層面是怎麼判斷『這是不是同一筆』的?」
→ 追問到寫入邏輯依賴的唯一鍵設計,
發現唯一鍵的組成欄位不夠精確,
導致本該是兩筆獨立資料的寫入,被誤判成同一筆而覆蓋
往回查之後發現:問題根本不在時序,而在資料庫的唯一鍵約束——唯一鍵的組成欄位設計得不夠精確,兩筆邏輯上獨立的資料,在唯一鍵的視角下被誤判成同一筆,第二筆寫入因此覆蓋或被資料庫忽略了第一筆。這件事跟時序完全無關,加鎖只是恰好透過「讓請求排隊」這個副作用,稍微降低了兩筆資料在時間上撞在一起的機率,但完全沒有修正唯一鍵設計本身的問題——所以極端情況下,就算排隊,兩筆資料還是可能在同一個處理窗口內撞上,症狀就會重現。
AI 給出的『已確認是時序問題』這個結論,只在它查證過的那個角度成立——它確實找到了『請求交錯』這個現象,但沒有去問『這個診斷方向,能不能解釋為什麼加鎖之後問題還是沒有完全消失』這個問題。 診斷停在「能自圓其說」的程度,而不是「能解釋全部現象」的程度,就是這個案例翻車的地方。
真正可靠的除錯紀律,不是「找到一個能解釋現象的診斷就動手修」,而是要求這個診斷能解釋你觀察到的全部現象,包括那些一開始看起來像雜訊、可以被忽略的殘留案例。加鎖之後偶爾還是會出現的少數情況,不是可以被忽略的雜訊,而是「診斷不完整」最直接的證據。
回想你上一次修一個 bug 的經驗:那個修法讓症狀頻率降低了,但有沒有完全消失?如果沒有完全消失,你當時是把殘留案例當成「可以接受的邊界情況」放過了,還是回頭質疑了診斷方向本身?
明天是另一個完全不同性質的案例:一次看似單純的維運操作——幫一張大表加索引——差點拖垮整個資料庫實例。