software-design-philosophy
深いモジュール設計・情報隠蔽・戦略的プログラミングを通じてソフトウェアの複雑性を管理します。「モジュール設計」「APIが複雑すぎる」「シャロークラス」「複雑性コスト」「deep module」「情報漏洩」「パススルーメソッド」といったキーワードが出た際や、インターフェース設計のシンプルさを評価する場面、汎用vs特化アプローチを選択する場面でトリガーされます。深いモジュールと浅いモジュールの比較、複雑性のレッドフラグ、設計ドキュメントとしてのコメント活用などをカバーします。
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", or "pass-through method". Also trigger when reviewing interface designs for simplicity, evaluating whether an abstraction is pulling its weight, 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 boundaries, see clean-architecture.
SKILL.md 本文
ソフトウェア設計の哲学フレームワーク
ソフトウェアエンジニアリングの根本的な課題である複雑性を管理するための実践的なフレームワークです。モジュール設計、API レビュー、コードのリファクタリング、またはアーキテクチャの判断を支援する際に適用します。中心的なテーゼは、複雑性はソフトウェア問題の根本原因であり、設計のあらゆるレベルで意図的で戦略的な思考が必要だということです。
コア原則
ソフトウェア開発の最大の制限は、自分たちが作成するシステムを理解する能力である。 複雑性が敵です。システムを理解しにくく、変更しにくくし、バグの原因となります。すべての設計決定は次の質問で評価されるべきです:「これはシステムの全体的な複雑性を増加させるか、それとも減少させるか?」目標はゼロの複雑性ではありません。有用なソフトウェアでは不可能です。むしろ、不必要な複雑性を最小化し、必要な複雑性を管理できる場所に集中させることです。
スコアリング
目標:10/10。 ソフトウェア設計のレビューや作成時に、以下の原則への準拠に基づいて 0~10 でスコアを付けます。10/10 とは、深いモジュール、クリーンな抽象化、優れた情報隠蔽、設計意図をキャプチャするコメントを意味します。低いスコアは、浅いモジュール、情報漏洩、戦術的なショートカット、または設計ドキュメントの不足を示します。常に現在のスコアと 10/10 に達するために必要な具体的な改善を提供してください。
ソフトウェア設計フレームワーク
複雑性を管理し、理解と変更が容易なシステムを構築するための 6 つの原則:
1. 複雑性とその原因
コア概念: 複雑性とは、ソフトウェアシステムの構造に関連した、理解と変更を困難にするすべてのものです。これは 3 つの症状を通じて現れます:変更の増幅、認知負荷、そして未知の未知です。
なぜ機能するのか: 複雑性の具体的な症状を特定することで、開発者は「ごちゃごちゃしたコード」といった曖昧な概念に頼るのではなく、正確に問題を診断できます。2 つの根本的な原因(依存関係と不明確さ)は、設計改善の明確なターゲットを提供します。
重要な洞察:
- 変更の増幅:単純な変更が多くの場所での変更を必要とする
- 認知負荷:開発者は変更を行うために大量の情報を心に留めておく必要がある
- 未知の未知:何を変更する必要があるのか、またはどの情報が関連しているのかが明らかでない(最悪の症状)
- 依存関係:コードは分離して理解または変更することができない
- 不明確さ:重要な情報がコードまたはドキュメントから明らかでない
- 複雑性は段階的です。1 つの大きな誤りではなく、数百の小さな決定から蓄積されます
- 複雑性の「死に至る千の切り傷」の性質は、すべての決定が重要であることを意味します
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| 変更の増幅 | 共有知識を一元化する | 20 ファイルに #ff0000 をハードコーディングするのではなく、色定数を抽出する |
| 認知負荷 | 開発者が知る必要があることを減らす | バッファサイズ、エンコード、ロックモードを要求するのではなく、単純な open(path) API を使用する |
| 未知の未知 | 依存関係を明示する | 型システムとインターフェースを使用して、変更が何に影響を与えるかを明らかにする |
| 依存関係管理 | モジュール間の結合を最小化する | 共有グローバル状態ではなく、明確に定義されたインターフェースを通じてデータを渡す |
| 不明確さ軽減 | 物事に正確な名前を付ける | n ではなく numBytesReceived;delay ではなく retryDelayMs |
参照:references/complexity-symptoms.md
2. 深いモジュール vs 浅いモジュール
コア概念: 最高のモジュールは深いです。シンプルなインターフェースの背後に強力な機能を提供します。浅いモジュールは、提供する機能に対して複雑なインターフェースを持ち、複雑性を軽減するのではなく増加させます。
なぜ機能するのか: モジュールのインターフェースは、システムの残りの部分に課す複雑性を表します。その実装は提供する機能を表します。深いモジュールは、インターフェスの複雑性に対して機能の高い比率を提供します。インターフェースはコスト。実装がベネフィットです。
重要な洞察:
- モジュールの深さ = 提供される機能 / 課される複雑性
- 深いモジュール:シンプルなインターフェース、強力な実装(Unix ファイル I/O、ガベージコレクタ)
- 浅いモジュール:複雑なインターフェース、限定的な実装(Java I/O ラッパークラス)
- 「クラシティス」:多くの小さな浅いクラスを作成する病気
- 各インターフェースは認知負荷を増加させます。より多くのクラス = 必ずしも優れた設計ではありません
- 最高の抽象化は、いくつかのシンプルな概念の背後に重大な複雑性を隠します
- 小さいメソッドが本質的に良いわけではありません。深さはサイズより重要です
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| 深いモジュール | シンプルな API の背後に複雑性を隠す | file.read(path) はディスクブロック、キャッシング、バッファリング、エンコードを隠す |
| 浅いモジュール | ただ渡すだけのシンラッパーを避ける | FileInputStream を BufferedInputStream で包み、さらに ObjectInputStream で包む |
| クラシティス対策 | 関連する浅いクラスをマージする | RequestParser、RequestValidator、RequestProcessor を 1 つの RequestHandler に統合する |
| メソッド深度 | メソッドは何か実質的なことをすべき | ロック、ロギング、キャッシュ無効化、リバランシングを処理する delete(key) |
| インターフェスの簡潔性 | パラメータと メソッドを減らす | 15 個のコンストラクタパラメータではなく、合理的なデフォルトを持つ config.get(key) |
参照:references/deep-modules.md
3. 情報隠蔽と情報漏洩
コア概念: 各モジュールは、他のモジュールが必要としない知識をカプセル化すべきです。情報漏洩(設計決定が複数のモジュールに反映される)はソフトウェア設計における最も重要なレッドフラッグの 1 つです。
なぜ機能するのか: 情報がモジュール内に隠されている場合、その知識への変更はそのモジュールのみを変更します。情報がモジュール境界を越えて漏れると、変更はシステム全体に伝播します。情報隠蔽は、複雑性の 2 つの根本的な原因である依存関係と不明確さの両方を軽減します。
重要な洞察:
- 情報隠蔽:設計決定の知識を単一のモジュール内に組み込む
- 情報漏洩:同じ知識が複数のモジュールに現れる(レッドフラッグ)
- 時間的分解は漏洩を引き起こします。物事が発生する時間でコードを分割すると、フェーズ間で共有知識が強制されます
- バックドア漏洩(データ形式、プロトコル、共有仮定を通じた)が最も微妙な形式です
- デコレータは漏洩の頻繁な原因です。装飾されたインターフェースを公開しています
- 2 つのモジュールが知識を共有している場合、それらをマージするか、共有知識をカプセル化する新しいモジュールを作成することを検討してください
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| 情報隠蔽 | フォーマットの詳細をカプセル化する | 1 つのモジュールが HTTP 解析ロジックを所有し、呼び出し元は構造化オブジェクトを取得する |
| 時間的分解 | 時間ではなく知識で組織化する | 「設定を読む」と「設定を適用する」を単一の設定モジュールに統合する |
| フォーマット漏洩 | シリアル化を一元化する | すべての場所に json.dumps を分散させるのではなく、1 つのモジュールが JSON エンコード/デコードを処理する |
| プロトコル漏洩 | プロトコルの詳細を抽象化する | MessageBus.send(event) はトランスポートが HTTP、gRPC、またはキューであるかを隠す |
| デコレータ漏洩 | 深いラッパーは慎重に使用する | 外部で包むのではなく、ファイルクラス内にバッファリングを追加することを優先する |
参照:references/information-hiding.md
4. 汎用 vs 専用モジュール
コア概念: 「ある程度汎用的な」モジュールを設計します。インターフェースは複数の用途をサポートするのに十分な汎用性がありながら、今日の具体的な要件に限定されず、実装は現在のニーズを処理します。問い:「すべての現在のニーズをカバーする最もシンプルなインターフェースは何か?」
なぜ機能するのか: 汎用インターフェースは特殊なケースを排除するため、より単純になる傾向があります。また、新しいユースケースが既存の抽象化に適合することが多いため、設計を将来に対応させます。ただし、過度に汎用化すると、労力が無駄になり、不要な抽象化を通じて複雑性そのものが導入される可能性があります。
重要な洞察:
- 「ある程度汎用的」は、特殊すぎると汎用的すぎるの間のスイートスポットです
- 重要な質問:「すべての現在のニーズをカバーする最もシンプルなインターフェースは何か?」
- 汎用インターフェースは、特殊なケースが少ないため、特殊用途のものより単純なことが多い
- 複雑性を下へ押し下げる:下位レベルのモジュールは困難な事例を処理し、上位レベルをシンプルに保つべき
- 設定パラメータは、正しい動作を決定できなかったことを表すことが多いです。各パラメータは呼び出し元に押し付けられた複雑性です
- 不確実な場合は、より単純で汎用的なアプローチを最初に実装してください
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| API の汎用性 | 1 つのユースケースではなく、概念に基づいて設計する | text.addBulletPoint() ではなく text.insert(position, string) API |
| 複雑性を下へ | モジュール内でデフォルトを処理する | 呼び出し元に設定を要求するのではなく、合理的なバッファサイズを選択する web サーバー |
| 設定を削減 | 動作を自動的に決定する | パラメータを要求するのではなく、ファイルエンコードを自動検出する |
| 過度な特殊化を避ける | ユースケース固有のメソッドを削除する | storeUser()、storeProduct()、storeOrder() ではなく 1 つの store(key, value, options) |
| ある程度汎用的 | 汎用インターフェース、具体的実装 | 現在は PostgreSQL をサポートしているが、SQL 概念を公開していない Datastore インターフェース |
参照:references/general-vs-special.md
5. 設計ドキュメントとしてのコメント
コア概念: コメントは、コードから明らかではないことを説明すべきです。設計意図、抽象化の根拠、コードでは表現できない情報をキャプチャします。「優れたコードは自動的に文書化される」という主張は、低レベルの実装の詳細以外のすべてについては誤りです。
なぜ機能するのか: コードはプログラムが何をするかを示しますが、なぜそのようにするのか、設計の代替案は何か、コードが何を仮定しているのかは示しません。コメントはデザイナーのメンタルモデル(抽象化)をキャプチャします。これはシステム内で最も価値があり、最も失われやすい情報です。
重要な洞察:
- 4 つのタイプ:インターフェスコメント、データ構造メンバーコメント、実装コメント、モジュール間コメント
- インターフェスコメントが最も重要です。モジュールが提示する抽象化を定義します
- コメントを最初に書く(コメント駆動設計)ことで、コードを書く前に思考を明確にします
- 「自動的に文書化されるコード」は低レベルの「何」に対してのみ機能します。「なぜ」、仮定、抽象化には失敗します
- コメントは明らかでないことを説明すべきです。コードがそれを明確にしている場合、繰り返さないでください
- コメントをそれが説明するコードの近くに保ち、コードが変更されるときに更新してください
- コメントを書くのが難しい場合、設計が複雑すぎる可能性があります
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| インターフェスコメント | 実装ではなく抽象化を説明する | 「与えられた位置に最も近いウィジェットを返すか、しきい値距離内にウィジェットが存在しない場合は null を返す」 |
| データ構造コメント | 不変式と制約を説明する | 「リストは優先度の降順でソートされ、同順位は挿入順序で解決される」 |
| 実装コメント | 何ではなくなぜを説明する | 「// このリストは常にソート済みであり、100k+ 項目を含む可能性があるため、ここで二分探索を使用する」 |
| モジュール間コメント | 関連する設計決定をリンクする | 「// このタイムアウトは RetryPolicy.java の再試行間隔と一致する必要があります」 |
| コメント駆動設計 | コードの前にインターフェスコメントを書く | 実装する前に、関数のコントラクトと動作をドラフトする |
参照:references/comments-as-design.md
6. 戦略的 vs 戦術的プログラミング
コア概念: 戦術的プログラミングは機能を素早く動作させることに焦点を当て、各ショートカットで複雑性を蓄積します。戦略的プログラミングは良好な設計に 10~20% の追加努力を投資し、すべての変更をシステム構造改善の機会として扱います。
なぜ機能するのか: 戦術的プログラミングは短期的には高速に見えますが、着実にコードベースを劣化させ、将来のすべての変更を難しくします。戦略的プログラミングは、時間の経過とともに簡単に変更できるコードベースを生成します。小さな初期投資は複合効果をもたらします。戦略的に設計されたシステムは、数ヶ月後には使用するのが高速です。
重要な洞察:
- 戦術的竜巻:機能を素早く生産しますが、跡を残す開発者。短期的には祝われることが多いですが、長期的には破壊的です
- 戦略的な考え方:基本的な仕事は、たまたま設計を持つ動作するコードではなく、たまたま動く優れた設計を生成することです
- 10~20% の投資:開発時間の約 10~20% を設計改善に費やします
- スタートアップは戦略的プログラミングが最も必要です。初期の設計ショートカットは、チームが成長するにつれて、制限的な技術的負債に複合化します
- 「高速に動き、物を壊す」文化(初期段階の Facebook)vs 設計中心の文化(Google)。Google エンジニアは複雑なシステムでより生産的でした
- すべてのコード変更は投資機会です。コードを見つけたときより少し良い状態のままにしてください
- リファクタリングは特別なイベントではありません。すべての機能開発の一部です
コード適用:
| 文脈 | パターン | 例 |
|---|---|---|
| 戦術的な罠 | 不器用な修正に抵抗する | 「この 1 つの特殊なケース」を処理するためだけにブール型パラメータを追加しないでください |
| 戦略的投資 | 機能作業中に構造を改善する | 機能を追加する際、モジュールインターフェスが厄介になっている場合はリファクタリングしてください |
| 戦術的竜巻 | 認識と介入 | 2 倍のコードを書きますが、3 倍のメンテナンス負担を作成する開発者 |
| スタートアップの規律 | 初日から設計に投資する | 時間圧力下でも、クリーンなモジュール境界と優れた抽象化を実現する |
| 段階的改善 | PR ごとに 1 つの設計問題を修正する | 各プルリクエストは少なくとも 1 つの抽象化を改善するか、複雑性を排除します |
| 設計レビュー | 正確性だけでなく構造を評価する | コードレビューは「これはシステムをより単純にするか?」と尋ねるべきです。「これは動作するか?」ではなく |
参照:references/strategic-programming.md
一般的な誤り
| 誤り | 失敗する理由 | 修正 |
|---|---|---|
| 多すぎる小さなクラスを作成する | クラシティスは深度を追加せずにインターフェースを追加します。各クラス境界は認知オーバーヘッドです | 関連する浅いクラスをマージして、シンプルなインターフェースを持つ深いモジュールに統合する |
| 時間順でモジュールを分割する | 「読む、次に処理、次に書く」は 3 つのモジュールにわたって知識を共有させられます | 情報で組織化します。知識を共有するコードを 1 つのモジュールにグループ化する |
| 実装をインターフェースで公開する | 呼び出し元は内部の詳細に依存します。変更はすべての場所に伝播します | インターフェースを実装ではなく抽象化の周りに設計する。フォーマットとプロトコルの詳細を隠す |
| コメントをオプションとして扱う | 設計意図、仮定、抽象化が失われます。新しい開発者は誤った推測をします | インターフェスコメントを最初に書く。コードが発展するにつれてそれらを保持する |
| すべてに対して設定パラメータ | 各パラメータは呼び出し元に決定を押し付け、認知負荷を増加させます | 動作を自動的に決定する。合理的なデフォルトを提供する。必要な設定を最小化する |
| 高速でまあまあな戦術的修正 | 各ショートカットは少量の複雑性を追加します。時間をかけて、システムは不可能になります | 良好な設計に 10~20% の追加を投資する。すべての変更を設計機会として扱う |
| パススルーメソッド | メソッドが別のメソッドに委譲するだけで、深度を追加せずにインターフェースを追加します | パススルーを呼び出し元またはカリーにマージする |
| 特定のユースケースに合わせて設計する | 特殊用途のインターフェースは特殊なケースを蓄積し、肥大化します | 「すべての現在のニーズをカバーする最もシンプルなインターフェースは何か?」と尋ねる |
クイック診断
| 質問 | いいえの場合 | アクション |
|---|---|---|
| 各モジュールが何をするかを 1 文で説明できますか? | モジュールがやりすぎているか、目的が不明確です | 責任が明確で説明可能なモジュールに分割する |
| インターフェスが実装より単純ですか? | モジュールは浅い。複雑性を外部に漏らしています | より多くを隠すように再設計する。浅いクラスをより深いものにマージする |
| 呼び出し元に影響させずにモジュールの実装を変更できますか? | 情報がモジュール境界を越えて漏れています | 漏洩した知識を特定し、1 つのモジュール内にカプセル化する |
| インターフェスコメントは、コードではなく抽象化を説明していますか? | 設計意図が失われます。開発者はモジュールを誤用します | モジュールが約束することを説明するコメントを書く(どのように機能するかではなく) |
| 設計議論はコードレビューの一部ですか? | レビューはバグのみをキャッチします。複雑性の増加はキャッチしません | 「これはシステムの複雑性を減らしたり増やしたりするか?」をレビュー基準に追加する |
| 各モジュールは少なくとも 1 つの重要な設計決定を隠していますか? | モジュールはコードの周りに組織化されています。情報の周りではありません | 各モジュールが特定の知識の一部を所有するように再組織化する |
| 実装を読まずに新しいチームメンバーがモジュール境界を理解できますか? | 抽象化がドキュメント化されていないか、あまりに漏れています | インターフェスコメントを改善し、インターフェスが自明になるまで簡略化する |
| 設計改善に 10~20% の時間を費やしていますか? | 技術的負債はすべての機能で蓄積しています | 戦略的な考え方を採用する。すべての PR に設計改善を含める |
参照ファイル
complexity-symptoms.md:複雑性の 3 つの症状、2 つの原因、複雑性の測定、複雑性の段階的性質deep-modules.md:深いモジュール vs 浅いモジュール、インターフェスと機能の比率、クラシティス、深さに対する設計information-hiding.md:情報隠蔽原則、情報漏洩レッドフラッグ、時間的分解、デコレータの落とし穴general-vs-special.md:ある程度汎用的なアプローチ、複雑性を下に押す、設定パラメータのアンチパターンcomments-as-design.md:4 つのコメントタイプ、コメント駆動設計、自動ドキュメント化コードの神話、コメント保持strategic-programming.md:戦略的 vs 戦術的な考え方、戦術的竜巻、投資アプローチ、スタートアップの考慮事項
参考文献
このスキルは John Ousterhout の実用的なソフトウェア設計ガイドに基づいています。詳細な例を含む完全な方法論については:
- "A Philosophy of Software Design" by John Ousterhout(第 2 版)
著者について
John Ousterhout はスタンフォード大学のコンピュータサイエンスの Bosack Lerner 教授です。彼は Tcl スクリプト言語と Tk ツールキットの作成者であり、Electric Cloud と Clustrix を含むいくつかの企業を共同設立しました。Ousterhout は ACM Software System Award、UC Berkeley Distinguished Teaching Award、USENIX Lifetime Achievement Award を含む多くの賞を受賞しています。彼は A Philosophy of Software Design を Stanford の CS 190 コースから開発しました。このコースでは、学生は複数段階のソフトウェア設計プロジェクトに取り組み、複雑性を認識して削減することを学びます。この本は、システムソフトウェア構築と設計教育の数十年の経験を、言語、パラダイム、システムスケール全体に適用される簡潔な原則セットに抽出しています。現在第 2 版となった本は、正確性と明確性を超えて設計スキルを向上させたいソフトウェアエンジニアの間で広く推奨されるリソースになっています。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- wondelai
- リポジトリ
- wondelai/skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/wondelai/skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。