目次を表示する

仕様駆動開発(SDD)入門 ── AI時代の「正しい作り方」

SPEC.mdの書き方 ── AIが迷わない仕様の作り方

SPEC.mdの書き方 ── AIが迷わない仕様の作り方

この章は本書の核心だ。「どんなSPEC.mdを書けばAIが意図通りに実装するか」を具体的に解説する。

SPEC.mdの6要素

効果的なSPEC.mdは、以下の6要素で構成される。

1. Outcomes        ── 何を達成したいか(ゴール)
2. Scope           ── 何を含み、何を含まないか(境界)
3. Constraints     ── 制約条件(技術・ビジネス)
4. Prior Decisions ── 既存の決定事項(アーキテクチャ前提)
5. Tasks           ── 実装タスクのリスト
6. Verification    ── 完了条件・テスト基準

これらを順番に埋めていくと、AIが迷わず実装できる仕様が完成する。

実践:ウィッシュリスト機能のSPEC.md

ECサイトのウィッシュリスト機能を例に、SPEC.mdを一から書いてみよう。

❌ 悪い例(Vibe Codingレベルの指示)

ウィッシュリスト機能を作ってください。
ユーザーが商品を保存できて、後で見られるようにする。

問題

  • どんなデータを保存するのか不明
  • 認証は?共有機能は?制限数は?
  • 既存のアーキテクチャとどう統合するのか不明
  • テスト基準が存在しない

AIはこれを受け取ると「自分なりに最適な実装」を生成する。チームの方針と合わない可能性が高い。

✅ 良い例(SPEC.mdの6要素)

# Feature: ウィッシュリスト管理

## Outcomes(ゴール)
- ログイン済みユーザーが商品をウィッシュリストに追加・削除できる
- ウィッシュリストを公開URLで第三者に共有できる
- 最大100件まで保存可能

## Scope

### In Scope(含む)
- 単一ユーザーのウィッシュリスト(1ユーザー = 1リスト)
- 商品の追加・削除
- Public/Privateの切り替え
- 共有用トークンの生成(UUIDベース)
- 共有URLからの閲覧(認証不要)

### Out of Scope(含まない、明確に除外)
- 複数リストの管理(次フェーズ以降)
- 価格変動の通知アラート(別チームが担当)
- SNSシェア機能(MVP外)
- ゲストユーザーのウィッシュリスト

## Constraints(制約)
- レスポンスタイム: GET /api/wishlist は100ms以内(p95)
- ウィッシュリストの上限: 100件(超えたら409エラー)
- 共有トークン: UUID v4、有効期限なし
- 認証: 既存のJWTミドルウェア(src/middleware/auth.ts)を使用すること
- バリデーション: ZodでAPIエントリポイントを必ず検証

## Prior Decisions(既存の決定事項)
- DB: PostgreSQL + Prisma ORM(Supabase上)
- エラーフォーマット: `{ error: { code: string, message: string } }`
- 認証方式: JWT(AuthorizationヘッダーのBearerトークン)
- 既存の商品テーブル: `products`(id, name, price, imageUrl)

## Tasks(実装タスク)

### バックエンド
1. Prismaスキーマに `wishlists` テーブル追加(userId FK、shareToken unique)
2. Prismaスキーマに `wishlist_items` テーブル追加(wishlistId FK、productId FK)
3. `POST /api/wishlist/items` — 商品追加
4. `DELETE /api/wishlist/items/:productId` — 商品削除
5. `GET /api/wishlist` — 自分のウィッシュリスト取得(認証必要)
6. `GET /api/wishlist/share/:token` — 公開ウィッシュリスト取得(認証不要)
7. `PUT /api/wishlist/visibility` — Public/Private切り替え

### テスト
8. `tests/wishlist.test.ts` — 上記エンドポイントの統合テスト
9. `tests/wishlist.unit.test.ts` — サービス層のユニットテスト

## Verification(完了条件)
- [ ] 全エンドポイントの正常系テストがPASS
- [ ] 上限100件を超えた時に409エラーが返る
- [ ] 未認証アクセスで401エラーが返る(共有URLエンドポイントを除く)
- [ ] `GET /api/wishlist` のレスポンスタイムが100ms以内(DBに50件の場合)
- [ ] 共有URLから認証なしで商品リストを閲覧できる
- [ ] カバレッジ: ビジネスロジック層 80%以上

この仕様を渡せば、AIは「何を作るか」「何を作らないか」「既存コードとどう統合するか」を全て理解した状態で実装できる。

