software-design-philosophy

software-design-philosophy

熱門

透過深層模組、資訊隱藏與策略性程式設計來管理軟體複雜度。當使用者提及「模組設計」、「API 太複雜」、「淺層類別」、「複雜度預算」、「策略 vs 戰術」、「深層模組」、「資訊洩漏」、「轉送方法」、「這段程式碼過度設計」或「簡化這個設計」時使用。也適用於審查介面簡潔度、評估抽象是否值得、決定是否該寫註解,或選擇通用 vs 專用方法。涵蓋深層與淺層模組、複雜度紅旗,以及作為設計文件的註解。關於程式碼品質,請參閱 clean-code。關於架構邊界,請參閱 clean-architecture。

1698星標
171分支
更新於 2026/7/16
SKILL.md
readonlyread-only
name
software-design-philosophy
description

Manage software complexity through deep modules, information hiding, and strategic programming. Use when the user mentions "module design", "API too complex", "shallow class", "complexity budget", "strategic vs tactical", "deep module", "information leakage", "pass-through method", "this code is over-engineered", or "simplify this design". Also trigger when reviewing an interface for simplicity, evaluating whether an abstraction is pulling its weight, deciding whether a comment is worth writing, or choosing between general-purpose and special-purpose approaches. Covers deep vs shallow modules, red flags for complexity, and comments as design documentation. For code quality, see clean-code. For architecture boundaries, see clean-architecture.

軟體設計哲學框架

一個實用框架,用於管理軟體工程的基本挑戰:複雜度。在設計模組、審查 API、重構程式碼或提供架構決策建議時,應用這些原則。

核心原則

撰寫軟體的最大限制,是我們理解自己所建立系統的能力。 複雜度是敵人:它讓系統難以理解、難以修改,並成為錯誤的來源。評估每個設計決策時,問自己「這會增加還是減少系統的整體複雜度?」——目標不是零複雜度,而是最小化不必要的複雜度,並將必要的複雜度集中到可管理的地方。

評分

目標:10/10。 在審查或建立設計時,根據滿足多少個快速診斷列來評分(每列約 1.25 分),然後對照區間進行合理性檢查:

  • 9-10 — 深層模組,介面遠比實作簡單;沒有資訊洩漏(實作可以改變而不影響呼叫者);介面註解捕捉設計意圖;設計改進是常態。八項診斷全部通過。
  • 6-8 — 大致深層,但有一兩個洩漏、淺層類別或未記錄的抽象。5-6 項診斷通過。
  • 3-5 — 類別炎或時間分解、反覆洩漏、僅重述程式碼的註解。2-4 項診斷通過。
  • ≤2 — 戰術龍捲風程式碼:淺層模組、普遍洩漏、未記錄設計意圖。0-1 項診斷通過。

務必說明當前分數、未通過的診斷列,以及每項需要哪些具體改變才能達到 10/10。

軟體設計框架

管理複雜度並產出易於理解和修改的系統的六項原則:

1. 複雜度及其成因

核心概念: 複雜度是指系統結構中任何使其難以理解和修改的部分。它表現為三種症狀——變更放大、認知負荷和未知的未知——並有兩個成因:依賴關係和模糊性。

關鍵見解:

  • 變更放大:一個簡單的變更需要修改多處
  • 認知負荷:開發者必須記住太多資訊才能進行變更
  • 未知的未知:不明顯需要改變什麼或哪些資訊相關——這是最糟的症狀
  • 複雜度是漸進的——它由數百個小決策累積而成(「千刀萬剮」),因此每個決策都很重要

程式碼應用:

情境 模式 範例
變更放大 集中共享知識 提取顏色常數,而不是在 20 個檔案中硬編碼 #ff0000
認知負荷 減少開發者需知道的資訊 open(path) 而非要求緩衝區大小、編碼、鎖定模式
未知的未知 讓依賴關係明確 型別系統和介面揭示變更影響的範圍
模糊性 精確命名 numBytesReceived 而非 nretryDelayMs 而非 delay

請參閱 references/complexity-symptoms.md 當你需要指出程式碼庫有哪種症狀後再修復——每種症狀的辨識測試、依賴關係分類(語法/語意/時間/隱藏)、C = Σ(cp·tp) 成本公式,以及一個 10 列的紅旗表格。

2. 深層 vs 淺層模組

核心概念: 最好的模組是深層的:強大的功能背後是簡單的介面。淺層模組的介面相對於其提供的功能來說過於複雜——它們增加複雜度而非隱藏它。

為什麼有效: 介面是模組對系統其他部分施加的成本;實作則是效益。因此,一個比你自己重新實作還難學的方法就是淨負值——深度,而非程式碼行數,決定了一個模組是否值得存在。

