ousterhout-quality-program

作者:Rob Zapp尚無安裝尚無按讚更新於 2026年9月21日分類: 程式碼審查

它能做什麼

每當撰寫或審查的程式碼創建或更改邊界時使用——新的模組、類別、元件、輔助函式、hook、服務或包裝器;任何共享程式碼的抽取或集中;任何「讓我們讓它可重用」的時刻——以及在明確審查、重構或設計模組時使用。判斷一個抽象是否值得存在:模組深度、是否隱藏設計決策、重複程式碼是保護共享不變式還是僅僅押韻、介面是否穩定。防止機械式的 SOLID/Clean Code 產生許多淺層類別。也定義了讀者成本測試(對人類和代理來說易於閱讀和修改的程式碼)以及將現有程式碼庫重構到此標準的程序。

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

SKILL.md

---
name: ousterhout-quality-program
description: 每當撰寫或審查的程式碼創建或更改邊界時使用——新的模組、類別、元件、輔助函式、hook、服務或包裝器;任何共享程式碼的抽取或集中;任何「讓我們讓它可重用」的時刻——以及在明確審查、重構或設計模組時使用。判斷一個抽象是否值得存在:模組深度、是否隱藏設計決策、重複程式碼是保護共享不變式還是僅僅押韻、介面是否穩定。防止機械式的 SOLID/Clean Code 產生許多淺層類別。也定義了讀者成本測試(對人類和代理來說易於閱讀和修改的程式碼)以及將現有程式碼庫重構到此標準的程序。
---

# Ousterhout 品質計畫

## 概述

模組的工作是將複雜性隱藏在一個小介面後面。核心衡量標準是**深度**:一個深度模組提供一個簡單的介面來涵蓋大量功能;一個淺層模組的介面幾乎和其實作一樣複雜,因此毫無價值。複雜性是當變更迫使你理解或觸及你原本不預期的程式碼時所感受到的——Ousterhout 指出兩個來源:**依賴性**(你無法改變 A 而不改變 B)和**模糊性**(重要資訊不明顯)。

Ousterhout 本身告訴你一個好模組的*感覺*。它最強大的是結合其他幾個視角,告訴你邊界應該在哪裡,以及如何安全地朝那邊界移動。這個技能就是那個結合的視角。

## 審查實際出錯的地方

這個技能存在的兩個失敗點——在代理人撰寫的程式碼中反覆觀察到——是在**修正方法**,而非是否拆分的判斷本身:

1. **淺層修正。** 面對六個 `as unknown as` 的轉型,未經輔助的審查者會將它們集中成一個通用的 `castRows<T>()` 輔助函式——看起來更整潔,但模糊性依然存在。深層修正是使用有型別的 row→domain 映射器,並先釘住測試(套用 Parnas:轉型是缺少邊界的味道;Beck:先證明映射正確再移動它)。整理味道不等於消除味道。
2. **反射式抽取。** 面對三個兄弟元件中重複的更新邏輯,每個未經輔助的審查者都說「抽取共用輔助函式」——DRY 的反射動作。這個計畫的規則,延伸 Metz:等待不變式,而非第三個相似項——當程式碼保護共用規則時才集中,不是因為它們押韻。

當你發現自己要建議修正時,請用兩個標準檢視:它是消除模糊性還是只是搬移模糊性?抽取是保護不變式還是僅僅去重形狀?

## 比例門檻

當變更沒有新增任何匯出/可匯入名稱,沒有建立新模組/類別/元件/輔助函式/Hook/服務/包裝器,且沒有集中任何東西時,跳過此視角。純重命名、機械式代碼修改、設定/資料編輯和單行修正皆免除。若有疑慮,只執行兩個核心測試(深度、不變式)並停止。

## 完整規則

每一段產生或審查的程式碼在任務完成前都必須通過 Ousterhout 視角——不僅是明確的設計審查——除非變更低於比例門檻(無新邊界,無集中:重命名、代碼修改、設定編輯)。兩個測試:(1) **深度**——新介面必須隱藏遠多於它暴露的東西;介面複雜度與包裝物相當則毫無價值。(2) **不變式**——只有當抽取的共用程式碼保護共用規則時才抽取,絕不因為三個地方相似;修正必須消除模糊性,而非搬移它(將六個轉型集中成一個輔助函式仍是六個轉型)。當變更建立或重塑邊界時,先找出已建立產品如何解決此形狀與規模的問題,並採用其慣例,除非有明確理由不採用(訓練中回想的模式是主張,不是來源),然後執行以下檢查。

