軟體設計哲學

作者:Rob Zapp尚無安裝尚無按讚更新於 2026年10月8日分類: 工程

它能做什麼

在撰寫、修改或審查程式碼時使用,當變更新增了可匯出或可匯入的名稱、建立模組、類別、元件、輔助函式、hook、服務或包裝器、集中重複程式碼,或更改 API 時使用。Ousterhout 規則(深層模組、資訊隱藏、降低複雜度)加上共用程式碼的不變測試、讀者成本測試,以及最後必須附上的設計說明。

安裝會在你的 AgentsRoom 桌面版開啟這個條目。如果還沒有安裝應用,你會被帶到下載頁面。

SKILL.md

---
name: 軟體設計哲學
description: 在撰寫、修改或審查程式碼時使用,當變更新增了可匯出或可匯入的名稱、建立模組、類別、元件、輔助函式、hook、服務或包裝器、集中重複程式碼,或更改 API 時使用。Ousterhout 規則(深層模組、資訊隱藏、降低複雜度)加上共用程式碼的不變測試、讀者成本測試,以及最後必須附上的設計說明。
---

# 軟體設計哲學(John Ousterhout)

## 何時使用此技能

當你設計、撰寫、修改或審查程式碼時,使用此技能。它適用於模組設計、API 變更、拆解、重構、命名、註解、測試及效能工作。當變更感覺不自然,或一個變更散布在多個檔案時,也應使用此技能。

## 需修正的偏誤

可運作的程式碼不等於簡單的程式碼。小片段、熟悉的模式、旗標、包裝器及額外文件可能使設計更複雜。當它們增加讀者必須知道的內容,或當它們將知識洩漏到其他模組時,就會造成複雜性。

## 決策規則

- 以降低複雜度的程度來衡量設計。偏好能減輕讀者負擔的設計。複雜度有四個徵兆:一個變更需要在多處編輯;依賴關係被隱藏;步驟必須按固定順序執行;讀者必須記住許多事實。
- 將設計視為持續的工作。第一個可運作的補丁若使後續變更更困難,則尚未完成。對介面、模組拆分或抽象的決策,應比較兩個或多個可能的設計。
- 偏好深度模組。深度模組擁有小型介面並隱藏大量複雜性。拒絕傳遞服務、薄型函式庫包裝器及小型輔助模組。拒絕任何只增加名稱但不減輕讀者負擔的抽取。
- 以呼叫者必須知道的內容設計介面,而非實作如何運作。避免脆弱的設定序列、模式旗標、配置旋鈕及顯示內部選擇的參數。
- 隱藏可能改變的決策。例子有內部表示、儲存結構、協定、檔案格式及效能技巧。帳務、正規化及邊緣案例也是例子。將每個決策保留在擁有該知識的模組內。
- 將複雜度拉到擁有細節的模組中。當它能給呼叫者更簡單的契約並移除每個呼叫點的重複工作時,接受較複雜的實作。
- 使模組在適當層級上通用。不要為單一呼叫者量身打造模組。不要為未來需求加入模糊的抽象。將罕見的邊緣案例排除在主要路徑外,並將特殊行為放在自己的位置。
- 以總體複雜度來合併或拆分模組。不要依大小、程式碼執行順序、習慣或外觀來合併或拆分。將相關狀態、行為、規則及決策放在一起。只有當新邊界更深且讀者能獨立理解雙方時才拆分。
- 使例外集合更小。盡可能改變介面或規則,使無效狀態無法發生。不要讓每個呼叫者重複相同的防禦程式碼。
- 使用註解來降低複雜度。寫下介面契約、必須保持正確的規則、隱藏的設計決策及其原因。也寫下呼叫者不必知道的困難事實。不要在註解中重複程式碼。不要用註解掩蓋糟糕的命名、糟糕的拆分或令人困惑的控制流程。
- 將命名、一致性及清晰度視為設計資訊。名稱告訴讀者抽象,而非機制。相關操作使用相同慣例。令人驚訝的程式碼會增加複雜度,即使它很短。
- 針對公開契約及穩定 API 撰寫測試。透過這些契約測試隱藏的複雜度及特殊案例。不要因測試容易而強迫淺層或洩漏的介面。
- 只有出於兩個原因之一,才加入效能變更、模式、範式或框架。它能降低此程式碼庫的複雜度,或證據顯示此權衡是必要的。將每個優化隱藏在穩定介面後。

