第 10 / 15 部 待錄

開發者測試與除錯

《Code Complete 軟體建構之道》Podcast 準備稿:開發者測試與除錯

書名: 軟體建構之道 Code Complete(A Practical Handbook of Software Construction, 2nd Edition) 作者: Steve McConnell 系列: 啃一本大書(恩普拉氏) 公開標題建議: 啃一本大書|Code Complete.測試是為了破壞,除錯靠科學方法 (10/15) 涵蓋範圍: 第22章 開發者測試 + 第23章 除錯

背景速覽

這集講「程式碼改善」部裡最日常的兩件苦差事:測試(找出 bug)和除錯(診斷並修掉 bug)。McConnell 把這兩件事分得很清楚——測試是偵測「有沒有錯」,除錯是「為什麼錯、怎麼修」。這集最有價值的是:他把這兩件大家憑感覺在做的事,講成有系統、有方法的工程活動。尤其除錯那章,點出最強與最弱的工程師效率差距高達 20:1——而差距來自方法,不是天賦。

一句話重點

測試的心態反人性——它的目標是「破壞」你的軟體、是證明它有錯,而且測試本身不會提升品質(像體重計不會幫你減肥);而除錯的關鍵不是手氣或猜測,是科學方法——穩定錯誤、形成假設、設計驗證、修根源不修表象。

值得討論的重點

1. 測試的反直覺心態——它的目標是「破壞」軟體

McConnell 說測試對開發者是反直覺的:目標是破壞自己的作品、無法證明無錯、本身不改善品質、必須假設 code 有錯。Myers 的實驗很扎心——經驗豐富的程式設計師平均只找到 15 個已知缺陷裡的 5 個。所以「測自己的 code」天生有盲區。一個重要提醒:測試只是品質計畫的一部分——協同實踐(審查)能抓更高比例、成本更低的 bug(呼應 EP9)。開發者測試該佔專案總時間的 8-25%。最有價值的實踐是先寫測試 test-first:它不增加工作量(只是重新排序)、更早抓到缺陷、逼你先想清楚需求與設計。

2. 系統化產生測試案例——別憑感覺亂測

窮舉測試不可能(簡單的姓名+地址+電話就有約 10^66 種組合),所以測試的藝術是挑最可能揭露 bug 的少數案例。McConnell 給了一套系統方法:結構化基礎測試(從 1 開始,每個 if/while/for/and/or 加 1,算出至少要幾個案例才能跑過每一行)、資料流測試(追蹤變數的「定義-使用-終結」三種狀態,可疑的組合如「定義後直接終結」「使用已終結的變數」)、等價分割(把輸入空間分類,每類測一個代表值)、邊界分析(針對 off-by-one,每個範圍測三個案例:邊界以下、邊界上、邊界以上)。一個實用小撇步:用容易手算的整數(如 $20,000)當測試資料,別用 $90,783.82 這種醜數字,降低自己手算出錯的機率。

3. 80/20 法則在 bug 上——缺陷高度集中

這段數據很有衝擊力:80% 的錯誤集中在 20% 的類別裡,50% 集中在 5% 的類別裡。 IBM 的 IMS 系統,修復 425 個類別裡 31 個「易出錯類別」後,客戶缺陷減少十倍、維護成本降 45%。維護策略的啟示很明確:別在原 code 上反覆修補那些 bug 王,要集中識別、重新設計、重寫它們。其他扎心數據:85% 的錯誤只需改一個常式、95% 是程式設計師自己的錯、三個史上最昂貴的軟體錯誤都只是改了單一字元。還有一條警告:測試 code 的錯誤密度常常等於甚至高於被測 code——所以寫測試要跟寫正式 code 一樣嚴謹。

4. 除錯靠科學方法,不靠巫毒

第23章是這集的靈魂。McConnell 點出最強與最弱工程師除錯效率差 20:1(甚至更多),而差距來自方法。他先列無效的除錯方式:靠猜測(隨機灑 print、東改西改直到「好像能動」)、不花時間理解問題、修表面不修根源(用 if (client == 45) 硬改特定結果)、迷信式除錯(怪機器怪編譯器)。正解是科學方法:穩定錯誤(使它可重現,並簡化到最小),接著定位來源(蒐集資料、形成假設、設計驗證、執行),然後修正、測試修正,最後 尋找類似錯誤(bug 會群聚)。最重的一句心態:即使錯誤看似不是你的問題,先假設它是你的責任——這能幫你更快找到 bug。還有一個迷人的技巧「告解式除錯」:把問題講給別人(甚至一隻橡皮鴨)聽,解釋的過程中你常常自己就找到答案了。

注意事項