關鍵見解:

  • 深度 = 提供的功能 / 施加的介面複雜度(Unix 檔案 I/O 是深層的;薄薄的 Java I/O 包裝是淺層的)
  • 「類別炎」:建立過多小型淺層類別的疾病——每個介面都增加認知負荷
  • 小方法本身並非好事;深度比大小更重要
  • 最好的抽象將顯著的複雜度隱藏在幾個簡單概念之後

程式碼應用:

情境 模式 範例
深層模組 將複雜度隱藏在簡單 API 後 file.read(path) 隱藏磁碟區塊、快取、緩衝、編碼
類別炎治療 合併相關的淺層類別 RequestParser + RequestValidator + RequestProcessor → 一個 RequestHandler
介面簡潔 更少的參數、更少的方法 config.get(key) 搭配合理的預設值,而非 15 個建構子參數

請參閱 references/deep-modules.md 當判斷一個抽象是否值得——深度比的前後程式碼、類別炎治療的詳細步驟,以及案例研究(Unix I/O、GC、TCP/IP)。

3. 資訊隱藏與洩漏

核心概念: 每個模組應封裝其他模組不需要的知識。資訊洩漏——一個設計決策反映在多個模組中——是軟體設計中最重要的紅旗之一。

為什麼有效: 存在於一個模組中的決策可以在那裡改變,其他地方不變;同樣的決策洩漏到 N 個模組,會將一次編輯變成 N 次編輯,而且編譯器不會提醒你。隱藏正是將變更放大轉回局部變更的方法。

關鍵見解:

  • 時間分解導致洩漏:按何時發生來拆分程式碼會強制跨模組共享知識——應按知識來組織
  • 透過資料格式、協定或共享假設的後門洩漏是最微妙的形式
  • 裝飾者經常洩漏——它們暴露被裝飾的介面
  • 如果兩個模組共享知識,要麼合併它們,要麼建立一個封裝該知識的新模組

程式碼應用:

情境 模式 範例
格式洩漏 集中序列化 一個模組擁有 JSON 編碼/解碼,而非到處 json.dumps
時間分解 按知識而非時間組織 將「讀取設定」和「套用設定」合併為一個設定模組
協定洩漏 抽象傳輸細節 MessageBus.send(event) 隱藏 HTTP vs. gRPC vs. 佇列

請參閱 references/information-hiding.md 當一個變更迫使你同步編輯兩個模組時——四種洩漏形式(介面、後門、時間、裝飾者)附程式碼、五種減少策略、HTTP 處理案例研究,以及一個偵測表格。

4. 通用 vs 專用模組

核心概念: 設計「某種程度上通用」的模組:介面足夠通用以支援多種用途,實作則處理當前需求。問:「能涵蓋我所有當前需求的最簡單介面是什麼?」

為什麼有效: 違反直覺的是,通用介面通常簡單——特殊情況方法會隨著需求增長而倍增,而一個通用方法可以吸收它們。陷阱是另一方向:當前需求不需要的通用性是投機複雜度,為可能永遠不會到來的使用案例付出代價。

關鍵見解:

  • 「某種程度上通用」是太特定和太通用之間的最佳點
  • 將複雜度向下推:底層模組應處理困難情況,讓上層保持簡單
  • 設定參數通常代表未能做出決定——每個參數都是推給呼叫者的複雜度
  • 有疑問時,先實作更簡單、更通用的方法

程式碼應用:

情境 模式 範例
API 通用性 為概念而非單一使用案例設計 text.insert(position, string) 而非 text.addBulletPoint()
減少設定 自動決定行為 自動偵測檔案編碼,而非 encoding 參數
避免過度專用化 一個通用方法勝過多個特定方法 store(key, value, options) 而非 storeUser()storeProduct()storeOrder()

請參閱 references/general-vs-special.md 當選擇介面應多通用時——「所有當前需求的最簡單介面」測試、設定參數反模式,以及將複雜度向下推的詳細步驟。

5. 作為設計文件的註解

核心概念: 註解應描述程式碼中不明顯的內容:設計意圖、抽象理由、不變量和假設。「好的程式碼會自我文件化」是一個迷思,僅對低層實作細節成立。

為什麼有效: 程式碼只能記錄做了什麼——永遠無法說明為什麼選擇這種方法而非其他,或它默默假設了什麼。這個理由是系統中最易流失的資訊:它只存在於作者腦中,一旦作者離開就消失,因此註解是捕捉它的唯一機會。

關鍵見解:

  • 四種類型:介面註解(最重要——它們定義抽象)、資料結構成員註解、實作註解、跨模組註解
  • 先寫註解(註解驅動設計)以在寫程式碼前釐清思路
  • 不要重複程式碼已說明的內容;將註解放在它們描述的程式碼旁邊,並一起更新
  • 如果註解很難寫,設計可能太複雜