## 訊號及對應回應

- 功能不自然,或一個變更散布多個檔案,或審查者必須尋找隱藏依賴。回應:尋找缺失的資訊隱藏及淺層模組。也尋找固定順序的步驟,以及呼叫者攜帶的複雜度。
- 你新增模組、層、服務、輔助、包裝器或外觀。或新增模式、選項、回呼或參數。回應:證明它隱藏的複雜度多於增加的。
- 你變更 API。回應:檢查一般呼叫者必須知道什麼。呼叫者不應需要呼叫順序、表示法或儲存。呼叫者不應需要傳輸、快取、協定或檔案格式。呼叫者不應需要內部工作流程或多個設定步驟。
- 你新增特殊案例、旗標、例外路徑、條件或呼叫者可見的容器。回應:先問擁有模組能做什麼。它可以移除無效狀態、隔離異常行為或提供更強的操作。
- 你拆分程式碼、抽取函式或新增變數。回應:檢查新邊界或名稱是否有意義。它不應只增加跳轉、傳遞狀態或呼叫者可見的中間步驟。
- 程式碼有階段如 `prepare`、`process` 和 `finalize`,或呼叫者必須分階段建立物件。回應:檢查時間順序是否為真正概念。若不是,圍繞穩定責任組織程式碼。
- 名稱模糊、命名機制、不一致或令人驚訝。回應:重新思考抽象邊界。不要接受幾乎正確的名稱。
- 註解冗長、重複程式碼、解釋令人困惑的介面,或顯示內部以說明用法。回應:改變抽象,或將缺失的契約移入介面。
- 你優化效能。回應:先測量,再隱藏優化。沒有證據顯示權衡必要,勿放棄模組深度或資訊隱藏。
- 你測試或審查。回應:查看公開行為及介面契約。也查看穩定 API 背後的隱藏複雜度及抽象後的特殊案例。

## 最終檢查清單

- 這個變更是否減少了理解、修改、驗證和擴展系統的工作量?
- 每個介面元素、包裝器、層、輔助工具、選項和名稱是否隱藏了足夠的複雜度以證明其存在的合理性?
- 重要的決策是否集中在一處?依賴關係是否清晰可見?呼叫者需要遵守的限制是否有記錄?可變更的內部實作是否受到保護?
- 常見情況是否能在不額外操作的情況下正常運作?罕見的控制、特殊案例、效能技巧和例外細節是否都不會干擾常見流程?
- 名稱是否精確且一致?註解是否是最新的,且不重複程式碼內容?程式碼是否遵循現有慣例,除非有新資訊需要更改?

## Gate

當變更新增一個其他程式碼可以匯出或匯入的名稱時,請使用完整檢查清單。當變更建立模組、類別、元件、輔助工具、hook、服務或包裝器,或將重複程式碼集中時,也請使用。重新命名、程式碼轉換、設定變更、資料變更和一行修正則不需要。

## 不變式測試:只共享會一起變更的程式碼

- 只有當共享程式碼保護一條你能命名的規則時才提取。證據是共同變更:歷史顯示這些複製品是一起修正或變更的。僅看起來相似但獨立變更的程式碼是押韻。押韻就保留為重複。三個相似區塊不代表一條規則。
- 修正必須消除問題,而非移動問題。六個轉型集中到一個通用轉型輔助工具仍是六個轉型。請寫出轉型所隱藏的型別映射器。
- 當抽象錯誤時,將程式碼放回內聯並讓重複回歸。不要用旗標扭曲抽象。
- 不要僅因程式碼大小而拆分。一個 400 行的模組隱藏一個決策,比四個 100 行且洩漏相同連接的模組好。
- 機械式閱讀 Clean Code 或 SOLID(非常小的函式,每個責任一個類別)會產生淺層模組。本技能優先於此壓力。

