第 7 / 11 部 待錄

設計兩次與為何寫註解

《A Philosophy of Software Design 軟體設計的哲學》Podcast 準備稿:設計兩次與為何寫註解

書名: 軟體設計的哲學 A Philosophy of Software Design 作者: John Ousterhout 系列: 旗艦評書(恩普拉氏) 公開標題建議: 評書|A Philosophy of Software Design.設計兩次,以及「好程式自己會說話」這個美麗的謊言 (7/11) 涵蓋範圍: 第11章 設計兩次 + 第12章 為何要寫註解

背景速覽

這一集是「設計篇」收尾、「文件篇」開場的轉場集,把兩個篇幅較短但份量很重的章節合在一起。第11章「設計兩次」談一個工作習慣——別做第一個冒出來的方案,至少認真比較兩個差異很大的設計;它還意外地戳中了「聰明人」的盲點。第12章「為何要寫註解」則正面拆解「好程式碼自己會說話、不需要註解」這個工程界最流行的迷思——Ousterhout 把它比喻成「冰淇淋對健康有益」:我們都希望它是真的,但它不是。這兩章合起來,正好是「想清楚」和「寫下來」這對孿生工夫。

一句話重點

第一個冒出來的設計幾乎不可能是最好的,所以對每個重要決策都該設計兩次——逼自己比較幾個差異很大的方案,這不只讓這次的設計更好,還會持續磨利你的設計直覺;而「好程式碼不需要註解」是個美麗的謊言,因為抽象的本質就是隱藏複雜性,而程式碼本身無法表達抽象——沒有註解,使用者就得讀你的實作,抽象當場破功。

值得討論的重點

1. 設計兩次:方案要「差異夠大」才學得到東西

Ousterhout 的做法不複雜:面對重要的設計決策(一個類別的介面、一個模組的拆解),別停在第一個點子,至少草擬兩三個差異很大的方案,不必把細節釘死,列出各自的優劣,然後選一個——或者綜合多個方案的優點生出一個新的。編輯器文字類別就是例子:行導向、字元導向、範圍導向三個方案攤開比,你會發現「行和字元兩個方案都讓上層得做額外文字操作」這個共同弱點,本身就是線索,把你導向「範圍導向」這個更好的答案。他強調:就算你確信只有一條路,也至少想一個第二方案,再爛都好——思考爛設計的弱點本身就極具教育意義。

2. 設計兩次為什麼對「聰明人」特別難——一個誠實又扎心的觀察

這是本章最有記憶點的段落。Ousterhout 觀察:「設計兩次」對非常聰明的人特別難接受。因為他們從小靠「第一直覺」就能拿好成績,從不需要第二、第三個方案。但隨職涯推進,問題越來越難,總有一天第一個點子不再夠好——而大型軟體系統正是這種問題,沒有人能一次就做對。他看過太多聰明人堅持實作第一個點子,反而讓自己的真實潛力被低估、也很難合作。關鍵的心態轉換:你想不出最佳設計,不是因為你不夠聰明,而是因為問題真的很難——這是好事,思考難題比解 trivial 問題有趣多了。而且成本極低:小模組比較替代方案大概一兩小時,相對於後面好幾天的實作,回收綽綽有餘。

3. 「好程式自己會說話」是美麗的謊言

第12章正面開戰。Ousterhout 承認:好命名、清楚結構確實能減少對註解的需求——但不能取代。因為有些資訊程式碼根本表達不了:介面的非正式部分(方法的高階行為、回傳值的意義)、設計決策的理由(rationale)、一個方法何時該被呼叫。而「想知道方法做什麼就去讀程式碼」這個論點不成立——從實作推回抽象既慢又痛,還會逼你把方法寫得很短(製造一堆淺方法),大型系統根本不可能靠讀 code 學行為。最根本的一刀:抽象的目的是藏起複雜性,如果使用者必須讀你的實作才能用你的方法,抽象就破功了——沒有註解時,方法唯一的抽象就只剩它的宣告(名字+參數+回傳型別),而光看 substring(start, end) 的宣告,你根本不知道含不含 endstart > end 會怎樣。

4. 拆解四個「不寫註解」的藉口

Ousterhout 逐一回應四個常見藉口:

  • 「好程式不需要註解」:見上,迷思。
  • 「沒時間寫」:對應第3章投資心態——估算顯示好註解大概只增加總開發時間 10% 左右,很快回本;而且一旦讓寫文件被降級,因為永遠有更急的事,你最後就是沒有文件。
  • 「註解會過時」:確實會,但大規模文件變更只在 code 大改時才需要,而 code 改本身就比文件改更花時間,所以維護成本只是一小部分;靠「文件貼近 code」和 code review 控制。
  • 「所有註解都沒用」:這個最有道理——大家確實都看過爛註解。但結論不是「別寫」,而是「學會寫好」,後面幾章就是方法。別讓「過去看過爛註解」成為你寫不出好註解的藉口。

