codebase-design について
codebase-designは、ソフトウェアの設計とリファクタリング向けのエージェントスキルです。明確なシーム上の小さなインターフェースに多くの振る舞いを隠す設計を扱い、モジュールの深さ、アダプター、テスト容易性の原則を示します。
主な機能
- 01モジュールとインターフェースの用語
- 02小さなインターフェースへの複雑な振る舞いの集約
- 03シームとアダプターの役割の特定
- 04削除テストによるモジュール価値の評価
- 05テストしやすいインターフェース設計
活用シーン
- 01新しいモジュールの明確なインターフェース設計
- 02浅い中継モジュールのリファクタリング
- 03テストが通るシームの選定
- 04依存関係や副作用がテストに及ぼす影響の評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vinvcn/ mattpocock-skills-zh-cn --skill codebase-designインストール後、Claude Code で/codebase-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
DEEPENING.mdDESIGN-IT-TWICE.mdagents/openai.yaml詳細
コードベース設計
deep modules を設計します。小さな interface の背後に多くの振る舞いを置き、明確な seam 上に interface を配置し、その interface を通してテストします。コードを設計またはリファクタリングするときは、常にこの言葉と原則を使ってください。目標は、callers に leverage をもたらし、maintainers に locality をもたらし、誰にとってもテストしやすくすることです。
用語集
これらの用語を正確に使い、「component」「service」「API」「boundary」に置き換えないでください。一貫した言葉を使うことが重要です。
Module - interface と implementation を持つものすべてです。意図的に規模を限定していません。function、class、package、あるいは複数レイヤーにまたがる slice でもかまいません。避ける: unit、component、service。
Interface - caller が module を正しく使うために知っておく必要のあるすべてです。type signature に加え、invariants、ordering constraints、error modes、required configuration、performance characteristics が含まれます。避ける: API、signature(狭すぎます。type-level surface のみを指すためです)。
Implementation - module 内部のコードです。Adapter とは異なります。小さな adapter に大きな implementation が備わっている場合(Postgres repo)もあれば、大きな adapter に小さな implementation が備わっている場合(in-memory fake)もあります。seam について話すときは adapter、それ以外では implementation と呼びます。
Depth - interface がもたらす leverage です。caller(または test)が interface を学ぶために費やす単位あたり、どれだけの振る舞いにアクセスできるかを表します。多くの振る舞いが小さな interface の背後に隠れている module は deep であり、interface が implementation とほぼ同じ複雑さを持つ module は shallow です。
Seam(Michael Feathers)- 現在の場所を編集せずに振る舞いを変えられる場所です。つまり、module の interface が存在する 場所 です。seam をどこに置くかは、seam の背後に何を置くかとは別の設計上の決定です。避ける: boundary(DDD の bounded context と意味が重複するためです)。
Adapter - seam 上で特定の interface を満たす具体的なものです。内部に何があるかという実体(substance)ではなく、どの枠を埋めるかという 役割(role)を表します。
Leverage - callers が depth から得る利益です。interface を学ぶために費やす単位あたり、より多くの能力が得られます。1 つの implementation が N 個の call sites と M 個の tests で効果を発揮します。
Locality - maintainers が depth から得る利益です。change、bugs、knowledge、verification が callers に分散せず、一か所に集まります。一度直せば、あらゆる場所で修正されます。
Deep と shallow
Deep module = 小さな interface + 多くの implementation:
+------------------+
| Small Interface | -> few methods, simple params
+------------------+
| |
| Deep | -> complex logic hidden
| Implementation |
| |
+------------------+
Shallow module = 大きな interface + 少ない implementation(避けてください):
+-------------------------------+
| Large Interface | -> many methods, complex params
+-------------------------------+
| Thin Implementation | -> mostly pass-through
+-------------------------------+
interface を設計するときは、次の点を確認してください。
- methods の数を減らせますか?
- parameters を簡素化できますか?
- さらに複雑さを内部に隠せますか?
原則
- Depth は implementation ではなく、interface の特性です。 Deep module の内部は、小さくて mock 可能かつ差し替え可能な parts で構成されていてもかまいません。ただし、それらは interface の一部ではありません。module は internal seams(implementation 内部にあり、自身の tests で使われるもの)と、interface 上にある external seam の両方を持つことができます。
- 削除テスト。 この module を削除すると想像してください。複雑さがなくなるなら、それは単なる pass-through です。複雑さが N 個の callers に再び散らばるなら、その module は価値を発揮しています。
- Interface はテスト対象の surface です。 Callers と tests は同じ seam を通ります。interface の背後にある内部の詳細をテストしたいなら、この module の形は適切でない可能性があります。
- Adapter が 1 つなら仮定上の seam、2 つなら実在する seam です。 seam をまたいで変化するものが実際にない限り、seam を導入しないでください。
テスト容易性を考慮した設計
優れた interfaces なら、自然にテストできます。
-
依存関係を受け取り、生成しない。
// 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; } -
小さな surface area。 methods が少ないほど、必要な tests も少なくなります。params が少ないほど、test setup も簡単になります。
関係
- 1 つの Module は、ちょうど 1 つの Interface(callers と tests に提示する surface)を持ちます。
- Depth は Module の特性であり、その Interface を基準に測ります。
- Seam は Module の Interface が存在する場所です。
- Adapter は Seam 上にあり、Interface を満たします。
- Depth は callers に Leverage を、maintainers に Locality をもたらします。
採用しない表現
- depth を implementation の行数と interface の行数の比として扱うこと(Ousterhout):implementation の水増しを促すためです。ここでは depth-as-leverage を使います。
- 「Interface」を TypeScript の
interfacekeyword や class の public methods と解釈すること:狭すぎます。ここでいう interface には、caller が知っておく必要のあるすべての事実が含まれます。 - 「Boundary」:DDD の bounded context と意味が重複します。seam または interface と呼んでください。
さらに深く理解する
- 依存関係を踏まえた cluster の deepening - DEEPENING.md を参照してください。Dependency の分類、Seam の規律、「追加ではなく置き換える」というテスト戦略について説明しています。
- 別の interfaces を検討する - DESIGN-IT-TWICE.md を参照してください。sub-agents を並行して起動し、いくつかの大きく異なる方法で interface を設計したうえで、depth、locality、seam の配置を比較します。