仕様駆動開発

一言で

「何を作るか」を先に仕様として書き、仕様を実行可能にする。

開発フロー

STEP 1
仕様を書く
STEP 2
仕様を実行可能に
STEP 3
テストを通す実装
STEP 4
仕様がSSOT

TDDが「テスト」を先に書くなら、仕様駆動は「仕様」を先に書く。テストは仕様の一部。

いつ使うか

対象
要件が明確なAPI開発(OpenAPI spec → 実装)
外部システムとのインターフェース設計
データ形式が決まっている処理(JSON Schema → バリデーション)
要件が不確定で試行錯誤が必要な段階
UIの見た目を探っている段階

コード例 — Python (OpenAPI + FastAPI)

# specs/api.yaml — まず仕様を書く
openapi: "3.0.0"
info:
  title: Reservation API
  version: "1.0"
paths:
  /reservations:
    post:
      summary: 予約を作成
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [guest_name, party_size, date]
              properties:
                guest_name: { type: string, minLength: 1 }
                party_size: { type: integer, minimum: 1, maximum: 20 }
                date: { type: string, format: date }
      responses:
        "201":
          description: 作成成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  status: { type: string, enum: [pending] }

コード例 — Python (Pydantic + FastAPIテスト)

from pydantic import BaseModel, Field
from datetime import date

class CreateReservationRequest(BaseModel):
    guest_name: str = Field(min_length=1)
    party_size: int = Field(ge=1, le=20)
    date: date

class ReservationResponse(BaseModel):
    id: str
    status: str

# テスト(仕様に基づく)
def test_create_reservation_success(client: TestClient):
    response = client.post("/reservations", json={
        "guest_name": "田中",
        "party_size": 4,
        "date": "2026-06-01",
    })
    assert response.status_code == 201
    data = response.json()
    assert "id" in data
    assert data["status"] == "pending"

def test_create_reservation_invalid_party_size(client: TestClient):
    response = client.post("/reservations", json={
        "guest_name": "田中",
        "party_size": 0,  # 仕様: minimum: 1
        "date": "2026-06-01",
    })
    assert response.status_code == 400  # 仕様: 400エラー

コード例 — TypeScript (Zod)

// schemas/reservation.ts — 仕様をZodスキーマで定義
import { z } from "zod";

export const CreateReservationSchema = z.object({
  guest_name: z.string().min(1),
  party_size: z.number().int().min(1).max(20),
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});

export type CreateReservationRequest = z.infer;

// テスト(スキーマに基づく)
describe("CreateReservationSchema", () => {
  it("有効なリクエストを受け入れる", () => {
    const result = CreateReservationSchema.safeParse({
      guest_name: "田中",
      party_size: 4,
      date: "2026-06-01",
    });
    expect(result.success).toBe(true);
  });

  it("party_sizeが0ならエラー", () => {
    const result = CreateReservationSchema.safeParse({
      guest_name: "田中",
      party_size: 0,  // 仕様: minimum: 1
      date: "2026-06-01",
    });
    expect(result.success).toBe(false);
  });
});

組み合わせ

TDD — 仕様→テスト→実装 BDD — 仕様の書き方がBDDシナリオと互換 クリーンアーキテクチャ — Interface Adapter層の境界定義 DDD — ドメインモデルは仕様より抽象度が高い
CLAUDE.md用プロンプト:
## 開発手法: 仕様駆動開発
- 実装の前に仕様(API定義・スキーマ)を書く
- API: OpenAPI YAML または Pydantic/Zod スキーマで入出力を定義
- スキーマからテストケースを生成し、テストが通るように実装する
- 仕様が真実の源。実装と仕様が矛盾したら仕様を正とする
← 開発手法セレクタに戻る ← ガイド一覧に戻る