## 何時使用

- 判斷一個新類別/函式/Hook 是否值得它的介面,或只是淺層的傳遞。
- 檔案超過大小門檻,且你在決定*如何*拆分,而不只是是否拆分。
- 重複程式碼誘惑你抽取共用輔助函式。
- 設計或審查圍繞商業規則的邊界(授權範圍檢查、金錢/四捨五入規則、狀態機轉換守衛、資料保留規則)。
- 介面即將增加參數或特殊案例。
- 將現有程式碼庫帶到此標準——見下方「將現有程式碼庫重構至此標準」。

**不適用於:** 瑣碎的機械式編輯,或專案慣例已規定結構時——見上方比例門檻。當有 `karpathy-guidelines` 可用時,請依其進行精準變更紀律,並搭配測試驅動開發技能作為重構安全網。

## 視角

每個視角增加一個問題。Ousterhout 是主幹;其他視角修正其盲點。

| 觀點 | 它增加的問題 | 何時會被覆蓋 |
|---|---|---|
| **Ousterhout** — 深層模組 | 這個介面隱藏的東西是否多於它暴露的? | 預設主軸。 |
| **Parnas** — 資訊隱藏 | 這個模組隱藏了什麼設計決策(可能會改變)? | 模組應該是深層的*原因*。如果它沒有隱藏任何會改變的東西,深度只是表面功夫。 |
| **Brooks** — 本質 vs 偶然 | 這是移除偶然複雜度,還是只是重新定位本質領域複雜度? | 取消那些「重構」只是搬移混亂卻沒有縮小它的行為。 |
| **Evans** — 領域驅動設計 | 這個邊界是用領域語言命名,而非通用工具語言嗎? | 重新命名 `utils`/`helpers` — 用這個倉庫實際擁有的不變式來命名邊界。 |
| **Fowler** — 重構 / 味道 | 向更深設計邁進的最小安全步驟是什麼? | 把「應該更深」轉化為通過測試的具體步驟。 |
| **Beck** — 簡單設計,測試優先 | 在加深接縫前,我是否已證明目前行為? | 防止過早架構設計。先讓它運作並通過測試,再加深正確的接縫。 |
| **Hickey** — 簡單 vs 容易 | 這是否交織了不相關的概念,還是真正一個概念? | 淺層輔助通常是*容易*(附近、快速),而非*簡單*(少量交織概念)。偏好簡單。 |
| **Metz** — 複製優於錯誤抽象 | 這段重複程式碼是保護共享不變式,還是只是看起來相似(本程式規則,擴展 Metz)? | Metz:複製比錯誤抽象便宜 — 反而將錯誤抽象內聯回來,而非扭曲它。本程式擴展她的規則:**不要**因為重複就集中管理;只有在保護真正不變式時才集中。容忍複製直到不變式顯現。 |
| **Hyrum's Law** — 可觀察行為 | 呼叫者會依賴超出此介面契約的行為嗎? | 主張小且穩定的介面:每個可觀察行為最終都會成為承重行為。 |

## 組合配方

依此順序套用 — 後面的觀點只有在前面的通過後才重要:

1. **Metz — 入場門檻。** 這個邊界/抽象是否值得存在?本程式規則,擴展 Metz:只有當程式碼保護共享規則時才抽取 — 三個相似的並非顯現的不變式。如果否,至此停止。
2. **Parnas / Ousterhout** — 將易變決策(授權範圍、四捨五入規則、轉換守衛、保留規則)隱藏在深層模組後。
3. **Evans** — 用領域語言命名該模組,而非 `utils`。
4. **Beck / Fowler** — 對現有程式碼,用測試固定目前行為,然後以小且安全的步驟重構。對新產生的程式碼沒有目前行為可固定 — 改寫定義預期行為的測試。
5. **Hickey** — 拒絕因工作流程相似而混合不相關概念的介面。

