# A Philosophy of Software Design
## 概要
John Ousterhout が Stanford University の CS 190(ソフトウェア設計演習)で 3 度の講義を通じて抽出した設計原則をまとめた書籍である。ソフトウェア設計の中心問題を**複雑性(complexity)の管理**という 1 点に絞り、複雑性を「変更増幅・認知負荷・未知の未知」という観測可能な症状として定義したうえで、モジュール分割・インターフェース設計・コメント・命名という具体的な水準まで一貫して降ろす。著者自身が「これは意見表明(opinion piece)であり、ソフトウェア設計についての会話を始めるための本である」と述べているとおり、実証研究ではなく約 25 万行のコードを書いた個人的経験からの抽出である点を明示している。(Source: Preface)
## 書誌情報
- **著者**: John Ousterhout(Stanford University 教授)
- **出版社**: Yaknyam Press(Palo Alto, CA)
- **版**: First Edition — 2018 年 4 月(v1.0)、2018 年 11 月(v1.01)
- **ISBN**: 978-1-7321022-0-0
- **構成**: 全 21 章 + Preface + 巻末の Summary of Design Principles / Summary of Red Flags(全 188 ページ)
- **原本**: `.raw/books/a-philosophy-of-software-design/`
## 成立の経緯(Preface より)
著者は「電子計算機のためのプログラムが書かれるようになって 80 年以上たつのに、それをどう設計すべきか・良いプログラムがどう見えるべきかについての議論は驚くほど少ない」という問題意識から本書を書いている。アジャイル開発のような開発プロセス、デバッガやバージョン管理のようなツール、オブジェクト指向・関数型プログラミング・デザインパターン・アルゴリズムといった技法については膨大な議論があるが、**ソフトウェア設計の中核問題はほぼ手つかずのままだ**というのが著者の見立てである。David Parnas の古典 "On the Criteria to be used in Decomposing Systems into Modules"(1971)以降、設計技術の水準はその論文からさほど進んでいないと述べている。(Source: Preface)
著者が「計算機科学における最も根本的な問題」と呼ぶのは**問題分解(problem decomposition)**、すなわち複雑な問題を独立に解ける部分に分ける方法である。問題分解はプログラマが日々直面する中心的な設計課題であるにもかかわらず、それを中心主題に据えた講義を著者はどの大学にも見いだせなかったという。この問題意識から Stanford University に CS 190(ソフトウェア設計)を開設し、英作文の授業のように「書く → フィードバックを受ける → 書き直す」という反復で学生に大規模なソフトウェアを設計させ、広範なコードレビューを通じて設計問題を特定させた。本書はその講義から立ち上がってきた設計原則をまとめたものである。(Source: Preface)
著者は本書の位置づけについて自覚的である。設計論の授業を受けたことも設計原則を教えてくれる師を持ったこともなく、キャリアを通じて書いた約 25 万行のコード(3 つのオペレーティングシステム、複数のファイル/ストレージシステム、デバッガ・ビルドシステム・GUI ツールキット、スクリプト言語、各種の対話的エディタ)と他人のコードを読んだ経験から共通の糸を抽出したものだと述べ、**「本書は意見表明(opinion piece)であり、設計についての会話を始めることが目的である」「本書のアイデアを自分の経験と比べ、複雑性を実際に減らすかどうかは自分で判断してほしい」**と明記している。原則より複雑性の削減という目標のほうが重要であり、ある原則を試して複雑性が減らないなら使い続ける義務はない、とも書いている。(Source: Preface)
なお「deep / shallow(深い / 浅い)」という本書の中心的な用語は Christos Kozyrakis の提案によるもので、それ以前は曖昧な "thick / thin" が使われていた。(Source: Preface)
## 構成と主要テーマ
本書は明示的な部(Part)構成を持たず 21 章が連続するが、主題は以下のようにまとまっている。
### 第 1〜3 章: 複雑性という問題と、設計に投資するという姿勢
本書の前提を据える。ソフトウェア設計の目的は複雑性の削減という一点であり、そのために継続的な投資が要るという立場を宣言する。
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 1 Introduction]] — 複雑性を不可避の敵と位置づけ、コードを単純明白にする戦略とモジュール設計でカプセル化する戦略の 2 本柱、および設計は完成しないという漸進的な開発観を提示する序章
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 2 The Nature of Complexity]] — 複雑性を「システムを理解し変更することを困難にするもの」と実務的に定義し、変更増幅・認知負荷・未知の未知という 3 症状と、依存・不明瞭さという 2 原因に整理する
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 3 Working Code Isn't Enough]] — 戦術的プログラミングと戦略的プログラミングを対比し、開発時間の 10〜20% を設計に投資する立場を、Facebook・Google・VMware の実例とともに論じる
### 第 4〜10 章: モジュール設計 — 深さ・情報隠蔽・抽象の階層・エラー設計
本書の中核。モジュールを「インターフェース(コスト)対 実装(利益)」の比で評価する見方を導入し、そこから一貫した設計判断を導く。
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 4 Modules Should Be Deep]] — 深いモジュールという中心概念を、Unix ファイル I/O を好例、Java のストリーム API とクラス病(classitis)を反例として提示する
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 5 Information Hiding (and Leakage)]] — 深さを生む最重要の技術である情報隠蔽と、その失敗形(情報漏出・時間的分解・過剰露出)を、学生の HTTP サーバ実装事例で検討する
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 6 General-Purpose Modules are Deeper]] — 汎用化するとインターフェースが単純になり実装がやや複雑になるが総体として深くなることを、テキストエディタ事例で示す
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 7 Different Layer, Different Abstraction]] — 層が違えば抽象も違うべきという原則から、パススルーメソッド・デコレータ・パススルー変数という 3 つの API 重複パターンを診断する
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 8 Pull Complexity Downwards]] — 回避できない複雑性は利用者ではなくモジュール開発者が引き受けるべきだと説き、設定パラメータの多用を複雑性の上方への押し上げとして批判する
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 9 Better Together Or Better Apart?]] — 2 つのコードを結合すべきか分離すべきかの判断基準を示し、メソッドは長さだけを理由に分割すべきでないという立場を取る
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 10 Define Errors Out Of Existence]] — 例外処理を複雑性の最悪の原因の一つと位置づけ、意味論の再定義・隠蔽・集約・クラッシュによって例外を処理すべき箇所の数そのものを減らす
### 第 11 章: 設計プロセス
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 11 Design it Twice]] — 重要な設計判断のたびに根本的に異なる複数案を出して比較する手続きを提案し、それが設計スキルそのものへの投資になると論じる
### 第 12〜16 章: 文書化 — コメントと命名
コメントを「抽象を完成させるための手段」と位置づけ、なぜ書くか・何を書くか・いつ書くか・どう維持するかを順に扱う。
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 12 Why Write Comments? The Four Excuses]] — コメントを書かない 4 つの言い訳をすべて退け、コメントなしに抽象は完成しないと論じる
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 13 Comments Should Describe Things that Aren't Obvious from the Code]] — コメントを 4 分類して規約化し、コードを繰り返さず、低水準コメントで精度を・高水準コメントで直感を与え、インターフェース文書を実装の細部で汚染しないことを説く(本書最長の章)
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] — 名前を精度と一貫性の 2 性質で論じ、著者自身が Sprite OS で経験した `block` の多義性によるバグを教訓として示す
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 15 Write The Comments First]] — コードより先にコメントを書く手順を提案し、抽象を言語化する行為が設計の欠陥を早期に露呈させると論じる
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 16 Modifying Existing Code]] — 既存コードの変更時も戦略的に振る舞い、コメントをコードの近くに置く・コミットログではなくコードに書く等の技法で文書を最新に保つ
### 第 17〜18 章: 一貫性と明白さ
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 17 Consistency]] — 一貫性が認知負荷を下げる仕組みと、それを維持する手段、そしてやりすぎると誤った推測を招くという限界を示す
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 18 Code Should be Obvious]] — 不明瞭さの裏返しである「明白さ」は読み手だけが判定できるとし、明白さを損なうものを列挙する。設計原則 14「読みやすさのために設計する」の出典
### 第 19〜21 章: 応用と総括
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 19 Software Trends]] — 本書の原則を使って継承・アジャイル開発・単体テスト・テスト駆動開発・デザインパターン・ゲッター/セッターを評価する応用章。単体テストは支持し、テスト駆動開発とゲッター/セッターには批判的
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 20 Designing for Performance]] — 単純な設計はしばしば高速でもあると論じ、RAMCloud の `Buffer` 再設計で速度が約 2 倍・コード行数が約 20% 削減された事例を示す
→ [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 21 Conclusion]] — 複雑性が本書唯一の主題であることを再確認し、設計への投資は初期コストを生むが早期に回収されると結論づける
## 巻末: 設計原則の一覧(Summary of Design Principles)
本書が提示する最も重要な設計原則として、著者が巻末に列挙しているもの(括弧内は原書の印字ページ)。
1. 複雑性は漸増する。小さなことに汗をかかねばならない(p.11)
2. 動くコードだけでは足りない(p.14)
3. システム設計を改善するために、継続的に小さな投資を行う(p.15)
4. モジュールは深くあるべきである(p.22)
5. インターフェースは、最も一般的な使い方が可能な限り単純になるよう設計すべきである(p.27)
6. モジュールにとっては、実装が単純であることよりインターフェースが単純であることのほうが重要である(pp.55, 71)
7. 汎用モジュールのほうが深い(p.39)
8. 汎用コードと特定用途向けコードを分離する(p.62)
9. 層が違えば抽象も違うべきである(p.45)
10. 複雑性を下方に押し込む(p.55)
11. エラー(と特殊ケース)を存在しないものとして定義する(p.79)
12. 二度設計する(p.91)
13. コメントはコードから明白でないことを記述すべきである(p.101)
14. ソフトウェアは書きやすさではなく読みやすさのために設計すべきである(p.149)
15. ソフトウェア開発の増分は機能ではなく抽象であるべきである(p.154)
## 巻末: レッドフラグの一覧(Summary of Red Flags)
著者は設計の問題を示す症状を**レッドフラグ**として名前づけている。いずれかがシステムに現れていれば設計に問題があることを示唆する、という使い方をする。
- **Shallow Module(浅いモジュール)**: クラスやメソッドのインターフェースが実装よりさほど単純でない(pp.25, 110)
- **Information Leakage(情報漏出)**: ある設計判断が複数のモジュールに反映されている(p.31)
- **Temporal Decomposition(時間的分解)**: コード構造が情報隠蔽ではなく操作の実行順に基づいている(p.32)
- **Overexposure(過剰露出)**: よく使う機能を使うために、めったに使わない機能まで API の利用者に意識させる(p.36)
- **Pass-Through Method(パススルーメソッド)**: 似たシグネチャの別メソッドに引数を渡すだけでほぼ何もしないメソッド(p.46)
- **Repetition(反復)**: 自明でないコード片が何度も繰り返される(p.62)
- **Special-General Mixture(特殊と汎用の混在)**: 特定用途向けコードが汎用コードから明確に分離されていない(p.65)
- **Conjoined Methods(癒着したメソッド)**: 2 つのメソッドの依存が多すぎて、片方の実装を理解せずにもう片方を理解できない(p.72)
- **Comment Repeats Code(コードを繰り返すコメント)**: コメントの情報がすべて隣のコードから即座に明白である(p.104)
- **Implementation Documentation Contaminates Interface(実装文書がインターフェースを汚染する)**: インターフェースコメントが、利用者に不要な実装の細部を記述している(p.114)
- **Vague Name(曖昧な名前)**: 変数やメソッドの名前が不正確で、有用な情報をほとんど伝えない(p.123)
- **Hard to Pick Name(名前をつけにくい)**: ある実体に正確で直感的な名前をつけるのが難しい(p.125)
- **Hard to Describe(記述しにくい)**: 変数やメソッドの文書が、完全であろうとすると長くならざるをえない(p.131)
- **Nonobvious Code(明白でないコード)**: コード片の振る舞いや意味を容易に理解できない(p.148)
## 影響と位置づけ
本書の特徴は、**設計原則を「複雑性が減るかどうか」という単一の評価軸に従属させている**点にある。原則そのものを守ることが目的ではなく、著者は Preface で「ある原則を試して複雑性が実際に減らないなら使い続ける義務はない」と明言している。この姿勢のため、本書は既存の通説にしばしば逆らう。メソッドを短くすること自体を目的にしない(第 9 章)、テスト駆動開発を批判する(第 19 章)、ゲッター/セッターを情報隠蔽に反するとする(第 19 章)、設定パラメータの多用を複雑性の上方への押し上げと見なす(第 8 章)といった主張がそれにあたる。いずれも著者の意見表明であり実証研究ではない。
もう一つの特徴は、**抽象の設計自由度そのものを問題解決の手段として使う**発想である。第 10 章の「エラーを存在しないものとして定義する」は、実装を工夫してエラーに対処するのではなく、インターフェースの意味論を再定義してエラーだった状況を正常な振る舞いに含めてしまう。第 6 章の汎用化(特定用途向けメソッド群を汎用メソッドに置き換えて情報漏出ごと消す)も同じ発想の適用であり、両者は「実装で頑張るのではなく抽象の切り方を変える」という共通の型を持つ。
第 20 章は本書の主張の中で最も反直感的な部分である。性能最適化は通常、単純性と対立するものと考えられているが、著者はクリティカルパスに絞った根本的な再設計であれば両立しうるとし、RAMCloud の `Buffer` 事例で速度と行数の両方が改善したことを示す。ただしこれは条件つきの主張であり、複雑さを持ち込む種類の最適化には著者自身が慎重である。
## 関連
- [[John Ousterhout]] — 著者
- [[RAMCloud]] — 第 20 章の中心事例となる分散インメモリストレージシステム
- [[ソフトウェア複雑性]] — 本書全体を貫く中心概念
- [[深いモジュール]] / [[情報隠蔽]] / [[抽象化(ソフトウェア設計)]] — モジュール設計の中核概念
- [[戦略的プログラミング]] — 設計への投資という姿勢
- [[コメント設計]] / [[命名]] / [[設計の一貫性]] — 文書化と可読性
- [[例外処理の設計]] / [[性能を意識した設計]] — 個別領域への適用
- [[本質的複雑性と偶発的複雑性]] — Brooks の区別との対比
- [[技術的負債]] — 負債の発生を防ぐ側からの接続
- [[ソフトウェア保守]] — 既存コードの変更(第 16 章)
## 出典
- John Ousterhout, *A Philosophy of Software Design*, Yaknyam Press, 2018.