## 閱讀成本:第三項測試

深度測試和不變式測試決定邊界是否必須存在。閱讀成本測試決定邊界周圍的程式碼是否容易變更。下一個讀者(人或代理)必須為他們必須閱讀的每一行付出代價。代理以代幣付費。代理透過文字搜尋、部分閱讀、型別檢查和測試來尋找程式碼。

- **可尋找。** 每個概念使用一個名稱。到處拼寫相同,讓純文字搜尋能找到。缺陷:名稱由字串組成、透過匯入副作用接線、同一概念有兩個名稱。隱藏定義的重新匯出鏈也是缺陷。
- **早期停止。** 將契約放在檔案頂端或匯出上方。說明它承諾什麼、隱藏什麼、絕不做什麼。讓讀者能早點停止。
- **機器可檢查。** 在每個邊界輸入和輸出使用精確型別,讓型別檢查取代閱讀呼叫者。缺陷:`any`、純字典、意義只在本體的布林旗標。
- **可見耦合。** 兩處必須一起變更。用共用型別、測試或單一來源強制執行。若無法,兩處都標記。
- **無噪音。** 移除重複程式碼的註解和被註解掉的程式碼。移除死分支和記錄變更歷史的註解。移除舊路徑且不留在替代路徑旁。
- **可預測。** 遵循現有的儲存庫佈局。將測試放在讀者會找的地方,並讓它獨立執行。

檔案大小故意不在此清單中。非常大的檔案是尋找第二個隱藏決策的理由,絕不是拆檔的理由。

## 安全性

對現有程式碼,先寫測試以維持目前行為,再讓模組更深。對新程式碼,寫定義預期行為的測試。

## 設計說明(當 gate 適用時必填)

當 gate 適用時,請在 pull request 描述中加入標題為 `## Design note` 的章節。寫兩到四行:

- 你新增的每個邊界,以及它隱藏的決策。
- 你故意保留的每個重複,以及原因。
- 你接受的每個淺層部分,以及原因。

若 gate 不適用,請寫 `## Design note`,接著寫 `Gate not applicable: <原因>`。也請將設計說明放在最終步驟的摘要中。

## 審查模式

當你審查或測試其他代理或人員撰寫的程式碼時使用此章節。

1. 檢查設計說明。當 gate 適用且 pull request 沒有 `## Design note` 章節時,回報阻斷性發現。當說明與差異不符時,回報阻斷性發現。
2. 設計發現只有在同時符合兩個條件時才是阻斷性的:
   - 它命名了本技能的規則。該規則是決策規則、gate、不變式測試或閱讀成本項目之一。
   - 它陳述對讀者或下一次變更的具體成本。範例:「呼叫者必須知道儲存形態。」「一個概念有兩個名稱。」「上限變更需要修改三個檔案。」
3. 將其他所有設計觀察標記為非阻斷性。放在標題為「非阻斷性設計說明」的獨立清單中。非阻斷性說明絕不會將工作退回給建置者。
4. 不要將偏好回報為發現。不同名稱、檔案佈局或風格是偏好。只有當它違反命名規則且有具體成本時,才成為發現。
5. 當相同設計發現於第二次審查循環中再次出現時,請升級處理。不要第三次要求相同變更。

## 相關技能(安裝時)

- `find-shared-code`:僅報告最近歷史中值得共享的程式碼搜尋。使用本技能的不變式測試和深度測試。
- `refactoring` 和 `working-effectively-with-legacy-code`:邁向更深設計的安全步驟。本技能決定新邊界是否保留。

## 來源與授權

此技能建立於 GitHub 上 ciembor/agent-rules-books 倉庫中《A Philosophy of Software Design》的「迷你」規則(MIT 授權,提交版本 893a88a)。閘門、不變式測試、讀者成本測試、設計註記與審查模式是對這些規則的補充。該倉庫也包含了該書的完整規則。

標籤

設計架構ousterhout審查