コーディングエージェントは高速で自信満々ですが、指示しない限りコードベースのアーキテクチャを知りません。Claude CodeやCodexに曖昧なチケットを渡すと、コンパイルできて簡単なテストに通るコードは作れます。しかし、ドメイン層とHTTP層の境界、トランザクション方針、API契約などを静かに破ることがあります。DESIGN.mdは、エージェントが必ず参照できるリポジトリ内に、アーキテクチャ上の意図・制約・判断理由を書き残すための実用的なファイルです。
TL;DR
DESIGN.mdは、コードベースのアーキテクチャ意図、制約、設計判断をMarkdownで記録するリポジトリ内ドキュメントです。
主な目的は次のとおりです。
- Claude Code、Codex、Cursorなどのコーディングエージェントに設計上の前提を渡す
- レイヤー境界、ドメイン不変条件、API契約を守らせる
- 「なぜこの設計なのか」を説明する
-
AGENTS.mdやCLAUDE.mdを肥大化させず、そこから参照させる
役割の違いはシンプルです。
-
README.md: これは何か、どう始めるか -
AGENTS.md: どうビルド・テスト・リントするか -
CLAUDE.md: Claude Code向けの指示 -
DESIGN.md: なぜこの設計なのか、何を破ってはいけないか
はじめに
コーディングエージェントを導入したチームがすぐに遭遇する典型的な失敗があります。
たとえば、決済サービスに返金エンドポイントを追加するよう依頼したとします。エージェントは次のようなコードを返すかもしれません。
- コントローラーから直接データベースを呼ぶ
- 決済ゲートウェイのエラーを握りつぶす
- 既存の
Money型に気づかず、新しい通貨型を作る - 生成された型を手で修正する
差分はきれいで、テストも通ります。しかし、アーキテクチャを知っているレビュアーだけが「これはまずい」と気づきます。
問題は、エージェントがコーディングできないことではありません。問題は、重要な設計判断がコード外にあることです。
- Notionの設計メモ
- Slackの古いスレッド
- シニアエンジニアの頭の中
- 過去の障害対応から生まれた暗黙ルール
DESIGN.mdは、これらをリポジトリに移し、エージェントと人間の両方が参照できる形にします。
DESIGN.mdとは何か
DESIGN.mdは、コードがなぜその形になっているのかを説明するファイルです。
READMEのようにプロジェクト概要を書くものではありません。AGENTS.mdのようにビルドコマンドを書くものでもありません。
たとえば、次のような判断を記録します。
- HTTPハンドラーから決済ゲートウェイを直接呼ばない。
すべてアウトボックステーブル経由で非同期処理する。
- 金額は常に最小単位の整数で表す。
浮動小数点数は丸め誤差の原因になるため禁止する。
- `Account`集約だけが台帳を書き込める。
他のモジュールから直接台帳へ書き込んではならない。
エージェントはコードからAccount.debit()の存在は見つけられます。しかし、それが「唯一の書き込みパス」として意図的に設計されたことは推測できません。そのため、明示しない限り2つ目の書き込みパスを作ってしまいます。
DESIGN.mdは、次の既存プラクティスに近いものです。
-
ARCHITECTURE.md: コードベースの構造を説明する - ADR(Architecture Decision Record): 設計判断と理由を時系列で残す
-
CONTRIBUTING.md: 開発参加時のルールを書く
ただし、DESIGN.mdはコーディングエージェントにも読ませる前提で書きます。したがって、長い文章よりも、明確な見出し・箇条書き・禁止事項・不変条件が重要です。
DESIGN.md vs AGENTS.md vs CLAUDE.md vs README
これらのファイルは役割が重なって見えますが、分けたほうが運用しやすくなります。
AGENTS.mdは、agents.mdプロジェクトで定義されている、コーディングエージェント向けの指示ファイルです。ビルド、テスト、リント、スタイル、コミット規約などを書きます。
AnthropicのClaude Codeのメモリに関するドキュメントでは、CLAUDE.mdがClaude Code向けのメモリファイルとして扱われます。既存のAGENTS.mdを@AGENTS.mdでインポートする構成も推奨されています。
ただし、これらに詳細なアーキテクチャ判断まで詰め込むと、ファイルが長くなりすぎます。Claude Codeのドキュメントでも、CLAUDE.mdは短く保つことが推奨されています。
そのため、構成は次のように分けます。
| ファイル | 対象読者 | 答えること | 変更頻度 | 長さ |
|---|---|---|---|---|
README.md |
人間 | これは何か、どう始めるか | 機能に応じて変更 | 中 |
AGENTS.md |
コーディングエージェント | どうビルド、テスト、リント、コミットするか | ツールに応じて変更 | 短 |
CLAUDE.md |
Claude Code | AGENTS.mdとClaude固有の指示 | ツールに応じて変更 | 短 |
DESIGN.md |
エージェント、エンジニア、レビュアー | なぜこの設計なのか、何を破ってはいけないか | アーキテクチャ変更時のみ | 中 |
実用的な構成例は次のとおりです。
README.md # 人間向けの概要
AGENTS.md # ビルド、テスト、リント、スタイル
CLAUDE.md # @AGENTS.md + Claude固有の補足
DESIGN.md # アーキテクチャ、不変条件、設計判断
AGENTS.mdには次のような1行を追加します。
アーキテクチャと設計上の制約は `DESIGN.md` を参照してください。
構造変更、API変更、DB変更、ドメインロジック変更の前に必ず読んでください。
Claude Codeのメモリ設計については、Claude Codeワークフローでも詳しく解説されています。
DESIGN.mdに書くべき内容
DESIGN.mdには、エージェントがコードだけから推測できない情報を書きます。
特に有効なのは次の項目です。
システム形状
レイヤー、モジュール、依存方向を書く。不変条件
常に守るべきルールを書く。主要な設計判断と根拠
なぜその設計にしたのかを書く。却下した代替案
再導入してほしくないパターンを書く。データとドメインルール
金額、時刻、ID、テナンシー、ソフトデリートなどを書く。API契約の信頼できる情報源
OpenAPI仕様の場所と、生成コードを手で編集しないルールを書く。新規コードの配置場所
エージェントがロジックを散らさないようにする。触ってはいけない範囲
生成コード、凍結済みレガシー、適用済みマイグレーションなどを書く。
DESIGN.mdテンプレート
以下は、決済APIサービス向けの実用的なテンプレートです。不要な項目を削除し、自分のシステムに合わせて埋めてください。
# DESIGN.md: 決済APIサービス
このファイルは、アーキテクチャの意図と設計判断を記録します。
コードを生成または変更する前に読んでください。
変更がこのファイルのルールと衝突する場合は、回避策を実装せず、
作業を止めて衝突点を指摘してください。
## システム形状
依存関係は内側にのみ向かいます。
http (handlers, DTOs)
-> app (use cases)
-> domain (entities, invariants)
<- infra (db, gateway clients)
- `domain/`は`http/`、`app/`、フレームワークをインポートしません。
- `infra/`は`domain/`または`app/`で宣言されたインターフェースを実装します。
- `http/`はDBや決済ゲートウェイを直接操作しません。
必ず`app/`のユースケースを呼び出します。
## 不変条件
- 台帳エントリは一度書き込まれたら変更できません。
修正は更新や削除ではなく、相殺エントリで表します。
- 口座残高は台帳エントリから導出します。
直接更新される可変フィールドとして保存しません。
- 金額は最小単位の整数とISO-4217通貨コードで表します。
浮動小数点数は使いません。
- 外部決済ゲートウェイへの呼び出しは、`idempotency_key`で冪等にします。
リトライで二重課金してはいけません。
- 明示的な`OverdraftPolicy`がない限り、残高はマイナスになりません。
## 主要な設計判断と根拠
### ゲートウェイ呼び出しはアウトボックス経由
HTTPハンドラーは、ビジネス変更と同じDBトランザクションでアウトボックス行を書き込みます。
その後、ワーカーが決済ゲートウェイを呼び出します。
理由:
- ゲートウェイは負荷時にタイムアウトする
- リクエストスレッドで外部呼び出しをすると遅延と失敗処理が不安定になる
- DB変更と外部呼び出しの整合性を保ちたい
禁止:
- HTTPハンドラーから決済ゲートウェイを直接呼び出さない
### 集約ごとの単一書き込みパス
`Account.post_entry()`だけが台帳に書き込めます。
理由:
- 複数の書き込みパスが残高不整合を引き起こしたため
禁止:
- 新しいリポジトリメソッドから台帳を直接更新しない
## 却下した代替案
- 集約間のORM遅延読み込みは使いません。
N+1問題と不明確なトランザクション境界を引き起こしたためです。
- 残高を直接更新される列として保存しません。
残高は常に台帳から導出します。
- 汎用`Money`ライブラリは使いません。
`domain/money.py`を使ってください。
- リクエストスレッドから加盟店へ同期Webhookを送信しません。
通知キューを使います。
## データとドメインルール
- タイムスタンプはすべてUTCです。
DBでは`timestamptz`として保存し、API境界ではRFC 3339で返します。
- IDはアプリケーション層で生成するULIDです。
DBのオートインクリメントIDは使いません。
- ソフトデリートは使いません。
必要な場合は明示的なユースケースでアーカイブテーブルへ移動します。
- すべてのクエリは`tenant_id`でスコープします。
テナントスコープのないリポジトリメソッドはバグです。
## API契約の信頼できる情報源
- `api/openapi.yaml`がAPI契約の信頼できる情報源です。
- リクエスト/レスポンスタイプはOpenAPI仕様から生成します。
- `http/generated/`内の生成コードを手動編集してはいけません。
- 新規または変更されたエンドポイントは、先に`api/openapi.yaml`を更新します。
- エラー応答はRFC 9457の`problem+json`形式にします。
共有の`problem()`ヘルパーを使ってください。
## 新規コードの配置場所
- 新しいエンドポイント:
- ルート: `http/routes/`
- DTO: `http/dto/`
- ユースケース: `app/usecases/`
- ドメインロジック: `domain/`
- 新しい外部統合:
- クライアント: `infra/clients/`
- インターフェース: `app/ports/`
- 横断的関心事:
- 認証、ロギング、冪等性は`http/middleware/`
- ハンドラーにインライン実装しない
## 触ってはいけない範囲
- `http/generated/`
- OpenAPIから再生成されるため、手動編集しない
- `legacy/billing_v1/`
- 凍結済みで移行中。拡張しない
- `migrations/`
- 適用済みマイグレーションを編集しない
- 新しいマイグレーションを追加する
## 迷ったとき
要求された変更がこのファイルのルールを破る必要がある場合は、
黙って回避策を実装しないでください。
代わりに、衝突している設計ルールを指摘し、
最小限の設計整合性を保つ代替案を提案してください。
最後の「迷ったとき」は重要です。エージェントに「制約と衝突した場合は止まる」と明示しないと、制約を迂回するコードを生成しがちです。
エージェントにDESIGN.mdを読ませる方法
エージェントはDESIGN.md専用の特別なパーサーを持っているわけではありません。通常のファイルとして読み、コンテキストに入れます。
そのため、起動時に読まれる指示ファイルから参照させます。
Claude Codeの場合
CLAUDE.mdに次を追加します。
@DESIGN.md
または、より明示的に書きます。
構造変更、API変更、DB変更、ドメインロジック変更の前に `DESIGN.md` を読んでください。
AGENTS.mdの場合
AGENTS.mdに次を追加します。
## Architecture
アーキテクチャ、不変条件、API契約、禁止事項は `DESIGN.md` を参照してください。
構造変更、API変更、DB変更、ドメインロジック変更の前に必ず読んでください。
レビュー時の使い方
PRレビューでは、違反を具体的に指摘します。
これは DESIGN.md の「HTTPハンドラーからDBを直接操作しない」ルールに違反しています。
app/usecases/ にユースケースを追加し、ハンドラーからそれを呼び出す形に直してください。
このように書くと、エージェントは修正しやすくなります。
独自のエージェント実行ループを作る場合も、このフィードバックループが重要です。詳細は独自のClaude Codeを構築するを参照してください。
アンチパターン
DESIGN.mdは、書き方を間違えるとすぐに役に立たなくなります。次を避けてください。
1. コードを説明し直す
悪い例:
`UserService`はユーザーを処理します。
これはコードを見れば分かります。
良い例:
ユーザー作成時のメール送信は同期実行しません。
`user_created`イベントを発行し、通知ワーカーで処理します。
理由: 外部メールサービス障害でサインアップを失敗させないため。
2. チュートリアル化する
DESIGN.mdに長い手順やシェルコマンドを書きすぎると、CONTRIBUTING.mdやAGENTS.mdと役割が混ざります。
悪い例:
ローカルで開発するには npm install を実行し...
これはAGENTS.mdやREADME.mdに書きます。
3. 願望を事実として書く
悪い例:
すべての書き込みはユースケース層を通ります。
実際にはlegacy/がバイパスしているなら、これは嘘になります。
良い例:
目標: すべての書き込みはユースケース層を通す。
現状: `legacy/`はこのルールをバイパスしている。
新規コードで`legacy/`のパターンを拡張してはいけない。
4. オーナーがいない
DESIGN.mdは更新責任者を決めてください。
PRテンプレートに次のチェックを入れると運用しやすくなります。
- [ ] この変更は `DESIGN.md` の設計判断、不変条件、API契約に影響しますか?
- [ ] 影響する場合、同じPRで `DESIGN.md` を更新しましたか?
5. コードと行単位で同期しようとする
DESIGN.mdはコードの索引ではありません。頻繁に変わる関数名や細かいクラス構造まで書くと、すぐに古くなります。
書くべきなのは、年に数回しか変わらないレベルの判断です。
- レイヤー境界
- トランザクション方針
- API契約の権威
- ドメイン不変条件
- 禁止した設計パターン
API・バックエンドで特に効く理由
DESIGN.mdは、APIやバックエンドコードベースで特に効果があります。
理由は、バックエンドにはコードから見えにくい高コストな制約が多いからです。
- API契約
- トランザクション境界
- 冪等性
- リトライ
- テナントスコープ
- 認可
- エラーモデル
- データ整合性
- 生成コードの扱い
これらは単一のファイルを読んでも分かりません。しかし、間違えると本番障害やデータ不整合につながります。
API契約を明示する
DESIGN.mdには、OpenAPI仕様が信頼できる情報源であることを書きます。
## API契約
- `api/openapi.yaml`がAPI契約の信頼できる情報源です。
- エンドポイントを追加・変更する場合は、先にOpenAPI仕様を更新します。
- `http/generated/`の生成コードを手動編集してはいけません。
- 実装はOpenAPI仕様に準拠している必要があります。
これにより、エージェントが勝手にレスポンススキーマを発明したり、生成型を手で直したりするリスクを下げられます。
Apidogのようなツールで先にAPI契約を設計し、OpenAPI仕様をリポジトリにエクスポートすれば、エージェントが従うべき明確なターゲットを用意できます。契約優先の考え方については、AIエージェント向けAPIの設計も参考になります。
トランザクション境界を書く
- DBトランザクション内で外部APIを呼び出してはいけません。
- 外部送信が必要な場合はアウトボックスに行を書き、ワーカーで処理します。
これを書かないと、エージェントは素朴にHTTPハンドラー内で外部APIを呼び出しがちです。
冪等性を書く
- 決済、返金、注文作成、プロビジョニング系の操作は冪等でなければなりません。
- `idempotency_key`なしで外部決済ゲートウェイを呼び出してはいけません。
決済や注文処理では、これが二重課金や二重作成を防ぎます。
エラーモデルを書く
- すべてのAPIエラーはRFC 9457の`problem+json`形式で返します。
- 共有の`problem()`ヘルパーを使います。
- エンドポイントごとに独自のエラー形式を作ってはいけません。
これだけで、エンドポイントごとにエラー形式がばらつく問題を防げます。
テナンシーと認可を書く
- すべてのクエリは`tenant_id`でスコープします。
- テナントスコープのないリポジトリメソッドはバグです。
- 認可チェックはユースケース層で行います。
これはセキュリティ上の不変条件です。コード断片だけではエージェントが見落としやすいため、明示する価値があります。
API設計を先に固める場合は、Apidogをダウンロードして、設計優先のワークスペース、OpenAPIエクスポート、生成されたエンドポイントの契約確認に利用できます。
実装手順
既存リポジトリにDESIGN.mdを導入するなら、次の順で進めると現実的です。
1. まず1ページで始める
最初から完全な設計書を作ろうとしないでください。まずは次の4項目だけで十分です。
# DESIGN.md
## レイヤー境界
## 不変条件
## API契約
## 触ってはいけない範囲
2. エージェントがよく間違える点から書く
過去のレビューコメントを見て、頻出する問題を追加します。
- 直接DBアクセス
- 生成コードの手動編集
- エラー形式のばらつき
- テナントスコープ漏れ
- 外部APIの同期呼び出し
- ドメインロジックのHTTP層混入
3. AGENTS.mdまたはCLAUDE.mdから参照する
構造変更、API変更、DB変更、ドメインロジック変更の前に `DESIGN.md` を読んでください。
Claude Codeなら次も使えます。
@DESIGN.md
4. PRテンプレートに追加する
## Design checklist
- [ ] この変更は `DESIGN.md` のルールに従っています
- [ ] 設計判断を変える場合、`DESIGN.md` を更新しました
- [ ] API変更がある場合、OpenAPI仕様を先に更新しました
5. レビューでルール名を使う
レビューコメントでは、個人の好みではなくDESIGN.mdのルールとして指摘します。
DESIGN.mdの「すべてのクエリはtenant_idでスコープする」に違反しています。
Repositoryメソッドにtenant_idを渡す形に修正してください。
この形式にすると、人間にもエージェントにも修正意図が伝わりやすくなります。
結論
DESIGN.mdは、エージェントにアーキテクチャ上の文脈を渡すための実用的なガードレールです。
導入時のポイントは次のとおりです。
- コードから推測できない設計判断を書く
- 不変条件、禁止事項、根拠を明確に書く
-
AGENTS.mdやCLAUDE.mdから参照させる - 長い説明ではなく、宣言的なルールとして書く
- API契約、トランザクション、冪等性、テナンシーを明示する
- PRテンプレートに更新チェックを入れる
- OpenAPI仕様をAPI契約の信頼できる情報源として指定する
特にバックエンドでは、OpenAPI仕様を権威として示すだけでも効果があります。エージェントはスキーマを発明するのではなく、契約に従うようになります。
APIを設計優先で進める場合は、ApidogをダウンロードしてOpenAPI仕様をエクスポートし、DESIGN.mdからその仕様を参照させる構成が実用的です。
よくある質問
DESIGN.mdはAGENTS.mdのような公式標準ですか?
いいえ。AGENTS.mdは、Linux FoundationのAgentic AI Foundationの下で管理されている、広く採用されたフォーマットです。
一方、DESIGN.mdはARCHITECTURE.mdやADRに近いコミュニティ慣習です。公式仕様ではなく、チームに合わせて適応するパターンとして扱ってください。
AGENTS.mdやCLAUDE.mdがあってもDESIGN.mdは必要ですか?
自明でないアーキテクチャ制約があるなら必要です。
AGENTS.mdやCLAUDE.mdは、ビルド、テスト、リントなどの運用ルールを書く場所です。深い設計判断まで入れると長くなり、エージェントが読みづらくなります。
運用ファイルについては、AGENTS.mdファイルの書き方も参考になります。
DESIGN.mdとARCHITECTURE.mdはどう違いますか?
大きな違いは対象読者です。
ARCHITECTURE.mdは主に人間向けの構造説明です。DESIGN.mdは、人間に加えてコーディングエージェントにも読ませる前提で、より宣言的に不変条件や禁止事項を書きます。
実際には、チームによってはどちらか1つの名前に統一しても問題ありません。
DESIGN.mdはどのくらいの長さがよいですか?
2〜4ページ程度から始めるのが実用的です。
重要なのは長さではなく密度です。エージェントが繰り返し間違える判断、不変条件、禁止事項に絞ってください。
コードを再説明している行や、チュートリアル化している行は削除します。
エージェントに実際に読ませるには?
AGENTS.mdまたはCLAUDE.mdから参照します。
Claude Codeなら次のように書けます。
@DESIGN.md
AGENTS.mdなら次のように書きます。
構造変更、API変更、DB変更、ドメインロジック変更の前に `DESIGN.md` を読んでください。
エージェントは常にDESIGN.mdに従いますか?
いいえ。DESIGN.mdは強制設定ではなく、モデルが参照するコンテキストです。
そのため、ルールは曖昧にせず、絶対条件として書きます。
HTTPハンドラーからDBを直接操作してはいけません。
レビュー時にも、違反したルールを明示して修正させます。
DESIGN.mdはAPI契約にも役立ちますか?
非常に役立ちます。
特に、OpenAPI仕様を信頼できる情報源として指定すると、エージェントが勝手にスキーマを作ったり、生成コードを手で直したりするリスクを下げられます。
Apidogのようなツールで先にAPI契約を設計しておけば、DESIGN.mdから明確に参照できるターゲットになります。
DESIGN.mdはどこに置くべきですか?
基本はリポジトリルートです。
README.md
AGENTS.md
CLAUDE.md
DESIGN.md
モノレポの場合は、ルートに全体方針を書き、各パッケージにローカルなDESIGN.mdを置く構成も有効です。
Top comments (0)