pragmatic-programmer

pragmatic-programmer

熱門

套用軟體工藝的後設原則:DRY、正交性、曳光彈與契約式設計。當使用者提到「最佳實務」、「務實做法」、「破窗效應」、「曳光彈」、「軟體工藝」、「避免技術債」、「程式碼所有權」或「如何成為更好的開發者」時使用。也適用於評估自建與購買的決策、設計估算方法,或選擇可逆與不可逆的架構決策。涵蓋估算、領域語言與可逆性。如需程式碼層級的品質,請參閱 clean-code。如需重構技術,請參閱 refactoring-patterns。

1706星標
171分支
更新於 2026/7/22
SKILL.md
唯讀
名稱
pragmatic-programmer
描述

套用軟體工藝的後設原則: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 分鐘內回滾此次部署? 可逆性缺口 加入功能開關與藍綠部署
我是否每週學習新東西? 知識投資組合停滯 安排每週學習時間並追蹤

延伸閱讀

關於作者

Andrew HuntDavid Thomas 共同創立 Pragmatic Bookshelf,是敏捷宣言的 17 位原始作者之一。Thomas 創造了「DRY」與「Code Kata」,並合著《Programming Ruby》(Pickaxe 書);Hunt 專注於團隊如何學習、溝通與維持品質。他們共同撰寫了《The Pragmatic Programmer》,這是有史以來最具影響力的軟體書籍之一。