はじめに
Claude CodeやCursorなどのコーディングエージェントに実装を任せる場面が増えてきました。エージェントの出力品質は、モデルの性能以上に入力されるコンテキストの質、つまり仕様書の書き方に左右されます。
従来の仕様書(Excel方眼紙、PowerPoint、装飾だらけのWord)は人間が目で読むことを前提にしています。エージェントにとっては、パースしにくく、曖昧さが多く、推測で埋めるしかない入力です。
この記事では、仕様書を「AIが読む入力データ」として設計し直すための具体的なプラクティスを、Before/Afterのコード例つきでまとめます。
内容は、実際に生成AIに「どんな仕様書が読みやすいか/読みにくいか」を聞き、その回答をベースに整理しています。
TL;DR
原則は1つです。情報をレイアウトではなくテキストに載せる。セルの位置・色・図の配置で表現した意味は、AIがテキストとして読み込んだ瞬間に失われます。
- 仕様書はMarkdownで書き、コードと同じリポジトリでGit管理する(Docs as Code)
- API・データ構造・DBはOpenAPI / JSON Schema / 型定義 / DDLで機械可読に定義する
- 要件はRFC 2119のキーワード(MUST / SHOULD / MAY)とID付きで書く
- 受け入れ条件は入力と期待値の具体例で書く
- スコープ外と未決事項を明記して、AIの「推測による穴埋め」を防ぐ
- 用語集を置き、表記揺れをなくす
AIが読みやすい仕様書のプラクティス
1. Markdown + Gitで管理する(Docs as Code)
Markdownは見出し階層がそのまま文書構造になり、AIが確実にパースできます。コードと同じリポジトリに置けば、エージェントがファイルを直接参照でき、PRで仕様変更と実装変更をセットでレビューできます。
2. インターフェースはスキーマで定義する
自然言語の「ユーザー情報を返す」は解釈に揺れが出ますが、スキーマには曖昧さがほぼありません。
| 対象 | 形式 |
|---|---|
| REST API | OpenAPI 3.x |
| データ構造 | JSON Schema / TypeScript型定義 |
| DB | DDL(CREATE TABLE) |
| シーケンス・状態遷移 | Mermaid |
# openapi.yaml(抜粋)
paths:
/users/{id}:
get:
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"404":
description: ユーザーが存在しない
図もMermaidならテキストのまま渡せます。画像の図より読み違いが起きません。
3. 要件の強さをRFC 2119キーワードで書く
「〜すること」「〜が望ましい」は強制度が曖昧です。MUST / SHOULD / MAYで書くと、AIが「必須か任意か」を取り違えません。
- トークンの有効期限は30分 (MUST)
- 期限切れ時は再発行画面へ誘導する (SHOULD)
4. 受け入れ条件を具体例で書く
最も効果が大きいポイントです。Given / When / Then形式で書くと、そのままテストケースに変換させやすくなります。
Given 未登録のメールアドレス "foo@example.com"
When パスワードリセットを申請する
Then HTTP 202 を返す
And 登録済みの場合と同じレスポンスボディを返す
5. 「なぜ」を書く
設計判断の理由があると、仕様に書かれていないエッジケースでもAIが意図に沿った実装を選べます。ADR(Architecture Decision Record)として残すのも有効です。
6. スコープ外と未決事項を明示する
AIは仕様の欠落を、黙ってもっともらしい実装で埋めがちです。Out of Scope と TBD のセクションがあれば、勝手な実装や過剰実装を防げます。
7. 要件にIDを振る
REQ-012 のようなIDがあれば、「REQ-012を実装して」「REQ-012のテストを書いて」と粒度を指定して指示できます。コミットメッセージやテスト名に含めればトレーサビリティも取れます。
AIが読みにくい仕様書のアンチパターン
1. Excel方眼紙
セル結合とレイアウトで構造を表現していると、CSVやテキストに変換した時点で「どの値がどの項目のものか」が崩れます。
# Before: 方眼紙をテキスト化した結果
項目名,,,,型,,必須,,
ユーザーID,,,,,,string,,○
<!-- After -->
| 項目名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| userId | string | MUST | UUID v4 |
2. 色・装飾だけで意味を表す
「赤字が変更箇所」「グレーアウトは廃止」はテキスト化で消えます。意味はラベルとして文字で書きます。
- [DEPRECATED] /v1/users は 2027-03 に廃止
- [CHANGED v2.1] レスポンスに `createdAt` を追加
3. 仕様の本体が画像にしかない
画面遷移図やER図がスクリーンショットだけだと、矢印の向きや小さな文字を読み違えるリスクがあります。Mermaidで書くか、画像を使う場合もテキストで要点を併記します。
4. 暗黙知に依存する
「既存APIと同様のエラー処理」「いつもの認証方式」は、参照先が書かれていなければAIには分かりません。
<!-- Before -->
エラー処理は既存APIと同様とする。
<!-- After -->
エラーレスポンスは docs/specs/common/error.md の形式に従う (MUST)
5. 用語が揺れている
「ユーザー」「会員」「顧客」が同一エンティティなのか、AIには判断できません。用語集でコード上の名前まで対応づけると、命名の揺れも防げます。
| 用語 | コード上の名前 | 定義 |
|---|---|---|
| 会員 | Member |
メール認証済みのユーザー |
| ゲスト | Guest |
未登録の閲覧者 |
6. 新旧の仕様が混在している
取り消し線や「※v2で変更」の注記が積み重なると、どれが現行仕様か誤認します。仕様書には現在の状態だけを書き、変更履歴はGitの履歴とCHANGELOGに任せます。
実例:エージェントに渡す要件ファイル
ここまでのプラクティスを1ファイルにまとめた例です。フロントマターでメタ情報を持たせ、スキーマは別ファイルを参照しています。
---
id: REQ-012
title: パスワードリセット
status: approved
related:
- api/openapi.yaml#/paths/~1password-reset
- specs/common/error.md
---
# REQ-012 パスワードリセット
## 目的
ログインできないユーザーが自力で復旧できるようにする(問い合わせ件数の削減)
## 仕様
- リセットトークンの有効期限は30分 (MUST)
- トークンは1回使用したら無効化する (MUST)
- 未登録アドレスでも登録済みと同じレスポンスを返す (MUST)
- 理由: アカウントの存在を推測されないため
## 受け入れ条件
- Given 未登録アドレス / When 申請 / Then 202 + 同一ボディ
- Given 発行から31分後のトークン / When 再設定 / Then 410 TOKEN_EXPIRED
## Out of Scope
- SMSによるリセット
## TBD
- 同一アドレスからの連続申請のレート制限値(担当: 田中)
この形なら、エージェントに「REQ-012を実装し、受け入れ条件をテストにして」と一言で指示できます。
リポジトリ構成とコンテキスト設計
仕様は機能単位でファイルを分割します。巨大な1ファイルはコンテキストウィンドウを圧迫し、無関係な記述がノイズになるからです。一方で分割しすぎると全体像が失われるため、エントリーポイントを1つ用意します。
repo/
├── CLAUDE.md # エージェント向けの入口(AGENTS.md なども同様)
├── docs/
│ ├── glossary.md # 用語集
│ ├── architecture.md
│ ├── adr/ # 設計判断の記録
│ └── specs/
│ ├── common/error.md
│ └── auth/REQ-012-password-reset.md
├── api/openapi.yaml
└── src/
エントリーポイントには、プロジェクト概要、用語集と仕様ディレクトリの場所、コーディング規約、テストの実行コマンドを書いておきます。エージェントは必要に応じて個別の仕様ファイルを読みにいけます。
さらに、markdownlintやOpenAPIのリンター(Spectralなど)をCIに組み込めば、「AIが読める状態」を機械的に維持できます。
まとめ:仕様書はエージェントへの入力データ
仕様書を「人が読む文書」から「エージェントに渡す入力データ」と捉え直すと、書き方の基準が明確になります。PRで仕様を出す前に、次の項目をセルフチェックしてみてください。
- Markdownで書かれ、コードと同じリポジトリにある
- API・データ構造がスキーマで定義されている
- 要件にIDとMUST / SHOULD / MAYが付いている
- 受け入れ条件が入力と期待値の具体例で書かれている
- Out of ScopeとTBDが明記されている
- 用語が用語集と一致している
- 色・画像・「既存と同様」に頼った記述がない
ちなみに、これらはそのまま「新しく入ったエンジニアが読みやすい仕様書」の条件でもあります。AI向けの整備は、チームのオンボーディングコストを下げる投資にもなります。