仕様駆動開発とは何か
テスト駆動開発(TDD)は多くのエンジニアに浸透しましたが、最近注目されている仕様駆動開発(Spec-Driven Development)はその先の段階だと考えます。単なるテストを書くのではなく、実行可能な仕様として要件を定義し、その仕様に基づいてコードを実装していくアプローチですね。
github/spec-kitは、このSpec-Driven Developmentを実践するためのツールキットです。AIアシスタント(Claude Copilotなど)との連携を前提に設計されており、仕様から実装へのギャップを最小化できます。
私がこのツールキットに注目したきっかけは、フリーランスで複数のクライアント案件を抱える中、要件定義の曖昧さによる手戻りが増えていたためです。仕様を形式化して、AIに正確に伝えられるようにしたいという課題がありました。
Spec-Driven DevelopmentとTDDの違い
まずアンチパターンから見てみましょう。従来的なTDDでは、テストコードが仕様の役割を果たしていました。
import unittest
class TestUserValidation(unittest.TestCase):
def test_email_format(self):
# テストがあるだけで、要件が明示的でない
self.assertTrue(is_valid_email('user@example.com'))
def test_password_length(self):
# 「なぜ8文字なのか」という背景が不明確
self.assertFalse(is_valid_password('short'))
一方、Spec-Driven Developmentでは仕様をより明示的に定義します:
"""User Registration Specification
As a user, I want to create an account with valid credentials.
Requirements:
- Email must follow RFC 5322 format
- Password must be at least 8 characters
- Password must contain uppercase, lowercase, and numbers
- Email must be unique in the system
Given a valid email and strong password
When the user submits the registration form
Then an account is created and a confirmation email is sent
"""
このように仕様を構造化することで、AIツールが要件を正確に理解でき、実装品質が向上します。
Spec-kitの主要な機能
github/spec-kitは以下のような機能を提供しています:
- 仕様ファイルの定義:マークダウンベースで、BDD(Behavior-Driven Development)スタイルの仕様を記述
- AI連携:Claude APIと統合し、仕様から自動でテストコードを生成
- 検証レポート:実装が仕様を満たしているかを自動チェック
- CLI ツール:コマンドラインから仕様の検証やテスト生成を実行
公式ドキュメントによると、v0.2.0からPythonの本格的なサポートが開始されました。
Pythonプロジェクトでの実装フロー
実際のプロジェクトで使う場合のステップを説明します。
ステップ1: 仕様ファイルの作成
プロジェクトルートにspecs/ディレクトリを作成し、以下のような仕様ファイルを配置します:
# Product Search API Specification
Version: 1.0
Last Updated: 2024-01-15
## Feature: Search for products
### Scenario 1: Search with valid query
Given a search query "laptop"
And the database has products matching the query
When the search endpoint is called
Then a list of products should be returned
And response status should be 200
### Scenario 2: Search with empty query
Given an empty search query
When the search endpoint is called
Then an error message should be returned
And response status should be 400
### Constraints
- Search should be case-insensitive
- Results should be paginated (max 50 items per page)
- Response time should be under 500ms
ステップ2: Spec-kitでテスト生成
CLIコマンドでこの仕様からテストコードを自動生成できます:
spec-kit generate --spec specs/search_api.md --output tests/test_search_api.py --language python
Generated test file: tests/test_search_api.py
Generated 2 test cases from specification
✓ test_search_with_valid_query
✓ test_search_with_empty_query
ステップ3: 生成されたテストコード
spec-kitが自動生成するテストは以下のような構造になります:
import pytest
from fastapi.testclient import TestClient
from app import app
client = TestClient(app)
class TestProductSearchAPI:
"""
Product Search API Specification
Feature: Search for products
"""
def test_search_with_valid_query(self):
"""
Scenario 1: Search with valid query
Given a search query "laptop"
And the database has products matching the query
When the search endpoint is called
Then a list of products should be returned
And response status should be 200
"""
response = client.get("/api/search?q=laptop")
assert response.status_code == 200
assert isinstance(response.json(), list)
assert len(response.json()) > 0
def test_search_with_empty_query(self):
"""
Scenario 2: Search with empty query
When the search endpoint is called with empty query
Then an error message should be returned
And response status should be 400
"""
response = client.get("/api/search?q=")
assert response.status_code == 400
assert "error" in response.json()
ステップ4: 仕様に基づく実装
生成されたテストに基づいて、実装コードを書きます:
from fastapi import FastAPI, HTTPException, Query
from typing import List
import time
app = FastAPI()
class Product:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
# ダミーデータベース
PRODUCTS = [
Product(1, "Laptop Pro 15"),
Product(2, "Gaming Laptop"),
Product(3, "Desktop PC"),
Product(4, "Laptop Stand"),
]
@app.get("/api/search")
async def search_products(
q: str = Query(..., min_length=1),
page: int = Query(1, ge=1),
limit: int = Query(50, ge=1, le=50)
) -> List[dict]:
"""
Search for products by query
- Search is case-insensitive
- Results are paginated
- Response time constraint: < 500ms
"""
start_time = time.time()
if not q:
raise HTTPException(status_code=400, detail="Search query cannot be empty")
# Case-insensitive search
query_lower = q.lower()
results = [
{"id": p.id, "name": p.name}
for p in PRODUCTS
if query_lower in p.name.lower()
]
# Pagination
start_idx = (page - 1) * limit
end_idx = start_idx + limit
paginated_results = results[start_idx:end_idx]
elapsed = time.time() - start_time
assert elapsed < 0.5, f"Response time exceeded: {elapsed}s"
return paginated_results
AIアシスタントとの連携
spec-kitの真の力は、Claude CopilotやGitHub Copilotのようなアシスタントとの組み合わせにあります。仕様ファイルが明確に定義されていれば、AIが生成するコードの品質が大幅に向上します。
調べてみたところ、spec-kitはプロンプト生成機能も備えており、仕様からAIへの指示文を自動で構成できるということです。例えば:
spec-kit prompt --spec specs/search_api.md --model claude-3-5-sonnet
このコマンドで、Claude向けの最適化されたプロンプトが生成され、より正確な実装提案を受け取れます。
複数シナリオの管理
より複雑なシステムでは、複数の仕様ファイルを組織的に管理する必要があります:
specs/user-management/- ユーザー認証・登録関連specs/payment/- 決済処理関連specs/notifications/- 通知機能関連
spec-kitはディレクトリ単位でのバッチ処理に対応しているため、全スペックを一括生成できます:
spec-kit generate --spec specs/ --output tests/ --language python --recursive
Processing specs/user-management/registration.md
Generated: tests/test_user_registration.py
Processing specs/payment/checkout.md
Generated: tests/test_payment_checkout.py
Total: 15 test files generated
実装での一般的なハマりどころ
このアプローチを導入する際に注意すべき点があります。
ハマりどころ1: 仕様の粒度が曖昧
仕様が大きすぎると、AIが生成するテストも不正確になります。1つの仕様ファイルは、1つの機能に焦点を当て、3~5個のシナリオに限定するのが目安です。
ハマりどころ2: 非機能要件の記述忘れ
レスポンス時間、キャッシュ戦略、セキュリティ要件などの非機能要件を仕様に含めないと、実装後に問題が生じます。Constraintsセクションに明示的に記載しましょう。
ハマりどころ3: 生成されたテストの過信
spec-kitが生成するテストはスケルトンであり、エッジケースを全てカバーしているわけではありません。生成後は必ず人間による確認が必要です。
Pythonでの型安全性の向上
spec-kitをPydanticと組み合わせると、型安全性をさらに高められます:
from pydantic import BaseModel, Field
from typing import List
class SearchQuery(BaseModel):
"""仕様から自動生成される入力スキーマ"""
q: str = Field(..., min_length=1, max_length=100, description="Search query")
page: int = Field(1, ge=1, description="Page number")
limit: int = Field(50, ge=1, le=50, description="Items per page")
class ProductResponse(BaseModel):
"""仕様から自動生成される出力スキーマ"""
id: int
name: str
relevance_score: float = Field(..., ge=0.0, le=1.0)
class SearchResult(BaseModel):
items: List[ProductResponse]
total: int
page: int
has_more: bool
このようにスキーマを定義しておくと、自動生成されるテストにも型情報が反映され、より正確な検証が可能になります。
CI/CDパイプラインへの統合
本番環境では、spec-kitの検証をCI/CDパイプラインに組み込むべきです:
# .github/workflows/spec-validation.yml
name: Spec-Driven Development Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install spec-kit
run: pip install spec-kit
- name: Validate specifications
run: spec-kit validate --spec specs/ --language python
- name: Generate tests
run: spec-kit generate --spec specs/ --output tests/generated/ --language python
- name: Run generated tests
run: pytest tests/generated/ -v
実装環境の設定
Pythonプロジェクトでspec-kitを導入する場合の最小限の設定を示します:
my-project/
├── specs/
│ ├── user-management.md
│ ├── search-api.md
│ └── payment-api.md
├── src/
│ ├── main.py
│ ├── models.py
│ └── api/
│ ├── search.py
│ └── payment.py
├── tests/
│ ├── generated/
│ │ ├── test_user_management.py
│ │ ├── test_search_api.py
│ │ └── test_payment_api.py
│ └── integration/
│ └── test_workflows.py
├── pyproject.toml
└── spec-kit.config.yaml
設定ファイルの例:
version: 1.0
language: python
framework: fastapi
spec:
dir: ./specs
format: markdown
test:
output_dir: ./tests/generated
framework: pytest
include_docstrings: true
ai:
provider: claude
model: claude-3-5-sonnet
auto_fix: false
validation:
strict: true
check_constraints: true
業界別の活用シーン
異なるドメインでの使い分けを考えてみましょう。
| ドメイン | 仕様の重点 | ツールの活用方法 |
|---|---|---|
| 金融系API | セキュリティ、監査証跡、トランザクション整合性 | Constraints セクションで厳格な要件を定義 |
| SaaS アプリケーション | ユーザー体験、エラーハンドリング、UI状態遷移 | 複数のシナリオで正常系・異常系を網羅 |
| IoT バックエンド | レスポンス時間、スケーラビリティ、データフォーマット | 非機能要件を詳細に記述 |
| マイクロサービス | API コントラクト、バージョニング、フォールバック | サービス間の依存関係を仕様に明示 |
まとめ
Spec-Driven Developmentは、仕様の形式化とAI連携による新しい開発アプローチです。github/spec-kitはPythonでこれを実践するための実用的なツールとなります。
- 仕様の構造化は、要件定義の曖昧さを減らし、AIとのコミュニケーション精度を向上させる
- テスト自動生成により、BDD的な思考が自然に身につく
- CI/CD統合で、仕様と実装の乖離を継続的に検証できる
- 複数シナリオ管理により、大規模プロジェクトでも仕様の一貫性を保ちやすい
- 型安全性との組み合わせで、実装品質が格段に向上する
よくある質問(FAQ)
Q1: 既存のテストコードがある場合、どう移行すべき?
既存のテストコードは一度に置き換える必要はありません。新機能からSpec-Driven Developmentを導入し、段階的に仕様を追加していくアプローチをお勧めします。既存テストと並行して運用し、リファクタリング時に仕様に統合するとスムーズです。
Q2: AIが生成したテストに信頼性がない場合は?
spec-kitの生成テストは初期スケルトンと考え、必ず人間による検査が必要です。特にエッジケース、セキュリティ検証、パフォーマンス境界値は手動で追加すべき領域です。auto_fix: falseに設定し、手動レビューを厳しくするのが安全です。
Q3: マイクロサービスアーキテクチャでの使用方法は?
マイクロサービス環境では、各サービスごとにspecs/ディレクトリを配置し、サービス間のAPI コントラクトを仕様に明記します。共有仕様はspecs/shared/に配置し、サービス間の整合性を保ちます。APIゲートウェイレベルでも統合テストを仕様化すると、サービス間の連携検証がより堅牢になります。