⚠️ McConnell 對「先寫測試 test-first」是肯定的,但他的立場比 TDD 純粹主義者溫和務實——他把它當成「最有價值的實踐之一」,而不是「唯一正道」。他關心的是「先想清楚需求和設計」這個效果,不是 red-green-refactor 的儀式。提醒聽眾:test-first 的價值在於逼你先定義「正確是什麼」,至於要不要嚴格遵守 TDD 教條,看團隊。

⚠️ 「修根源不修表象」聽起來理所當然,但實務上壓力下很難做到——deadline 逼近時,用 if 硬擋住症狀的誘惑極大。McConnell 給的判準很實用:研究發現缺陷修正首次正確的機率不到 50%,所以「一次只改一處、改完跑回歸測試、新增一個會暴露此 bug 的測試」這套紀律不是龜毛,是因為亂修的失敗率本來就高。

⚠️ 「除錯器不能取代思考,但思考有時也不能取代好的除錯器」——這句平衡得好。別讓聽眾以為「高手都不用 debugger 純靠腦」,McConnell 的立場是好思考 + 好工具的組合最強。

專家補充

💡 「先假設是自己的錯」「95% 的 bug 是你自己的」這條心態,呼應 EP4 講的「從迷信走向理解」,也預告 EP15 的「個人品格」裡的「知識誠實」。這是一條貫穿全書的職業心態暗線:專業,從承認自己會錯、且大部分問題就是自己造成的開始。 這比任何技術都難,因為它對抗的是人的自我防衛本能。

💡 「告解式除錯」(rubber duck debugging)今天已經是工程圈的流行語——對著一隻橡皮鴨講解你的 code,講著講著就找到 bug 了。這個技巧的原型就在這章(McConnell 叫它「跟別人說」)。它背後的認知科學是:把內隱的思路外化成語言,會逼你補上你跳過的邏輯步驟。 這也是為什麼寫文件、講解、教別人,是最好的學習方式。

💡 「80% 的 bug 在 20% 的類別」這個缺陷集中現象,加上「找出它們、重寫它們」的策略,是非常實用的維護洞見。它呼應了 EP11 重構會講的「針對高錯誤率、高複雜度模組下手」。可以給聽眾一個具體行動:看你的 bug tracker,哪幾個檔案/模組反覆出現?那就是你的 20%,與其一直貼 OK 繃,不如排個時間重寫。

討論問題

🎙️ 開場鉤子:「同樣一個 bug,最強的工程師五分鐘搞定,最弱的要花二十倍時間——而且這個差距,跟智商、年資都沒關係。差在哪?差在一個你以為很玄、其實很科學的東西:除錯的方法。」

🎙️ 自問自答:「測試到底能不能證明我的 code 是對的?」——不能。測試只能證明它『有錯』,永遠無法證明它『無錯』。所以測試的心態要轉個彎:你不是去確認它能動,你是去想盡辦法弄壞它。

🎙️ 帶走的一題:「下次卡在一個 bug 超過十五分鐘,停止亂改——找個同事(或一隻橡皮鴨),從頭到尾把問題講一遍。十次有八次,你會在講到一半的時候自己『啊』一聲找到答案。這就是告解式除錯。」

更大範圍關聯

  • 與測試經典群:本集的 test-first 接 Kent Beck《Test-Driven Development》、Myers《The Art of Software Testing》;可在 [軟體工程領域切分藍圖] 接到測試/TDD 線。
  • 職業心態暗線:「先假設是自己的錯」串起 EP4(從迷信到理解)、EP7(高手模擬 vs 菜鳥亂試)、EP15(知識誠實),是全書區分專業與業餘的核心判準。
  • 缺陷集中接重構:80/20 缺陷分布直接接 EP11 重構的「針對高錯誤率模組下手」策略。
  • 時代座標:2004 年正是 TDD、JUnit 興起的時刻,McConnell 對 test-first 的肯定反映了當時敏捷測試文化對傳統工程的滲透;但他的務實態度也代表了一種「吸收但不盲從」的成熟立場。

錄製建議

  • 建議時長:約 25-28 分鐘。配比:測試的反直覺心態+test-first 6 分;系統化產生測試案例 5 分;80/20 缺陷集中(衝擊數據)5 分;除錯的科學方法(靈魂)9 分。
  • 討論策略:用「20:1 的除錯效率差距,差在方法不是天賦」當開場鉤子,立刻抓住聽眾——因為這暗示「除錯能力是可學的」,很有希望感。測試那段強調「目標是破壞、本身不提升品質」這個反直覺心態。除錯是重頭戲,把「科學方法 vs 巫毒程式設計」「修根源不修表象」「先假設是自己的錯」三個點講透。「告解式除錯/橡皮鴨」當收尾,趣味又實用。
  • 全程穿插職業心態暗線:「先假設是自己的錯」這條從 EP4 延續、往 EP15 預告,讓聽眾感覺到全書有一個一致的「專業 = 誠實面對自己會錯」的精神內核。