## 結構性反模式

**機械式 SOLID / Clean Code 產生淺層模組。** 教條式解讀 — 每個類別一個責任,抽取每個函式,保持一切微小 — 產生一群介面複雜如其本體的類別。當規則說「拆分它」,問拆分隱藏了什麼*決策*(Parnas)以及它是否隱藏的多於暴露的(Ousterhout)。如果它沒有隱藏任何會改變的東西,就不要拆分。這個守護在重構壓力下(「整理這裡」、「這個檔案太大」)最重要 — 在冷靜分析時,審查者已經抗拒;在重構中間,帶著產生可見變更的任務時,淺層檔案群就會被寫出來。

## 常見錯誤

- **只因大小拆分。** 一個 400 行的查詢模組隱藏一個連貫決策,可能比四個各 100 行且各自洩漏相同連接的模組更深。
- **拆分命名為 `helpers`/`utils`。** 如果無法用領域語言命名(Evans),邊界可能錯誤。
- **第二次出現就抽取。** 本程式規則,擴展 Metz:等待不變式,而非第三個相似。
- **未固定行為就加深。** Beck:沒有測試證明目前行為,加深重構就是重寫。
- **將直通視為模組。** 轉發參數的包裝器增加介面但不隱藏任何東西 — 定義上是淺層。
- **誤將押韻當不變式。** 共享不變式的最佳證據是共變:複製品在歷史中一起修正或變更(同一錯誤在兩處修正)。獨立變更的相似是押韻;保留它們的複製。
- **整理味道而非移除。** 將六個轉型集中成一個通用轉型輔助是同樣模糊的整齊版本。深層修正是命名轉型所掩蓋的邊界。

## 讀者成本:第三個測試

深度與不變式決定邊界是否應存在。讀者成本決定周圍程式碼是否易於變更。下一個讀者,不論是人還是代理,為了安全變更必須載入的每一行都要付出代價。代理以代幣付費,並透過文字搜尋、部分閱讀及型別檢查/測試迴圈導航,因此相同缺陷對他們代價更高。請問:

- **可尋找性?** 每個概念一個名稱,且在所有地方拼寫一致,可透過純文字搜尋找到。缺陷:名稱由字串組合而成、透過匯入副作用連結、重新匯出鏈條隱藏定義、同一概念有兩個名稱。
- **讀者能否提前停止?** 合約位於檔案頂端或匯出上方:它承諾什麼、隱藏什麼、絕不做什麼。缺陷:合約只能透過閱讀主體推導出來。
- **機器可檢查?** 每個邊界的輸入輸出都有精確型別,讓型別檢查取代閱讀呼叫者。缺陷:`any`、裸字典、意義藏在主體的布林旗標。
- **耦合是否可見?** 必須一起變更的地方被強制(共用型別、測試、單一來源),否則在兩端標記。隱藏耦合的證據是歷史中共同變更,但程式碼中無任何提及。
- **無噪音?** 無重述程式碼的註解、無註解掉的程式碼、無死分支、無變更歷史註解、無舊路徑與替代路徑並存。
- **可預測?** 版面配置遵循倉庫既有模式;測試放在讀者會找的地方且能獨立執行。

檔案大小故意未列。非常大的檔案是尋找第二個隱藏決策的理由,絕非拆分的理由:讀者可搜尋並閱讀範圍,且不隱藏任何內容的拆分會增加介面而非減少負擔。

對於程式碼內標記與倉庫代碼地圖,若可用請使用 `context-audit`:其 `AIDEV-NOTE:` 錨點(一個不可恢復的事實加上來源參考,最多兩行,位於現場)是無法強制的耦合慣例標記。

## 將現有程式碼庫重構至此標準

改造的評判標準與新程式碼相同;不同的是順序與克制。大部分程式碼庫應保持不動。

