設計判準與日常實踐
這個主題處理兩件在日常裡分不開的事:把程式寫成之後改得動的樣子,以及讓那件事持續發生的工作習慣。它們分不開的理由是設計判準要靠習慣才生效——知道模組該小、介面該深,跟每次動手時真的去想這件事,中間隔著一整套日常做法。
三本書的差別在密度與主張數,不在誰對誰錯:《The Pragmatic Programmer》列幾十條短原則、《A Philosophy of Software Design》用一條主張貫穿全書、《Code Complete》鋪成百科式的對照表。手上的問題越具體,越適合主張集中的《A Philosophy of Software Design》;想建立通盤基礎,才輪得到百科式的《Code Complete》。
處境相容性這一項三本都查過,都沒有列出限制。它們的判準落在程式碼本身的形狀與個人的動手習慣上,成立與否不取決於組織有沒有職級制度、成員是不是同一個僱主、大家在不在同一個時區,也不受事故追責義務左右。這是全批唯一每一本都查過、而且每一本都沒有依賴的一篇——同一條技藝線上的另外三篇不是這樣,架構、驗證與改既有程式那三篇各自都有書預設了特定的組織條件。
起點是 The Pragmatic Programmer
Dave Thomas 與 Andy Hunt 的《The Pragmatic Programmer》把技藝拆成幾十條自成一段、互不依賴的實踐——從 DRY、正交性這種設計判準,到版本控制、純文字、自動化這種工具習慣,再到知識組合、除錯紀律這種職業態度——任何一條都可以單獨拿走用。條目多而彼此不相依,是它在本篇管得最寬、因此當起點的原因。
它最常被引用的是 DRY,而它的原文比多數轉述嚴格:系統裡的每一項知識都必須有單一、明確、權威的表述。這句話管的不只是程式碼——業務規則、設定值、資料定義同樣算,而多數 DRY 的誤用是把它讀成「不要複製貼上」,於是把兩段長得像但會各自變動的程式硬合併,製造出更難改的耦合。
另一條沒有被廣泛轉述的是曳光彈(tracer bullets):先打通一條從頭到尾都會動的最細路徑,再逐段加厚。它跟原型的差別在於曳光彈的程式碼會留下來成為骨架,原型是用完就丟——搞混這兩者的團隊會把探索用的臨時程式碼帶進生產。
20 週年版是大幅改寫過的,不是加註解的重印本,並補進了並行、actor model、property-based testing 與資安基礎。要買就買這一版。時效上,第一版綁的工具與環境(CVS、特定編輯器)在改版時清掉了;DRY、正交性、曳光彈、破窗理論這些主張不依賴任何工具世代。
證據來源是跨客戶的顧問經驗,形式是格言加短例而非資料——這也決定了它的用法:拿來當檢查表對照自己的習慣,而不是拿去說服別人。
這本書幾乎不需要前置準備,入行第一年就可以讀;而且每隔幾年重讀會讀出不同的東西,因為同一條原則在不同經驗量下對得上的情境不一樣。
想把設計問題想清楚時讀 A Philosophy of Software Design
John Ousterhout 的這本整本只推一個主張:軟體設計的根本問題是控制複雜度,其餘的判準都從這一條長出來。這種寫法的好處是它給得出一個可以隨身帶的判準,而不是一份要背的清單。
書中最可操作的是深模組(deep module)這個概念:好的模組是介面小而實作厚,壞的模組是介面跟實作一樣大。這條判準直接推翻了「函式應該盡量短」這種常見指導——把一個模組切成十個各自很淺的函式,介面總量反而變大,複雜度是上升的。另一組是紅旗清單(淺模組、資訊洩漏、時序分解、重複、註解重述程式碼),設計時可以逐條掃。
它跟 Pragmatic Programmer 的關係是深度換廣度:那本列出有哪些事該做,這本把其中一件(怎麼切模組)挖到底。兩本一起讀不會重複。
證據來源是單一路徑的個人經驗,而形態特別:素材來自作者多年開設軟體設計課的學生專案觀察,樣本受控但規模小、且偏向課堂專案,這是它跟業界顧問經驗不同的地方。作者是 Tcl 的作者、Stanford 教授。第二版於 2021 年出版,補了更多例子並回應了對第一版的批評。時效上沒有綁定任何工具世代。
讀這本要先有一段維護經驗當對照:維護過自己或別人寫的東西超過半年,經歷過「當初這樣切好像不對」。沒有這個對照時,深模組會讀成一個偏好而不是判準——可以先讀《The Pragmatic Programmer》,等維護經驗累積起來再回來讀這本。
繁體中文版查不到,簡體中文版《軟件設計的哲學》第二版由人民郵電出版社出版。
要一份百科式的對照時讀 Code Complete
Steve McConnell 的《Code Complete》第二版是這個主題涵蓋面最大的一本,把建構期的每個決定都拆開處理:變數命名、迴圈結構、防禦性編程、程式碼佈局、審查方式。它跟前兩本的差別在於它不推銷單一主張,而是把當時能找到的研究與實務歸納整理成對照表。
它的獨特之處是引用密度。多數技藝書的主張來自作者經驗,這本大量引用了實證研究——哪種審查方式抓到的缺陷比例較高、缺陷在哪個階段被發現的修復成本差幾倍,這類數字它會標出處。要拿數據去支持一個工程實踐的改變時,這本是站上少數能給引用來源的書。
時效要分層看,而這本的分層特別明顯。建構期的具體技術(那個年代的語言特性、命名慣例、程式碼佈局的爭論)大量過時,讀的時候會一直遇到已經不成立的例子;它引用的實證研究多數停在 2004 年之前,後續二十年的研究沒有納入;而它整理問題的方式——把每個建構決定拆成有哪些選項、各自的取捨是什麼——不依賴那些例子,那才是現在讀它的理由。
證據來源是理論建構(把當時的實證文獻與實務歸納整理成對照表)加上單一路徑的個人經驗,是這個主題裡唯一有系統引用實證研究的一本。這本不適合通讀,適合遇到具體問題時當工具書查——需要的是查閱的耐心,經驗門檻反而不高。繁體中文版譯名《CODE COMPLETE 2 中文版:軟體開發實務指南》。
為什麼只收這幾本
同樣回答「怎麼寫得更好」,這三本的密度差一個量級:Pragmatic Programmer 廣而每條短,A Philosophy of Software Design 窄而挖到底,Code Complete 大而全。手上的問題越具體越往《A Philosophy of Software Design》走,想建立通盤基礎才輪到《Code Complete》。
技藝類的書市極擁擠,而多數集中在兩個位置:特定語言的最佳實踐彙編,以及把某一套規則推銷成普適判準的書。前者屬於教學系列而非書單的責任範圍,後者的問題是規則脫離了推導它的取捨——分辨的方法是看它有沒有講清楚什麼情況下這條規則不適用,只給規則不給邊界的表示它把判斷收走了。
Robert Martin 的《Clean Code》常被列在這個位置,本書單未評估它。它的部分主張(函式應該極短、註解是失敗的象徵)在近年受到相當多的技術批評,而判斷那些批評成不成立需要逐條對照原文與反方論證,這裡沒有做。要讀的話值得同時找反方的討論一起看,不要當成無爭議的標準。
離收得進來最近的一門課是 MIT 的 6.005 Software Construction。大綱涵蓋規格、測試、抽象資料型別、物件導向設計模式、並行與函數式程式設計,跟本篇三本書重疊得最多,OpenCourseWare 上也放了考題、習題與程式作業——差的只有影音,而成因是那門課刻意不把課堂時間拿來講課(FAQ 自陳),因此沒有可錄的講課。這不代表設計判準拍不出來,理由寫在 工程技藝書單 的公開課段。
這個主題接到哪裡
設計判準要在既有程式碼上生效,靠的是改動的技術:手上有測試時怎麼移動結構、沒有測試時怎麼先弄出測試、以及小改動要不要現在做。那條路徑走 改既有的程式。
寫得好而團隊切錯時,交付一樣會卡,那條路徑走 組織結構與團隊設計。想知道自己在職涯位置上該補什麼,走 依位置選書。
各語言的具體寫法與慣例看 Python 維護指南、Go 維護指南、Flutter 實戰指南;測試怎麼分層與設計看 Testing 測試策略。