Skip to content

除錯的藝術

前言

程式碼寫完了,執行報錯——然後呢? 很多新手在這一步就卡住了,盯著螢幕不知所措。除錯(Debug)是程式設計中最核心的技能之一,甚至比寫程式碼本身更重要。因為寫程式碼只佔開發時間的 30%,剩下的 70% 都在理解問題、定位 Bug、驗證修復。

這篇文章會帶你學什麼?

學完這章後,你將獲得:

  • 除錯思維:建立系統化的問題定位方法,不再「瞎猜」
  • 錯誤閱讀能力:看懂報錯資訊,從錯誤堆疊中快速定位問題
  • 常用除錯方法:掌握二分法、橡皮鴨、最小重現等經典除錯技巧
  • 工具使用能力:了解中斷點除錯、日誌除錯、網路除錯等工具的使用場景
  • AI 輔助除錯:學會用 AI 加速除錯過程,但不依賴 AI
章节內容核心概念
第 1 章讀懂錯誤資訊錯誤類型、堆疊追蹤
第 2 章經典除錯方法二分法、橡皮鴨、最小重現
第 3 章除錯工具箱中斷點、日誌、網路抓包
第 4 章AI 時代的除錯AI 輔助 + 人工判斷
第 5 章除錯心態與習慣防禦性程式設計、除錯日誌

0. 全景圖:除錯是一種科學方法

除錯不是「碰運氣」,而是一個嚴謹的科學過程。物理學家做實驗的方法論,完全適用於除錯:

  1. 觀察現象:程式出了什麼問題?報了什麼錯?
  2. 提出假設:可能是什麼原因導致的?
  3. 設計實驗:怎麼驗證這個假設?
  4. 驗證結論:假設對了就修復,錯了就換一個假設

除錯的黃金法則

  • 先重現,再修復:不能穩定重現的 Bug,修了也不知道是不是真的修好了
  • 一次只改一個變數:同時改多處,就不知道是哪個改動解決了問題
  • 相信證據,不相信直覺:你覺得「不可能是這裡的問題」,往往就是這裡的問題
  • 最近改了什麼?:80% 的 Bug 都是最近的改動引入的

1. 讀懂錯誤資訊:報錯不是敵人,是線索

新手最常犯的錯誤:看到報錯就慌,直接關掉或者忽略。其實,錯誤資訊是程式在告訴你哪裡出了問題——它是你最好的朋友。

1.1 錯誤的三大類型

類型什麼時候出現舉例嚴重程度
語法錯誤程式碼還沒執行就報錯少了括號、拼錯關鍵字最容易修
執行時錯誤程式碼執行到某一行當掉存取不存在的變數、除以零中等難度
邏輯錯誤程式碼能執行,但結果不對計算公式寫錯、條件判斷反了最難發現

1.2 如何閱讀錯誤堆疊

以 JavaScript 為例,一個典型的錯誤資訊:

TypeError: Cannot read properties of undefined (reading 'name')
    at getUserName (app.js:15:23)
    at handleClick (app.js:42:10)
    at HTMLButtonElement.<anonymous> (app.js:58:5)

從上往下讀

  1. 第一行:錯誤類型 + 錯誤描述 → TypeError,試圖讀取 undefinedname 屬性
  2. 第二行:出錯的函式和位置 → getUserName 函式,app.js 第 15 行第 23 列
  3. 後續行:呼叫鏈 → 誰呼叫了這個函式?handleClick → 按鈕點擊事件

閱讀堆疊的口訣

從上往下找原因,從下往上找源頭。 第一行告訴你「出了什麼錯」,最後一行告訴你「從哪裡開始的」。

1.3 常見錯誤類型速查

錯誤名稱含意常見原因
SyntaxError語法錯誤括號不匹配、少了逗號
TypeError類型錯誤undefined/null 做操作
ReferenceError引用錯誤使用了未宣告的變數
RangeError範圍錯誤陣列越界、遞迴太深
NetworkError網路錯誤API 請求失敗、跨域問題
404 Not Found資源不存在URL 寫錯、檔案被刪除
500 Internal Server Error伺服器內部錯誤後端程式碼當掉