程式碼應用:

情境 模式 範例
介面註解 描述抽象,而非實作 「回傳最接近位置的 widget,若無則回傳 null」
資料結構註解 解釋不變量 「列表按優先級降序排序;平手時按插入順序」
實作註解 解釋為什麼,而非做什麼 「// 二元搜尋:列表始終排序,可容納 10 萬+ 項目」
跨模組註解 連結相關決策 「// 此逾時必須與 RetryPolicy.java 中的重試間隔一致」

請參閱 references/comments-as-design.md 當撰寫或審查註解且不確定應包含什麼時——四種註解類型附範例、註解驅動設計程序,以及對自我文件化程式碼迷思的反駁。

6. 策略 vs 戰術程式設計

核心概念: 戰術程式設計快速讓功能運作,並隨著每個捷徑累積複雜度。策略程式設計投入 10-20% 的額外努力在良好設計上,將每個變更視為改善結構的機會。

為什麼有效: 戰術速度是借來的:每個捷徑讓未來變更更困難,而策略投資會複利——策略性設計的系統在幾個月內就會變得更易於開發。

關鍵見解:

  • 戰術龍捲風:快速交付但留下破壞的開發者——短期受讚揚,長期具破壞性
  • 你的主要工作是做出一個恰好能運作的偉大設計,而不是一個恰好有設計的運作程式碼
  • 新創公司最需要策略程式設計——早期捷徑會隨著團隊成長複合成癱瘓性債務
  • 每個變更都是投資機會:讓程式碼變得更好一點;重構是每個功能的一部分,而非特殊事件

程式碼應用:

情境 模式 範例
戰術陷阱 抵制快速骯髒的修復 不要為「就這一個特殊情況」添加布林參數
策略投資 在功能開發期間改善結構 在新增功能時重構尷尬的模組介面
設計審查 評估結構,而非僅正確性 問「這會讓系統更簡單嗎?」而不只是「它能運作嗎?」

請參閱 references/strategic-programming.md 當決定一個變更應投入多少設計努力,或為其辯護時——10-20% 投資數學、戰術龍捲風模式,以及為什麼新創公司最需要策略程式設計。

常見錯誤

錯誤 為什麼失敗 修正
建立過多小型類別 類別炎增加介面而無深度;每個邊界都是認知負擔 將相關的淺層類別合併為更深層的模組
按時間順序拆分模組 「讀取、處理、寫入」強制跨模組共享知識 將共享知識的程式碼分組到一個模組
在介面中暴露實作 呼叫者依賴內部實作;變更會傳播 圍繞抽象設計介面;隱藏格式和協定
將註解視為可選 設計意圖和假設遺失;新人猜錯 先寫介面註解;與程式碼一起維護
萬物皆設定參數 參數推給呼叫者是你拒絕做出的決定(見 §4) 自動決定行為;提供合理的預設值
快速骯髒的戰術修復 捷徑累積直到系統無法運作 投入 10-20% 額外努力;將每個變更視為設計機會
轉送方法 僅轉送引數的方法增加介面但無功能 將轉送合併到呼叫者或被呼叫者
為特定使用案例設計 專用介面累積特殊情況 問:涵蓋所有當前需求的最簡單介面?

快速診斷

問題 如果否 行動
你能用一句話描述每個模組嗎? 模組做太多或缺乏目的 拆分為連貫、可描述的職責
介面是否比實作簡單? 模組淺層——複雜度向外洩漏 隱藏更多;將淺層類別合併為更深層的
你能在不影響呼叫者的情況下改變實作嗎? 資訊正在跨邊界洩漏 將洩漏的知識封裝在一個模組中
介面註解是否描述抽象? 設計意圖遺失;模組將被誤用 記錄模組承諾什麼,而非如何運作
設計討論是否納入程式碼審查? 審查捕捉錯誤但不捕捉複雜度增長 將「這是否減少複雜度?」加入審查標準
每個模組是否隱藏一個重要的設計決策? 模組圍繞程式碼而非資訊組織 重新組織,使每個模組擁有特定知識
新人能否在不閱讀實作的情況下理解模組邊界? 抽象未記錄或洩漏 改善介面註解;簡化介面
你是否花費 10-20% 的時間在設計改善上? 債務隨著每個功能累積 在每個 PR 中包含設計改善

延伸閱讀

如需包含詳細範例的完整方法論:

關於作者

John Ousterhout 是史丹佛大學電腦科學系的 Bosack Lerner 教授,也是 Tcl 腳本語言和 Tk 工具包的創作者。他從史丹佛 CS 190 課程中發展出《軟體設計哲學》,將數十年的系統建構經驗提煉為適用於各種語言和規模的原則。