codebase-design について
コードを再構成するエンジニア向けの設計スキルです。小さなインターフェースの背後に多くの振る舞いを置く設計を重視し、シーム、テスト容易性、workflow-axaでの適用範囲も扱います。
主な機能
- 01深いモジュールの設計原則
- 02インターフェースとシームの用語
- 03小さなインターフェースによるテスト容易性
- 04依存関係と副作用の設計指針
- 05workflow-axaでの適用範囲の調整
活用シーン
- 01小さなインターフェースを持つモジュールへの再構成
- 02設計上のシームの配置検討
- 03コードのテスト容易性の向上
- 04一般的なパターンよりプロジェクト規則を優先
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gazohhh/ workflow-axa --skill codebase-designインストール後、Claude Code で/codebase-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
DEEPENING.mdDESIGN-IT-TWICE.mdLICENSEagents/openai.yaml詳細
mattpocock/skills の
skills/engineering/codebase-design/SKILL.mdにある workflow-axa に同梱されています。4ae2317、MIT (LICENSE)。以下の上流テキストは変更されていません。末尾の workflow-axa による適応は、内容が矛盾する場合に優先されます。
コードベース設計
深いモジュールを設計します。つまり、明確な接合点に配置され、小さなインターフェースの背後に多くの振る舞いを持ち、そのインターフェースを通じてテストできるモジュールです。コードを設計または再構成する際は、どこでもこの用語と原則を使ってください。目的は、呼び出し側にとってのレバレッジ、保守担当者にとっての局所性、そしてすべての人にとってのテスト容易性です。
用語集
以下の用語を正確に使ってください。「コンポーネント」「サービス」「API」「境界」に置き換えないでください。一貫した用語を使うことが重要です。
モジュール:インターフェースと実装を持つものすべて。意図的に規模を限定しません。関数、クラス、パッケージ、複数層にまたがるスライスなどが該当します。避ける用語:ユニット、コンポーネント、サービス。
インターフェース:モジュールを正しく使うために、呼び出し側が知る必要のあるすべてのこと。型シグネチャだけでなく、不変条件、順序に関する制約、エラーの種類、必要な設定、パフォーマンス特性も含みます。避ける用語:API、シグネチャ(範囲が狭すぎます。型レベルの表面だけを指すためです)。
実装:モジュールの内部、つまりコード本体です。アダプターとは異なります。小さなアダプターに大きな実装(Postgres リポジトリ)がある場合も、大きなアダプターに小さな実装(インメモリのフェイク)がある場合もあります。接合点が話題であれば「アダプター」を、それ以外では「実装」を使ってください。
深さ:インターフェースがもたらすレバレッジです。呼び出し側(またはテスト)が、習得する必要のあるインターフェースの単位あたりに実行できる振る舞いの量を指します。多くの振る舞いが小さなインターフェースの背後にあるモジュールは深く、インターフェースが実装とほぼ同じくらい複雑なモジュールは浅いといいます。
接合点 (Michael Feathers):その場所自体を編集せずに振る舞いを変更できる場所です。モジュールのインターフェースが存在する位置を指します。接合点をどこに置くかは、その背後に何を置くかとは別の設計判断です。避ける用語:境界(DDD の境界づけられたコンテキストと意味が重複するため)。
アダプター:接合点においてインターフェースを満たす、具体的なものです。中身ではなく、何の役割を果たすか(どの枠を埋めるか)を表します。
レバレッジ:深さによって呼び出し側が得るものです。習得するインターフェースの単位あたりに、より多くの機能を得られます。1つの実装が、N 個の呼び出し箇所と M 個のテストで効果を発揮します。
局所性:深さによって保守担当者が得るものです。変更、バグ、知識、検証が呼び出し側に散らばらず、1か所に集約されます。一度修正すれば、すべての箇所で修正されます。
深いモジュールと浅いモジュール
深いモジュール = 小さなインターフェース + 多くの実装:
┌─────────────────────┐
│ Small Interface │ ← Few methods, simple params
├─────────────────────┤
│ │
│ Deep Implementation│ ← Complex logic hidden
│ │
└─────────────────────┘
浅いモジュール = 大きなインターフェース + 少ない実装(避ける):
┌─────────────────────────────────┐
│ Large Interface │ ← Many methods, complex params
├─────────────────────────────────┤
│ Thin Implementation │ ← Just passes through
└─────────────────────────────────┘
インターフェースを設計する際は、次の点を確認してください。
- メソッドの数を減らせますか?
- パラメーターを簡素化できますか?
- さらに多くの複雑さを内部に隠せますか?
原則
- 深さは実装ではなく、インターフェースの特性です。 深いモジュールは、内部で小さく、モック可能で、差し替え可能な部品から構成されていることがあります。それらはインターフェースの一部ではありません。モジュールは、インターフェースにある外部接合点に加えて、内部接合点(実装内に限定され、独自のテストで使われるもの)を持つこともあります。
- 削除テスト。 モジュールを削除することを想像してください。複雑さがなくなるなら、それは単なる中継でした。N 個の呼び出し側に複雑さが再び現れるなら、そのモジュールには存在する価値がありました。
- インターフェースがテスト対象の表面です。 呼び出し側もテストも、同じ接合点を通ります。インターフェースの先までテストしたいなら、そのモジュールの形はおそらく適切ではありません。
- アダプターが1つなら、接合点は仮説にすぎません。アダプターが2つあれば、実際の接合点です。 実際に変化するものがないなら、接合点を導入しないでください。
テストしやすい設計
優れたインターフェースは、自然にテストできるようにします。
-
依存関係は受け取り、生成しないでください。
// Testable function processOrder(order, paymentGateway) {} // Hard to test function processOrder(order) { const gateway = new StripeGateway(); } -
副作用を発生させず、結果を返してください。
// Testable function calculateDiscount(cart): Discount {} // Hard to test function applyDiscount(cart): void { cart.total -= discount; } -
表面積を小さく保つ。 メソッドが少なければ、必要なテストも少なくなります。パラメーターが少なければ、テストの準備も簡単になります。
関係性
- モジュールには、呼び出し元とテストに提示する表面であるインターフェースが、必ず1つあります。
- 深さはモジュールの特性であり、インターフェースを基準に測定されます。
- シームとは、モジュールのインターフェースが存在する場所です。
- アダプターはシームに位置し、インターフェースを満たします。
- 深さによって、呼び出し元にはレバレッジが、保守担当者には局所性がもたらされます。
採用しない考え方
- 実装行数とインターフェース行数の比率としての深さ(Ousterhout):実装の水増しを促します。ここでは、レバレッジとしての深さを採用します。
- TypeScript の
interfaceキーワードや、クラスのパブリックメソッドとしての「インターフェース」:範囲が狭すぎます。ここでいうインターフェースには、呼び出し元が知っておく必要のあるすべての情報が含まれます。 - 「境界」:DDD の境界づけられたコンテキストと意味が重なるため、シームまたはインターフェースを使います。
さらに深く掘り下げる
- 依存関係を踏まえてクラスターを深める方法については、DEEPENING.md を参照してください。依存関係のカテゴリ、シームの規律、テストでレイヤーを追加せず置き換える方法について説明しています。
- 代替インターフェースの検討については、DESIGN-IT-TWICE.md を参照してください。並行してサブエージェントを起動し、インターフェースを大きく異なる複数の方法で設計してから、深さ、局所性、シームの配置を比較します。
workflow-axa の適用
- 合意済みタスク内の限定された設計上の選択(インターフェース、シーム、モジュール境界)に使用します。設計が確定しているときに、新たなアーキテクチャに関するヒアリングを始めるためのものではありません。
- このファイルで推奨するパターンよりも、プロジェクトの用語、責任範囲、インターフェース、承認済みのルールを優先します。