1.4 Python 錯誤資訊對比

Python 的堆疊和 JavaScript 相反——從下往上讀

python
Traceback (most recent call last):
  File "main.py", line 10, in <module>
    result = calculate(data)
  File "main.py", line 5, in calculate
    return data["price"] * data["quantity"]
KeyError: 'quantity'

最後一行才是錯誤原因:KeyError: 'quantity',字典裡沒有 quantity 這個鍵。

不同語言,同一個思路

不管什麼語言,錯誤資訊都包含三個關鍵資訊:什麼錯(錯誤類型)、哪裡錯(檔案和行號)、為什麼錯(錯誤描述)。學會提取這三個資訊,就能讀懂任何語言的報錯。


2. 經典除錯方法:前人總結的智慧

這些方法不需要任何工具,只需要你的大腦。它們是所有進階除錯技巧的基礎。

2.1 二分法除錯

核心思想:把問題範圍縮小一半,再縮小一半,直到找到根源。

場景:程式碼很長,不知道哪一段出了問題。

步驟

  1. 在程式碼中間加一個 console.log(或 print
  2. 如果中間點之前就出錯了 → 問題在上半部分
  3. 如果中間點之後才出錯 → 問題在下半部分
  4. 對出錯的那一半,重複上述步驟
100 行程式碼出了 Bug
    ↓ 在第 50 行加 log
問題在 50-100 行
    ↓ 在第 75 行加 log
問題在 50-75 行
    ↓ 在第 62 行加 log
問題在第 60-62 行!

二分法的威力

100 行程式碼,最多只需要 7 次(log₂100 ≈ 7)就能定位到具體行。1000 行也只需要 10 次。

2.2 橡皮鴨除錯法

核心思想:把問題一行一行地「講」給別人聽(或者一隻橡皮鴨),講著講著你自己就發現問題了。

為什麼有效? 因為「寫程式碼」和「解釋程式碼」用的是大腦的不同區域。當你被迫用語言描述每一步邏輯時,那些你「以為對了」的假設會暴露出來。

實作方法

  1. 開啟出問題的程式碼
  2. 逐行解釋:「這一行做了什麼?為什麼要這麼做?」
  3. 當你說出「嗯,這裡應該是……等等」的時候,Bug 往往就在那裡

2.3 最小重現

核心思想:把複雜的問題簡化到最小,只保留能觸發 Bug 的最少程式碼。

為什麼重要?

  • 複雜系統中,Bug 可能被其他程式碼「掩蓋」
  • 最小重現能排除干擾因素,讓問題一目了然
  • 也方便你向別人求助——沒人願意看你 500 行程式碼

步驟

  1. 建立一個新的空檔案
  2. 只複製和問題相關的程式碼
  3. 逐步刪減,直到刪掉任何一行 Bug 就消失
  4. 剩下的就是 Bug 的根源

2.4 回退法(Git Bisect)

核心思想:如果程式碼「之前是好的,現在壞了」,那就找到是哪次提交引入的問題。

bash
# Git 自帶的二分搜尋工具
git bisect start
git bisect bad          # 標記目前版本有 Bug
git bisect good abc123  # 標記某個正常的舊版本
# Git 會自動切換到中間的提交,你測試後告訴它 good 或 bad
# 重複幾次就能找到引入 Bug 的那次提交

除錯方法選擇指南

情況推薦方法
不知道哪一段程式碼出錯二分法
邏輯看起來對但結果不對橡皮鴨
複雜系統中的 Bug最小重現
「之前好好的突然壞了」回退法 / Git Bisect

3. 除錯工具箱:用對工具事半功倍

方法論是基礎,但好的工具能讓除錯效率翻倍。

3.1 console.log / print:最樸素也最實用

適用場景:快速檢視變數值、確認程式碼執行到了哪裡。

javascript
// JavaScript
console.log('函式被呼叫了,參數是:', data)
console.log('計算結果:', result)
console.table(arrayData)  // 表格形式展示陣列/物件
python
# Python
print(f"目前值: {value}")
print(f"類型: {type(data)}")  # 檢查資料類型

進階技巧

方法用途
console.log()普通輸出
console.warn()黃色警告,容易在大量日誌中找到
console.error()紅色錯誤
console.table()表格展示陣列和物件
console.time() / console.timeEnd()測量程式碼執行時間
console.trace()列印呼叫堆疊

3.2 中斷點除錯:逐行執行,看清每一步

適用場景:邏輯複雜,需要一步步追蹤程式碼執行過程。

在瀏覽器中(Chrome DevTools):

  1. 開啟開發者工具(F12)→ Sources 面板
  2. 找到原始碼檔案,點擊行號設定中斷點
  3. 觸發相關操作,程式碼會在中斷點處暫停
  4. 用控制按鈕逐步執行:
    • 繼續(F8):執行到下一個中斷點
    • 逐步跳過(F10):執行目前行,不進入函式內部
    • 逐步進入(F11):進入函式內部
    • 逐步跳出(Shift+F11):跳出目前函式

在 VS Code 中

  1. 點擊行號左側設定中斷點(紅色圓點)
  2. 按 F5 啟動除錯
  3. 在「變數」面板檢視所有變數的目前值
  4. 在「監視」面板新增你關心的運算式

中斷點 vs console.log

console.log 適合快速驗證,用完就刪。中斷點除錯適合深入分析複雜邏輯。兩者不是替代關係,而是互補關係。

3.3 網路除錯:前後端之間的問題

適用場景:頁面顯示不對,但不確定是前端的問題還是後端返回的資料有問題。

Chrome DevTools → Network 面板

檢視內容能發現什麼問題
狀態碼404(位址錯)、500(伺服器當了)、403(沒權限)
請求參數前端發送的資料對不對
回應資料後端返回的資料格式對不對
請求時間哪個介面太慢,拖慢了頁面
請求標頭Token 有沒有帶、Content-Type 對不對

除錯口訣:先看狀態碼,再看請求參數,最後看回應資料。

3.4 除錯工具選擇速查

問題類型推薦工具
變數值不對console.log / 中斷點
邏輯執行順序不對中斷點除錯
API 請求失敗Network 面板
頁面樣式不對Elements 面板(檢查 CSS)
效能問題Performance 面板 / console.time
記憶體洩漏Memory 面板

4. AI 時代的除錯:讓 AI 當你的助手

AI 工具(ChatGPT、Claude、Cursor 等)能大幅加速除錯過程,但前提是你得知道怎麼用。

4.1 AI 擅長什麼?

AI 擅長AI 不擅長
解釋錯誤資訊的含意理解你的業務邏輯
提供常見問題的解決方案判斷哪個方案最適合你的專案
產生除錯程式碼片段重現只在特定環境出現的 Bug
分析程式碼中的潛在問題理解複雜的系統上下文

4.2 向 AI 提問的正確姿勢

差的提問

「我的程式碼報錯了,幫我看看」

好的提問

「我在用 React 寫一個表單元件,提交時報錯 TypeError: Cannot read properties of undefined (reading 'email')。以下是相關程式碼:[貼程式碼]。我已經確認 API 返回的資料格式是正確的,問題可能出在前端資料處理。」

提問模板

1. 我在做什麼:[背景]
2. 期望的行為:[應該怎樣]
3. 實際的行為:[實際怎樣]
4. 錯誤資訊:[完整報錯]
5. 相關程式碼:[貼程式碼]
6. 我已經嘗試了:[排除了什麼]

4.3 AI 除錯的陷阱

AI 除錯的三個坑

  1. AI 可能「自信地胡說」:AI 給的方案看起來很合理,但可能完全不對。永遠要自己驗證。
  2. AI 不了解你的上下文:它不知道你的專案結構、依賴版本、執行環境。你需要提供足夠的上下文。
  3. 過度依賴 AI 會退化除錯能力:如果每次報錯都直接丟給 AI,你永遠學不會自己除錯。建議先自己分析 5 分鐘,再求助 AI。

4.4 AI + 人工的最佳組合

遇到 Bug

第 1 步:自己讀錯誤資訊(1 分鐘)

第 2 步:自己提出假設(2 分鐘)

第 3 步:快速驗證假設(2 分鐘)

卡住了?→ 把錯誤資訊 + 程式碼 + 你的分析發給 AI

AI 給出建議 → 你判斷是否合理 → 驗證

5. 除錯心態與習慣:從「救火」到「防火」

最好的除錯是不需要除錯。養成好習慣,能從源頭減少 Bug。

5.1 防禦性程式設計

核心思想:寫程式碼時就假設「一切都可能出錯」,提前做好防護。

javascript
// 差:假設 data 一定存在
const name = data.user.name

// 好:防禦性寫法
const name = data?.user?.name ?? '未知使用者'
python
# 差:假設檔案一定能開啟
content = open('config.json').read()

# 好:防禦性寫法
try:
    content = open('config.json').read()
except FileNotFoundError:
    print("設定檔案不存在,使用預設設定")
    content = '{}'

5.2 寫好日誌

日誌是「事後除錯」的關鍵。正式環境不能打中斷點,只能靠日誌。

日誌級別用途舉例
DEBUG開發時的詳細資訊變數值、函式參數
INFO正常的業務流程「使用者登入成功」、「訂單建立」
WARN不影響功能但需要注意「快取未命中」、「重試第 2 次」
ERROR出錯了,需要處理「資料庫連線失敗」、「API 逾時」

好日誌的標準

一條好的日誌應該回答:什麼時候在哪裡發生了什麼關鍵資料是什麼

[2025-01-15 14:30:22] [ERROR] [OrderService] 建立訂單失敗
  使用者ID: 12345, 商品ID: 67890, 原因: 庫存不足

5.3 除錯檢查清單

遇到 Bug 時,按這個順序排查:

  1. 讀錯誤資訊:錯誤類型、檔案、行號
  2. 最近改了什麼?:用 git diff 看最近的改動
  3. 能重現嗎?:找到穩定的重現步驟
  4. 縮小範圍:用二分法或最小重現定位
  5. 提出假設並驗證:一次只改一個變數
  6. 修復後回歸測試:確保修復沒有引入新問題

5.4 新手常踩的除錯陷阱

陷阱正確做法
不看報錯就開始改程式碼先完整閱讀錯誤資訊
同時改好幾個地方一次只改一處,驗證後再改下一處
改完不測試就提交每次修改後都執行測試
只在自己電腦上測試考慮不同環境(瀏覽器、系統、網路)
除錯完不清理 console.log提交前刪除所有除錯程式碼
遇到問題就重啟/重裝先理解問題原因,重啟只是臨時方案

6. 總結

除錯是一門手藝,需要刻意練習。回顧本章的核心要點:

  1. 除錯是科學方法:觀察 → 假設 → 實驗 → 驗證,不是碰運氣
  2. 錯誤資訊是朋友:學會從報錯中提取「什麼錯、哪裡錯、為什麼錯」
  3. 經典方法永不過時:二分法、橡皮鴨、最小重現是所有除錯的基礎
  4. 工具要用對場景:console.log 快速驗證,中斷點深入分析,Network 排查介面
  5. AI 是助手不是拐杖:先自己分析,再讓 AI 輔助,最後自己驗證
  6. 防火勝於救火:防禦性程式設計、好的日誌習慣能從源頭減少 Bug

記住這句話

每個 Bug 都是一次學習機會。 你修過的每一個 Bug,都在幫你建立「模式識別」能力——下次遇到類似問題,你會更快地定位到原因。


延伸閱讀