1. **普查,只讀。** 列出邊界(模組、服務、共用輔助)。每筆記錄:隱藏的決策或「無」;介面大小與主體比;歷史中的共同變更夥伴;讀者成本缺陷。暫不更動。
2. **依變動頻率排序,不依醜陋度。** 優先順序為程式碼變動頻率乘以閱讀成本。運作正常的冷程式碼保持原狀,不論多淺。必要的領域複雜度保持原位(Brooks)。
3. **每個發現指派一個解決方案:**
   - 不隱藏任何東西的穿透層或包裝器:刪除它,呼叫者直接使用被包裝的;
   - 被旗標和特殊案例扭曲的錯誤抽象:內聯回去(Metz),再尋找真正的不變量;
   - 共享一個決策的淺層兄弟:合併成一個介面;
   - 外洩的決策(呼叫者知道格式、規則、結構):拉回擁有它的模組;
   - 通用名稱(`utils`、`helpers`、`manager`):依隱藏的決策重新命名,或解散到呼叫者;
   - 未定型邊界:定型,並用映射器取代原本的轉型;
   - 隱藏耦合:強制執行,或在兩端標記;
   - 噪音:刪除。

   獨立變更的韻律不需解決。
4. **先固定行為。** 未有測試證明所觸及程式碼的現行行為前,不開始解決方案(Beck)。重構須保留行為;行為變更另作提交。
5. **將工作切成單一代理可獨立完成的單元。** 每單元一個邊界。每單元命名其擁有的檔案、必須維持的合約,以及能獨立驗證的指令。兩個同時進行的單元不寫同一檔案;共用檔案(桶、註冊表、路由表)由單一擁有者管理或等待整合。多個單元依賴的介面變更先行,作為獨立單元。
6. **衡量結果。** 選擇一個代表性變更開始前,計算讀者為完成它必須載入的檔案與行數;完成後再計算。匯出名稱與總行數應下降或持平。增加介面的重構須說明理由。
7. **停止** 當剩餘部分為冷、必要或韻律。

相關技能(若有):`repo-review`(設計型別)產生普查作為建議性產物;`design-cleanup` 執行意外複雜度的修正與重掃迴圈;`context-audit` 新增錨點與代碼地圖;`ousterhout-build-deep` 是代理執行單元時的作者時間檢查清單。

## 本技能定位

此技能為審查與判斷層:用來決定抽象是否深刻、是否以正確決策命名、是否值得抽取。`find-shared-code` 在掃描近期歷史尋找值得共享的程式碼時,將其作為入場測試。下方附錄說明各作者的推理。

---

## 附錄:深入鏡頭

每位作者捕捉的失敗模式,以及他們給你的唯一行動。上表為快速參考;此處為背後推理。

### Ousterhout — 深度模組(主幹)

*《軟體設計哲學》。*

- **深度** = 利益(隱藏的功能)÷ 成本(介面複雜度)。深度模組以少量介面隱藏大量內容。淺層模組的介面幾乎與主體一樣複雜,故無收益。
- **複雜度** 是系統中使理解或修改困難的任何事物。兩個來源:
  - **依賴性** — 你無法改變一部分而不觸及另一部分。
  - **模糊性** — 重要資訊從程式碼中不明顯。
- **症狀:** 變更放大(一個決策,多處編輯)、認知負擔(你必須記憶多少)、未知的未知(你無法判斷變更會影響哪些程式碼)。
- **關鍵行動:** 將複雜度向下拉——模組吸收困難案例,讓呼叫者不必處理。配置參數與穿透層將複雜度推向呼叫者;那是淺層。

捕捉:洩漏實作的介面;無助益的輔助函式。

### Parnas — 資訊隱藏(深度重要原因)

*《系統模組分解標準》(1972)。*

- 將重點放在**可能改變的設計決策**上,而非計算步驟。每個模組隱藏一個這樣的決策。
- 這是深度模組的直接前身。模組之所以深度,是因為它隱藏了一個否則會影響呼叫者的決策。

陷阱:一個「模組」若沒有隱藏任何易變的東西——它的深度只是表面功夫。問問自己:在這個介面背後有什麼變化是呼叫者永遠看不到的?如果答案是「沒有」,那這個邊界只是裝飾。

### Brooks — 本質複雜度與偶然複雜度

*沒有銀彈。*

- **本質**複雜度是領域本身固有的(評估確實如此複雜)。**偶然**複雜度是我們的工具和結構強加的。
- 只有偶然複雜度是可以移除的。將本質領域複雜度從一個檔案移到另一個檔案的重構,並沒有真正改變什麼。