而好註解的本質,是捕捉那些存在於設計者腦中、卻無法用程式碼表達的資訊——它直接打擊第2章的兩大複雜性成因:澄清依賴、填補模糊;對「認知負擔」和「未知的未知」尤其有效。

注意事項

⚠️ 「設計兩次」別被聽成「每個 if 都要想三種寫法」。它針對的是重要的設計決策(介面、模組拆解這種影響面大的),不是每一行瑣碎邏輯。成本意識要帶到:花一兩小時比較大方向是投資,為了選 for 還是 while 糾結半天是浪費。

⚠️ 「好命名能減少註解」這條,別讓聽眾走極端變成「我命名好就完全不用註解」。Ousterhout 的立場很明確:命名是減少不是取代。介面的非正式部分、rationale、前置條件,再好的名字也表達不了。兩者是分工,不是二選一。

⚠️ 「10% 成本」又是估算不是實測,講的時候誠實。但這個估算的邏輯本身有說服力——真正敲程式碼的時間其實只佔開發一小部分,註解再貴也貴不到哪去。重點是傳達「寫註解沒你想像中那麼貴」這個直覺,而不是咬死數字。

專家補充

💡 「好程式碼自己會說話」這個迷思,其實是被《Clean Code》某種程度上推波助瀾的——Uncle Bob 確實說過「別用註解彌補爛 code」「最好的註解是你想辦法不寫的註解」。這是本季對照線一個很微妙的交鋒點:兩人都同意「用註解掩蓋爛 code」是錯的,但 Ousterhout 反對把這條推成「好 code 就不用註解」。精準的講法是——Uncle Bob 警惕的是「拿註解當遮羞布」,Ousterhout 補的是「就算 code 很乾淨,抽象、rationale、前置條件仍然只能靠註解」。聽眾常把前者誤讀成後者,這集正好幫他們把線劃清楚。呼應藍圖 §5。

💡 「設計兩次」其實是更廣的決策科學原則——心理學講的「考慮相反面(consider the opposite)」、產品設計的「至少三個方案」、建築的多版草圖,全是同一招:強迫自己離開第一直覺,才能看見它的盲區。Ousterhout 把它落到「class 介面」這個尺度,但這是個跨領域的好習慣。

💡 「抽象無法用程式碼表達、只能用註解」這個論點,其實點出了形式化方法的長年困境——學界一直想用形式規格(formal specification)取代自然語言註解,但 Ousterhout(和大多數業界)認為:自然語言精確度雖低、但表達力更高,寫出來的描述更直覺。這也是為什麼到了 AI 時代,「寫清楚的 docstring / 意圖註解」反而更值錢——它是人和工具都讀的那層抽象。

討論問題

🎙️ 開場鉤子:「有一種人,從小到大第一個答案就是對的,從不需要想第二個。如果這說的是你——這集有個壞消息:總有一天,你的第一個點子會不夠好,而那一天通常就是你開始寫大型系統的時候。」

🎙️ 自問自答:「『好的程式碼不需要註解』——你信過這句話嗎?」——用「光看 substring(start, end) 的宣告,你知道它含不含 end 嗎?」一句戳破:抽象藏在腦子裡,程式碼根本表達不出來。

🎙️ 帶走的一題:「下次做一個重要的設計決策時,逼自己在白板上寫第二個方案——一個跟第一個差很多的,哪怕你覺得它很爛。然後比較它們的弱點。看看這個過程,會不會讓你看見第一個方案裡你原本沒注意到的問題。」

更大範圍關聯

  • 與《Clean Code》的微妙交鋒:「好 code 不需要註解」是本季對照線上最容易被誤讀的一點,Ousterhout 對它的修正直接呼應 [軟體工程領域切分藍圖] §5「Clean 到底乾不乾淨」——Uncle Bob 反對「註解當遮羞布」,但不該被推成「乾淨就免註解」。
  • 決策科學:「設計兩次」呼應「consider the opposite」「至少三方案」等跨領域決策原則,也與《人月神話》「plan to throw one away(準備好丟掉第一版)」同源。
  • AI 時代的文件價值:「抽象只能用自然語言表達」這條,在 LLM 輔助開發時代反而更重要——意圖註解/docstring 是人與工具共讀的抽象層。可接到「documentation as code」「意圖即介面」的當代討論。

錄製建議

  • 建議時長:約 20–22 分鐘(兩章都短)。配比:設計兩次約 9 分(聰明人盲點是高潮)、為何寫註解約 11 分(四個藉口逐一拆)。
  • 討論策略:上半場用「聰明人的盲點」這個扎心觀察當鉤子,把「設計兩次」講成一個低成本高回報的習慣;下半場用「冰淇淋對健康有益」的比喻開「好 code 不用註解」的戰,再用 substring 宣告看不出含不含 end 收一個漂亮的論證。
  • 這是設計篇轉文件篇的轉場集,可在結尾預告:下一集進入全書第二厚的「怎麼寫好註解」——Ousterhout 給的不是雞湯,是一套可操作的框架。