套用軟體工藝的後設原則:DRY、正交性、曳光彈與契約式設計。當使用者提到「最佳實務」、「務實做法」、「破窗效應」、「曳光彈」、「軟體工藝」、「避免技術債」、「程式碼所有權」或「如何成為更好的開發者」時使用。也適用於評估自建與購買的決策、設計估算方法,或選擇可逆與不可逆的架構決策。涵蓋估算、領域語言與可逆性。如需程式碼層級的品質,請參閱 clean-code。如需重構技術,請參閱 refactoring-patterns。
務實的程式設計師框架
源自 Hunt 與 Thomas 的《The Pragmatic Programmer》(20 週年紀念版)中系統層級的軟體工藝方法。在設計系統、審查架構、撰寫程式碼或提供工程文化建議時套用這些後設原則——思考如何設計軟體,而不只是如何寫程式。
核心原則
在乎你的手藝。 軟體開發需要持續學習、紀律性的練習與個人責任——務實的程式設計師會超越眼前的問題,思考脈絡、取捨與長期後果。優秀的軟體來自良好的習慣:無情地避免重複、保持元件正交、將每一行程式碼視為必須證明其價值的活資產。目標不是完美——而是易於變更、易於理解、易於信任的系統。
評分
目標:10/10。 針對七項快速診斷問題評分:每個「是」約得 1.4 分(7 個「是」= 10)。然後分級:
- 9-10:所有原則都成立——DRY 知識、正交層次、可運作的曳光彈切片、邊界契約、無破窗、可逆的廠商/資料庫選擇、範圍估算。
- 5-6:有 1-2 項違規,造成實際的變更成本(例如業務邏輯與資料庫耦合、單點估算)。
- <=3:普遍的重複、全域狀態或累積的破窗——熵正在勝出。
務必陳述分數、指出失敗的診斷項目,並從「行動」欄位給出具體修正方式以達到 10/10。
七項後設原則
七項打造持久軟體的原則:
1. DRY(Don't Repeat Yourself,不要重複自己)
核心概念: 系統中的每一份知識都必須有單一、明確、權威的表述。DRY 是關於知識,而非程式碼——重複的邏輯、業務規則或設定比重複的語法危險得多。
為什麼有效: 重複的知識必須在多處修改;總會漏掉一處,導致不一致。DRY 減少 bug 的發生面,讓系統更容易變更。
關鍵見解:
- DRY 適用於知識與意圖,而非文字相似性——兩個服務不同業務規則的相同程式碼區塊並非重複
- 四種重複類型:強迫性(環境迫使)、無意性(開發者未察覺)、不耐煩性(懶得抽象化)、開發者間(多人重複)
- 重述程式碼的註解違反 DRY——應解釋為什麼,而非做什麼
- 資料庫綱要、API 規格與文件若未從單一來源產生,即構成重複
- DRY 的相反是 WET:「Write Everything Twice」(每件事寫兩次)或「We Enjoy Typing」(我們喜歡打字)
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 設定值 | 單一事實來源 | 資料庫連線放在一個環境變數檔,各處引用 |
| 驗證規則 | 共享綱要 | 一個 JSON Schema 或 Zod schema 同時用於客戶端與伺服器端 |
| API 合約 | 從規格產生 | OpenAPI 規格產生型別、文件與客戶端程式碼 |
參閱:references/dry-orthogonality.md 以分類特定重複或判斷兩個程式碼區塊是否為相同知識——包含各類型的範例與緩解方式。
2. 正交性(Orthogonality)
核心概念: 兩個元件若變更其中一個不會影響另一個,即為正交。設計系統時讓元件自包含、獨立,且具有單一明確的用途。
為什麼有效: 解耦將變更局部化——一個模組的修正不會波及無關的模組,因此影響範圍受到控制。變更資料庫層時 UI 不應受影響;變更驗證提供者時業務邏輯不應在意。
關鍵見解:
- 問:「如果我大幅變更某個函式背後的需求,會影響多少模組?」答案應為一個
- 消除無關事物之間的效應——記錄的變更絕不應破壞計費功能
- 分層架構促進正交性:表現層、領域邏輯、資料存取
- 避免全域資料——每個使用全域狀態的消費者都與之耦合
- 強迫你繼承其類別的框架會降低正交性
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 架構 | 分層分離 | Controller -> Service -> Repository,每層可替換 |
| 依賴 | 依賴注入 | 傳入 Notifier 介面,而非具體的 SlackClient 類別 |
| 測試 | 隔離的單元測試 | 測試業務邏輯時不依賴資料庫、網路或檔案系統 |
參閱:references/dry-orthogonality.md 以衡量耦合度或重構為解耦層次——包含變更影響測試、陌生人測試、分層架構圖與直升機比喻。
3. 曳光彈與原型(Tracer Bullets and Prototypes)
核心概念: 曳光彈是端到端的實作,以最少功能連接系統所有層次。與原型(可拋棄)不同,曳光彈程式碼是正式程式碼——雖薄但真實。
為什麼有效: 曳光彈在投入大量資源填滿所有功能之前,立即提供端到端的回饋。使用者看到實際成果,開發者有建構基礎,整合問題也提早浮現。
關鍵見解:
- 曳光彈:系統中一條薄但完整的路徑(UI -> API -> DB)——保留它
- 原型:針對單一風險面向的聚焦探索——拋棄它
- 在「黑暗中射擊」時使用曳光彈——需求模糊、架構未經驗證
- 若曳光彈未命中,調整後再射——迭代成本低
- 清楚標記原型為可拋棄——絕不讓它變成正式程式碼
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 新專案 | 垂直切片 | 一個功能端到端:按鈕 -> API -> DB -> 回應 |
| 不確定的技術 | 快速原型 | 在承諾使用前測試 WebSocket 效能 |
| 微服務 | 步行骨架 | 通過完整 CI/CD 管線的 Hello-world 服務 |
參閱:references/tracer-bullets.md 以決定在新專案中使用曳光彈或原型,或建構步行骨架——包含黑暗中射擊的決策、迭代迴圈與常見陷阱。
4. 契約式設計與斷言式程式設計(Design by Contract and Assertive Programming)
核心概念: 透過前置條件(呼叫前必須為真)、後置條件(呼叫後保證為真)與不變條件(始終為真)來定義並強制執行軟體模組的權利與責任。當契約被違反時,立即且大聲地失敗。
為什麼有效: 契約讓假設變得明確。與其默默破壞資料或在不合法狀態下勉強運作,系統在問題發生點崩潰——死掉的程式不會說謊。
關鍵見解:
- 前置條件:呼叫者的責任——「我只接受正整數」
- 後置條件:程式的保證——「我會回傳排序後的列表」
- 不變條件:始終為真——「帳戶餘額絕不為負」
- 及早崩潰:死掉的程式造成的損害遠小於殘缺的程式
- 對絕不該發生的事使用斷言;對可能發生的事使用錯誤處理
- 在動態語言中,透過執行時期檢查與守衛子句實作契約
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 函式進入點 | 前置條件守衛 | 函式開頭 assert age >= 0, "Age cannot be negative" |
| 類別狀態 | 不變條件驗證 | 每次狀態變更後呼叫 validate! |
| API 邊界 | 綱要驗證 | 處理前驗證請求主體是否符合綱要 |
參閱:references/contracts-assertions.md 以在程式中加入契約或決定使用斷言還是錯誤處理——包含前/後/不變條件的實際模式、動態語言的守衛子句,以及斷言與錯誤處理的邊界。
5. 破窗理論(The Broken Window Theory)
核心概念: 一扇破窗——一段設計不良的程式碼、一個糟糕的管理決策、一個「以後再修」的 hack——開始腐化。一旦系統顯露忽視,熵會加速,紀律崩潰。
為什麼有效: 心理學。當程式碼乾淨時,開發者會感受到社會壓力去維持;當程式碼已經混亂時,添加更多混亂的門檻降為零。品質是團隊習慣,而非個人英雄式努力。
關鍵見解:
- 不要讓破窗(不良設計、錯誤決策、糟糕程式碼)未修復
- 若無法立即修復,先封起來:附上票號的 TODO、停用的功能、樁程式
- 成為變革的催化劑:向人們展示未來的可行樣貌(石頭湯)
- 注意緩慢惡化(煮青蛙)——隨時間監控技術債指標
- 第一個 hack 最昂貴,因為它允許後續所有 hack
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 舊有程式碼 | 封窗 | 在新增功能前,用乾淨介面包裹不良程式碼 |
| 程式碼審查 | 對新債務零容忍 | 拒絕包含 // TODO: fix later 且無對應票號的 PR |
| 技術債 | 債務預算 | 每個 sprint 分配 20% 時間修復破窗 |
參閱:references/broken-windows.md 以在團隊正常化忽視時或需要推動轉變時使用——包含修復策略、石頭湯催化劑手法,以及建立品質文化。
6. 可逆性與彈性(Reversibility and Flexibility)
核心概念: 沒有最終決定。建構易於改變資料庫、框架、廠商、架構與部署目標的系統——變更成本應與變更範圍成比例。
為什麼有效: 需求會變、廠商被收購、技術過時。若你的架構對這些硬編碼假設,每次變更都變成重寫;彈性架構將決策視為設定而非結構。
關鍵見解:
- 將第三方依賴抽象化到自己的介面後——絕不讓廠商 API 洩漏到業務邏輯
- 「岔路」測試:你能在一週內從 Postgres 切換到 DynamoDB 嗎?如果不能,表示耦合
- 元資料驅動系統(設定檔、功能開關)比硬編碼邏輯更有彈性
- YAGNI 也適用於過早抽象化——不要建構你還不需要的彈性
- 可逆性不是預測未來;而是不把自己逼入死胡同
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| 資料庫 | Repository 模式 | 業務邏輯呼叫 repo.save(user),而非 pg.query(...) |
| 外部 API | 轉接器/包裝器 | PaymentGateway 介面包裝 Stripe;之後可換成 Braintree |
| 功能開關 | 執行時期切換 | 新結帳流程放在開關後,數秒內可回滾 |
參閱:references/reversibility.md 以在決定使用某廠商或框架時,或衡量某決策需要多大可逆性時使用——包含各層次的可逆性模式、岔路測試,以及何時不應最佳化可逆性。
7. 估算與知識投資組合(Estimation and Knowledge Portfolio)
核心概念: 透過理解範圍、建立模型、分解為元件並指定範圍來學習可靠估算。像管理財務投資組合一樣管理學習:定期投資、分散風險、重新平衡。
為什麼有效: 誠實的估算建立與利害關係人的信任(「1-3 週」勝過自信但錯誤的「2 週」)。知識投資組合讓你在技術轉變時保持相關性——停止學習的程式設計師將不再有效率。
關鍵見解:
- 問「這個估算的目的是什麼?」——脈絡決定精確度(預算規劃 vs. sprint 規劃)
- 使用 PERT:(樂觀 + 4x 最可能 + 悲觀)/ 6
- 分解為元件並分別估算;加總比單一猜測更準確
- 保留估算日誌:比較估算與實際值並校準
- 投資組合規則:定期投資(每週學習)、分散到技術棧之外、混合安全與投機性投資、早期學習新興技術(低買)
程式碼應用:
| 情境 | 模式 | 範例 |
|---|---|---|
| Sprint 規劃 | 範圍估算 | 「3-5 天」附信心水準,而非單一數字 |
| 新技術 | 限時快速原型 | 「花 2 天評估;之後才能適當估算」 |
| 學習 | 每週投資 | 每週 1 小時學習新語言、工具或領域 |
參閱:references/estimation-portfolio.md 以在需要對估算負責或校準過去的誤差時使用——包含 PERT 與分解程序、估算日誌校準迴圈,以及投資組合重新平衡。
常見錯誤
| 錯誤 | 為何失敗 | 修正 |
|---|---|---|
| 對服務不同目的的相似程式碼進行 DRY | 耦合不相關的概念;變更一個會破壞另一個 | 只對知識進行 DRY,而非巧合的程式碼相似性 |
| 跳過曳光彈,逐層建構 | 整合問題晚浮現;無端到端回饋 | 先建構一個薄的垂直切片 |
| 忽略破窗「因為以後會重構」 | 熵加速;以後永遠不會來;士氣下降 | 立即修復或用追蹤票號封窗 |
| 將估算視為單點承諾 | 虛假精確度在未達成時侵蝕信任 | 始終給出範圍與信心水準 |
| 一開始就讓一切「有彈性」 | 過度工程化;無證據的抽象化 | 在有具體證據需要時才加入彈性 |
| 移除正式環境的斷言「為了效能」 | 斷言本可捕捉的 bug 現在默默破壞資料 | 保留關鍵斷言;移除前先基準測試 |
| 全域狀態「為了方便」 | 破壞正交性;所有東西與所有東西耦合 | 使用依賴注入與明確參數 |
快速診斷
| 問題 | 若否 | 行動 |
|---|---|---|
| 我能否在不觸碰業務邏輯的情況下變更資料庫? | 正交性違規 | 引入 Repository/Adapter 模式 |
| 我是否有可運作的端到端切片? | 缺少曳光彈 | 在擴展前先建構一個垂直切片 |
| 每個業務規則是否只定義在一處? | DRY 違規 | 找出權威來源;移除重複 |
| 新開發者會稱此程式碼庫「乾淨」嗎? | 存在破窗 | 安排專門的清理 sprint |
| 我的估算是否包含範圍與信心水準? | 估算問題 | 改用 PERT 或基於範圍的估算 |
| 我能否在 5 分鐘內回滾此次部署? | 可逆性缺口 | 加入功能開關與藍綠部署 |
| 我是否每週學習新東西? | 知識投資組合停滯 | 安排每週學習時間並追蹤 |
延伸閱讀
- The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition by Andrew Hunt and David Thomas
關於作者
Andrew Hunt 與 David Thomas 共同創立 Pragmatic Bookshelf,是敏捷宣言的 17 位原始作者之一。Thomas 創造了「DRY」與「Code Kata」,並合著《Programming Ruby》(Pickaxe 書);Hunt 專注於團隊如何學習、溝通與維持品質。他們共同撰寫了《The Pragmatic Programmer》,這是有史以來最具影響力的軟體書籍之一。