陷阱:偽裝成簡化的重新排列。問問自己:總複雜度是降低了,還是只是移動了?

### Evans — 領域驅動設計

*領域驅動設計。*

- 邊界應該以領域的**普遍語言**命名,而非通用工具詞彙。名為 `helpers` 的模組毫無意義;名為 `AccessScope` 或 `PricingPolicy` 的模組則命名了一個不變式。
- 有界上下文防止商業不變式跨接縫洩漏。

陷阱:正確分解但命名無意義。如果你無法用領域語言命名模組,可能是邊界切錯了地方。

### Fowler — 重構與程式碼異味

*重構。*

- 提供具體、安全且有名稱的操作(抽取函式、移動欄位、以多型取代條件)來從現有設計走向更深層設計。
- 每個操作都保留行為且規模小,因而可逆。

陷阱:「這應該更深」與知道下一次提交之間的落差。Ousterhout 設定目標;Fowler 指引路徑。

### Beck — 簡單設計,先測試

*測試驅動開發;極限編程。*

- Beck 發表的簡單設計四規則:通過測試、無重複、揭示意圖、元素最少。本程式遵循後來 Fowler/Haines 的調整——意圖優先於重複——因為它服務於本程式擴展 Metz 不變式規則(見 Metz,下文):
  不要在能命名保護意圖之前對重複動手。
- 先測試是防止過早架構的剎車。先讓程式能運作並證明行為,然後再加深測試所保護的接縫。

陷阱:在行為確定前就建構架構。沒有測試證明現有行為,「加深」重構就是未驗證的重寫。

### Hickey — 簡單與容易

*Simple Made Easy。*

- **簡單** = 不交織:一個概念,沒有與其他概念交織(客觀)。
- **容易** = 近手、熟悉、快速可及(相對於你)。
- 兩者獨立。淺層輔助通常是*容易*的——寫起來快、離得近——但如果交織了不相關的關注點,就不是*簡單*的。

陷阱:便利偽裝成設計。即使交織的寫起來快,也要偏好保持概念不交織的結構。

### Metz — 寧可重複也不做錯誤抽象

*「錯誤抽象」(2016)。*

- 重複遠比錯誤抽象便宜。過早抽象會迫使未來所有呼叫者都必須繞過從未對所有人都成立的假設。
- 抽象錯誤時,Metz 的解方是將其內聯回去,讓重複回歸,而非強行調整以適應從未設計的情況。
- **本程式擴展 Metz 的規則:不要因為程式碼重複就集中化。集中化應該是為了保護真正的、共享的不變式。** 在不變式顯現之前,容忍重複。

陷阱:過度集中化——淺層共享輔助函式,大家都必須繞過它。這是機械式「不重複原則(DRY)不擇手段」的對立面。

### Hyrum 定律 — 可觀察行為成為契約

*「當使用者足夠多時,系統的每個可觀察行為都會被某人依賴。」*

- 介面*碰巧*做的事——排序、時序、錯誤訊息——終將有人依賴。因此你暴露的表面比你文件記錄的還大。
- 這支持 Ousterhout 偏好**小且穩定的介面**:暴露越少,意外成為承重的可能越低。

陷阱:過寬的介面會僵化。每多一個可觀察行為,都是未來的限制。

### 它們如何結合

- **Parnas → Ousterhout:** 隱藏易變決策 → 模組即深度。
- **Brooks:** 確認深度是移除複雜度而非搬移。
- **Evans:** 用領域語言命名邊界。
- **Beck → Fowler:** 鎖定行為,然後以小且安全的步驟重構。
- **Metz:** 抵抗集中化,直到不變式真實存在。
- **Hickey:** 介面保持單一概念。
- **Hyrum:** 介面保持小巧以維持穩定。

危險在於將 Ousterhout 與機械式解讀 SOLID 或 Clean Code 混合:會產生許多介面淺薄的小類別和函式——正好與深度模組相反。Ousterhout 與 Metz 互為制衡,是解藥。

標籤

designarchitecturereviewrefactoringousterhout