codebase-design について
既存コードベースのモジュールやインターフェースを設計する開発者向けのスキルです。呼び出し側の要件、振る舞いの責任範囲、テスト可能性を重視し、利用者の負担を減らさないラッパーや抽象化を避けます。
主な機能
- 01代表的な呼び出し側からインターフェースを設計
- 02振る舞いの責任を明確なモジュールに配置
- 03深いモジュールと浅いラッパーを区別
- 04依存関係の接点と振る舞いテストを案内
活用シーン
- 01既存ワークフロー向けモジュールの設計
- 02共有ルールの配置先の決定
- 03内部を公開せずに振る舞いをテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marcellocurto/ skills --skill codebase-designインストール後、Claude Code で/codebase-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
DEEPENING.mdDESIGN-IT-TWICE.mdDOMAIN-STRUCTURE.mdTYPE-DESIGN.mdagents/openai.yaml詳細
コードベースの設計
関連するルールと振る舞いを、内部の詳細を知らなくても呼び出し側が利用できるモジュールにまとめます。設計は、呼び出し側が何を知る必要があるか、ルールが変わったときに何か所を変更する必要があるか、振る舞いをどれだけ簡単にテストできるかを基準に判断します。
用語
プロジェクトで既に使われている名前を使用します。以下の区別は設計の説明に役立ちますが、コードやドメイン概念の名前を変更する理由にはなりません。
- モジュール: インターフェースと実装を持つ関数、クラス、パッケージ、またはその他の単位です。
- インターフェース: 型、必須の呼び出し順序、エラー、設定、パフォーマンス上の制約など、モジュールを正しく使うために呼び出し側が知っておく必要があるすべてのことです。TypeScript の
interface宣言だけを指すものではありません。 - 実装: インターフェースの背後にあるコードです。
- アダプター: インターフェースをデータベースや外部サービスなどの特定の依存先に接続する実装です。この用語は規模ではなく役割を表します。
- シーム: テストの場合を含め、振る舞いや依存先を差し替えられる箇所です。この区別が重要な場合は、それがモジュール内部のものか、アプリケーションの呼び出し側に公開されているものかを明示します。
深いモジュールは、呼び出し側が自ら判断しなければならないことを隠します。浅いラッパーは、その作業を減らさずにインターフェースだけを追加します。コードの量、メソッド数、インターフェースと実装の行数の比率では、どちらに当たるかは判断できません。
呼び出し側から設計する
型やメソッドを作る前に、代表的な呼び出し箇所をスケッチします。そうした呼び出し側が必要とするものからインターフェースを導きます。呼び出し側が制御する必要がない限り、処理段階、ストレージの詳細、調整処理はモジュール内に保ちます。
提案するインターフェースごとに、次の点を確認します。
- 呼び出し側は、内部ルールを知ったり、無関係な操作を調整したりせずにタスクを完了できますか?
- 各パラメーターは、呼び出し側が選択する必要のある事項を表していますか?
- ルールが変わったとき、それを呼び出し側全体ではなく、それを所有するモジュール内で更新できますか?
- モジュールを削除すれば不要な転送処理がなくなりますか?それとも、呼び出し側がその振る舞いを自分で処理する必要がありますか?
呼び出し側が一つ、またはアダプターが一つでも、モジュールが役立つ場合があります。プロトコル、副作用、または独立して変化するルールを切り離せるなら、そのモジュールを維持します。そのモジュールを正当化するために、別のアダプター、設定オプション、将来のユースケースを作り出さないでください。
振る舞いは、それを所有する場所に置く
同じルール、データ、またはワークフローを維持するコードをまとめます。独立して変化する責務に明確な置き場所を与えられる場合は、モジュールを分割します。呼び出し側が理解すべきことを減らさず、コードを移動するだけのラッパーは切り出しません。
モジュールは、より小さな内部モジュールや依存関係のインターフェースを利用できます。アプリケーションの呼び出し側が必要としない限り、それらの詳細は内部に保ちます。テストでは、アプリケーション API の一部にすることなく、内部モジュールのインターフェースを使ってそのモジュール自身の振る舞いを確認できます。
要件への対応に回避策が繰り返し必要になる場合は、その要件をきれいに満たす設計をスケッチしてから、必要な互換性を維持できる、認められた移行方法を選びます。既存の構造は考慮すべき背景であり、すべての層を残す要件ではありません。この比較は、無関係な書き換えを認めるものではありません。
振る舞いをテスト可能にする
適切なインターフェースを通じて依存関係を受け取り、テスト対象の振る舞いを置き換えずにテストから制御できるようにします。たとえば、注文処理に決済ゲートウェイを渡せば、実際に課金せずに決済失敗をテストできます。
計算と副作用がそれぞれ異なる責務を持つ場合は、分離します。計算した割引額を返すようにすれば、カートを変更したりストレージを呼び出したりせずに、そのルールをテストできます。
振る舞いは、それを担うインターフェースを通じてテストします。他のテストでは見逃す重要な障害を検出できる場合は、内部テストも残します。テストのセットアップを簡単にするためだけに、非公開の状態を公開したり、公開メソッドを追加したり、呼び出し側に実装の詳細を把握させたりしないでください。
呼び出し側に不要な選択肢をなくせる場合は、メソッドやパラメーターを簡素化します。メソッドの数が少なくても、テストする振る舞いが少なくなるとは限りません。
参考ガイダンス
- 依存関係の扱いと有用なテストを維持しながら、関連するモジュールを統合する場合は、DEEPENING.md を読んでください。
- 型、外部入力、キャストが、コードで適用すべきルールに影響する場合は、TYPE-DESIGN.md を読んでください。
- 状態を持つコード内で、判断、遷移ルール、検索が繰り返されているのに明確な担当箇所がない場合は、DOMAIN-STRUCTURE.md を読んでください。
- 要件とリポジトリの規約を確認しても複数の設計案が妥当なままの場合は、DESIGN-IT-TWICE.md を読んでください。