各要素の書き方詳解

Outcomes(ゴール)

「何が達成されれば成功か」をユーザー視点で書く。技術的な実装手段は書かない。

✅ 良い書き方:
  「ユーザーが商品を保存し、後から見返せる」

❌ 悪い書き方:
  「WishlistテーブルにproductIdとuserIdを保存する」
  (実装の詳細はTasksに書く)

Scope(スコープ)

Out of Scopeの明示が最も重要だ。 AIは「この仕様に書いてないことも実装したほうがいいかも」と判断することがある。除外リストを明確にすることでスコープクリープを防ぐ。

✅ Out of Scopeの良い書き方:
  「複数リストの管理(次フェーズ以降)」
  「価格アラート(別チームが担当)」
  「SNSシェア(MVP外、要件確定まで実装不可)」

❌ 悪い書き方:
  「高度な機能は後で」(曖昧すぎる)

Constraints(制約)

数値を含めた制約を書く。「速くする」ではなく「100ms以内」。

✅ 制約の良い書き方:
  「レスポンスタイム: 100ms以内(p95)」
  「上限: ユーザーあたり100件(超えたら409 Conflict)」
  「認証: src/middleware/auth.tsの既存実装を使用」

❌ 悪い書き方:
  「パフォーマンスに注意」(数値がない)
  「適切に認証する」(何を使うかが不明)

Prior Decisions(既存の決定事項)

プロジェクトのアーキテクチャ的な前提を書く。これがないとAIは「自分で決める」モードに入る。

Prior Decisionsに書くべきこと:
  - 使うDBとORM
  - エラーレスポンスのフォーマット
  - 認証方式と使うファイルパス
  - 既存テーブルのスキーマ(関連するもの)
  - コーディング規約の中で特に重要なもの

Tasks(タスク)

実装の粒度をFile単位で書くのが目安だ。「認証を実装する」は大きすぎる。「src/middleware/auth.tsにJWT検証ロジックを追加する」が適切。

✅ タスクの良い粒度:
  「POST /api/wishlist/items エンドポイントを追加」
  「Prismaスキーマに wishlists テーブルを追加」

❌ タスクの悪い粒度:
  「バックエンドを実装する」(大きすぎる)
  「wishlistService.tsのaddItemメソッドの引数を検証」(小さすぎる)

Verification(完了条件)

チェックボックス形式で書き、すべてにテスト可能な基準を設ける。「動作を確認する」ではなく「テストがPASS」が理想。

✅ 良い完了条件:
  - [ ] 統合テスト: 全エンドポイントの正常系・異常系がPASS
  - [ ] パフォーマンス: 100件のデータで100ms以内
  - [ ] セキュリティ: 未認証リクエストで401が返る

❌ 悪い完了条件:
  - [ ] 手動でUIを確認する
  - [ ] バグがないこと(測定不可能)

SPEC.mdの粒度感

どのくらいの機能を1つのSPEC.mdにまとめるか。

粒度規模
Epic SPEC2〜4週間の機能群「ユーザー認証システム全体」
Feature SPEC(推奨)3〜5日の機能「メール/パスワードログイン」
Task SPEC1〜2日の作業「パスワードリセットメール送信」

Feature SPECが最もバランスがよい。 それより細かくすると仕様が多すぎてAIが柔軟性を失い、大きすぎると仕様が曖昧になる。

SPEC.mdテンプレート

コピーして使えるテンプレートを用意した。

# Feature: [機能名]

## Outcomes
- [ユーザーが何を達成できるか]
- [ユーザーが何を達成できるか]

## Scope

### In Scope
- [含む機能1]
- [含む機能2]

### Out of Scope
- [含まない機能1](理由: [なぜ除外するか])
- [含まない機能2](理由: [なぜ除外するか])

## Constraints
- [制約1(数値を含む)]
- [制約2]
- [使用する既存モジュール・ファイルパス]

## Prior Decisions
- [アーキテクチャ上の決定1]
- [使用するDB・ORM・ライブラリ]
- [エラーフォーマット等の規約]

## Tasks

### [カテゴリ1]
1. [具体的なタスク(ファイル名を含む)]
2. [具体的なタスク]

### [カテゴリ2]
3. [具体的なタスク]

## Verification
- [ ] [テスト可能な完了条件1]
- [ ] [テスト可能な完了条件2]
- [ ] [パフォーマンス基準(数値)]
- [ ] [カバレッジ目標]

次の章では、このSPEC.mdを使った実際のワークフローを手を動かしながら追っていく。