AI時代の仕様駆動開発: Markdownでコードを書く新しいパラダイム
GitHub Spec Kitで実現する体系的なAI開発手法。Vibe Codingを超えて、スケーラブルで保守可能なプロダクションコードを書く完全ガイド
AIコーディングの新しいパラダイム
2025年初頭、Andrej Karpathyが「Vibe Coding」という用語を作りました。AIにプロンプトを投げて、生成されたコードをコピーして、動作することを祈る方式。プロトタイプには効果的ですが、大規模プロジェクトでは急速に崩壊します。
今、仕様駆動開発(Specification-Driven Development, SDD)が登場しました。Markdownで明確な仕様を書けば、AIコーディングエージェントがそれを実行可能なコードに「コンパイル」します。単なる方法論の変化ではなく、AIと一緒にソフトウェアを構築する方法の根本的な転換です。
Vibe Codingの限界
Vibe Codingの問題点を実際のシナリオで見てみましょう:
// Vibe Coding方式
// プロンプト: "ユーザー認証システムを作って"
// AIが生成したコード (1回目の試行)
function login(username: string, password: string) {
// 一部のロジック...
return true; // 常に成功?
}
// 問題発見、再プロンプト: "パスワードハッシュを追加して"
// AIがコードを再生成... 以前のロジックの一部が失われる
// また プロンプト: "トークン有効期限の処理を追加して"
// また再生成... ますます複雑になり一貫性がなくなる
問題点:
- プロンプトするたびにAIが全体のコンテキストを失う
- 以前の決定事項が無視されたり上書きされたりする
- コード品質が一貫性がなく予測不可能
- スケーリング不可能 (1-2ファイルは大丈夫だが、50ファイルは?)
仕様駆動開発とは?
仕様駆動開発は「何を(What)」構築するかを明確に定義した後、AIが「どのように(How)」を実装する手法です。
核心原則
-
仕様が真実の源泉(Single Source of Truth)
- コードではなく仕様がプロジェクトの定義
- すべての変更は仕様の更新から開始
-
構造化されたワークフロー
- 仕様作成(Specify) → 計画策定(Plan) → 作業分解(Task) → 実装(Implement)
- 各段階が明確に分離され追跡可能
-
AIをツールとして、開発者を設計者として
- 開発者は「何を」決定 (アーキテクチャ、ビジネスロジック)
- AIは「どのように」を実行 (コード生成、テスト、最適化)
従来の開発との比較
| 側面 | 従来の開発 | Vibe Coding | 仕様駆動開発 |
|---|---|---|---|
| 開始点 | 要件文書 | 即興的なプロンプト | 構造化された仕様書 |
| AIの役割 | なしまたは補助ツール | 全体のコード生成 | 仕様ベースのコード生成 |
| 一貫性 | 開発者の経験に依存 | 低い (プロンプトごとに変動) | 高い (仕様が保証) |
| スケーリング | 可能だが遅い | 不可能 (複雑度↑ 品質↓) | 優秀 (仕様のみ管理) |
| 保守性 | コード修正必要 | 全体再生成のリスク | 仕様更新後再生成 |
| 協業 | コードレビュー | 困難 | 仕様レビュー (より明確) |
実戦例: 仕様駆動で認証システムを構築する
実際のGitHub Spec Kitを使用した例を段階的に見てみましょう。
ステップ1: 仕様作成 (Specification)
<!-- spec/auth.md -->
# ユーザー認証システム仕様書
## 概要
JWTトークン、パスワードハッシュ、セッション管理を含むセキュアな認証システム。
## 機能要件
### FR-1: ユーザー登録
- **入力**: username (文字列、3-20文字)、email (有効な形式)、password (最小8文字)
- **処理過程**:
- 入力値検証
- bcryptでパスワードハッシュ (コスト係数: 12)
- データベースにユーザー保存
- 認証メール生成
- **出力**: ユーザーオブジェクト (パスワード除く)、HTTP 201
- **エラーケース**:
- 重複username/email → HTTP 409
- 無効な入力 → HTTP 400
### FR-2: ユーザーログイン
- **入力**: username/email、password
- **処理過程**:
- データベースでユーザー検索
- ハッシュ化されたパスワード比較
- JWTトークン生成 (有効期限: 24時間)
- セッションレコード作成
- **出力**: { token, refreshToken, expiresAt }
- **エラーケース**:
- 無効な認証情報 → HTTP 401
- アカウント未認証 → HTTP 403
### FR-3: トークン更新
- **入力**: refreshToken
- **処理過程**:
- リフレッシュトークン検証
- 新しいアクセストークン生成
- セッション更新
- **出力**: 新しいトークンペア
- **エラーケース**:
- 無効または期限切れトークン → HTTP 401
## 非機能要件
### NFR-1: セキュリティ
- パスワードはbcryptでハッシュ必須 (平文保存禁止)
- JWTはRS256アルゴリズムで署名
- リフレッシュトークンは使用ごとにローテーション
- レート制限: IP当たり15分に5回試行
### NFR-2: パフォーマンス
- 登録: < 500ms (95パーセンタイル)
- ログイン: < 200ms (95パーセンタイル)
- トークン更新: < 50ms (95パーセンタイル)
### NFR-3: データベーススキーマ
```sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
refresh_token VARCHAR(255) UNIQUE NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
```
## 実装ガイドライン
- TypeScript strictモード使用
- REST API規則準拠
- 全エンドポイントに対するユニットテスト作成 (カバレッジ > 90%)
- テスト可能性のための依存性注入使用
- すべての認証試行をログ記録 (成功/失敗)
ステップ2: 計画策定 (Planning)
AIエージェントに仕様に基づいて技術計画を生成するよう依頼:
# GitHub Spec Kit使用
npx github-spec-kit plan spec/auth.md
AIが生成した計画:
<!-- plan/auth-plan.md -->
# 認証システム - 技術計画
## アーキテクチャ
### コンポーネント
1. **AuthController**: RESTエンドポイントハンドラー
2. **AuthService**: ビジネスロジック層
3. **UserRepository**: データベースアクセス層
4. **PasswordHasher**: bcryptラッパーユーティリティ
5. **JWTManager**: トークン生成/検証
6. **RateLimiter**: リクエスト制限ミドルウェア
### 依存関係
- express: ^4.18.0
- bcryptjs: ^2.4.3
- jsonwebtoken: ^9.0.0
- pg: ^8.11.0 (PostgreSQLクライアント)
- express-rate-limit: ^7.0.0
- joi: ^17.10.0 (検証)
### ファイル構造
```
src/
├── controllers/
│ └── auth.controller.ts
├── services/
│ └── auth.service.ts
├── repositories/
│ └── user.repository.ts
├── utils/
│ ├── password-hasher.ts
│ ├── jwt-manager.ts
│ └── validators.ts
├── middleware/
│ └── rate-limiter.ts
├── types/
│ └── auth.types.ts
└── tests/
├── auth.controller.test.ts
├── auth.service.test.ts
└── user.repository.test.ts
```
## 実装ステップ
### Phase 1: コアインフラ
1. TypeScript設定構成
2. データベース接続設定
3. データベースマイグレーション実装
4. テストフレームワーク設定 (Jest)
### Phase 2: ユーティリティ
5. bcryptを使用したPasswordHasher実装
6. RS256を使用したJWTManager実装
7. 入力検証器実装
8. レート制限ミドルウェア追加
### Phase 3: データ層
9. UserRepository実装 (CRUD)
10. SessionRepository実装
11. データベーストランザクションサポート追加
### Phase 4: ビジネスロジック
12. AuthService.register()実装
13. AuthService.login()実装
14. AuthService.refreshToken()実装
15. メール認証ロジック追加
### Phase 5: API層
16. AuthControllerエンドポイント実装
17. エラー処理ミドルウェア追加
18. リクエスト/レスポンスロギング設定
### Phase 6: テストと文書化
19. ユニットテスト作成 (目標: 95%カバレッジ)
20. 統合テスト作成
21. APIドキュメント生成
22. セットアップ手順を含むREADME追加
## テスト戦略
- **ユニットテスト**: 各サービス/ユーティリティ関数を分離
- **統合テスト**: 完全なリクエスト/レスポンスサイクル
- **負荷テスト**: パフォーマンス要件検証
- **セキュリティテスト**: 認証フローのペネトレーションテスト
ステップ3: 作業分解 (Task Breakdown)
計画を実行可能な作業に分解:
<!-- tasks/auth-tasks.md -->
# 認証システム - タスクリスト
## Task 1: TypeScriptプロジェクト設定
- [ ] npmプロジェクト初期化
- [ ] TypeScriptおよび依存関係インストール
- [ ] tsconfig.json設定 (strictモード)
- [ ] ESLintおよびPrettier設定
- **推定時間**: 30分
## Task 2: データベース設定
- [ ] PostgreSQLデータベース作成
- [ ] マイグレーションスクリプト作成 (users、sessionsテーブル)
- [ ] 接続プール設定
- [ ] 環境変数追加 (.env)
- **推定時間**: 1時間
## Task 3: PasswordHasherユーティリティ実装
```typescript
// src/utils/password-hasher.ts
import bcrypt from 'bcryptjs';
export class PasswordHasher {
private readonly saltRounds = 12;
async hash(password: string): Promise<string> {
return bcrypt.hash(password, this.saltRounds);
}
async compare(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
}
```
- [ ] hash()メソッド実装
- [ ] compare()メソッド実装
- [ ] ユニットテスト作成
- **推定時間**: 30分
[... 各タスクについて具体的なコードスニペットと共に続く ...]
ステップ4: 実装 (Implementation)
今、AIコーディングエージェント(GitHub Copilot、Claude Codeなど)に各タスクを実装するよう依頼:
# Claude Code使用例
@claude "Task 3をspec/auth.mdとplan/auth-plan.mdを参考に実装してください。
テストも一緒に作成し、coverage 90%以上を確保してください。"
AIが生成したコード例:
// src/utils/password-hasher.ts
import bcrypt from 'bcryptjs';
/**
* Password hashing utility using bcrypt
* Complies with NFR-1 security requirements from spec/auth.md
*/
export class PasswordHasher {
private readonly saltRounds = 12;
/**
* Hash a plain text password
* @param password - Plain text password (min 8 chars)
* @returns Hashed password string
* @throws Error if password is invalid
*/
async hash(password: string): Promise<string> {
if (!password || password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
return bcrypt.hash(password, this.saltRounds);
}
/**
* Compare plain text password with hash
* @param password - Plain text password
* @param hash - Stored password hash
* @returns True if password matches, false otherwise
*/
async compare(password: string, hash: string): Promise<boolean> {
try {
return await bcrypt.compare(password, hash);
} catch (error) {
// Invalid hash format
return false;
}
}
}
// src/utils/password-hasher.test.ts
import { PasswordHasher } from './password-hasher';
describe('PasswordHasher', () => {
let hasher: PasswordHasher;
beforeEach(() => {
hasher = new PasswordHasher();
});
describe('hash()', () => {
it('should hash a valid password', async () => {
const password = 'mySecurePassword123';
const hash = await hasher.hash(password);
expect(hash).toBeDefined();
expect(hash).not.toBe(password);
expect(hash.startsWith('$2a$') || hash.startsWith('$2b$')).toBe(true);
});
it('should throw error for password < 8 chars', async () => {
await expect(hasher.hash('short')).rejects.toThrow(
'Password must be at least 8 characters'
);
});
it('should generate different hashes for same password', async () => {
const password = 'mySecurePassword123';
const hash1 = await hasher.hash(password);
const hash2 = await hasher.hash(password);
expect(hash1).not.toBe(hash2); // Different salts
});
});
describe('compare()', () => {
it('should return true for correct password', async () => {
const password = 'mySecurePassword123';
const hash = await hasher.hash(password);
const result = await hasher.compare(password, hash);
expect(result).toBe(true);
});
it('should return false for incorrect password', async () => {
const password = 'mySecurePassword123';
const hash = await hasher.hash(password);
const result = await hasher.compare('wrongPassword', hash);
expect(result).toBe(false);
});
it('should return false for invalid hash', async () => {
const result = await hasher.compare('password', 'invalid_hash');
expect(result).toBe(false);
});
});
describe('performance', () => {
it('should hash within 500ms', async () => {
const start = Date.now();
await hasher.hash('mySecurePassword123');
const duration = Date.now() - start;
expect(duration).toBeLessThan(500);
});
});
});
核心ポイント:
- AIが仕様書のすべての要件を正確に反映 (NFR-1セキュリティ要件)
- エラー処理、エッジケース、パフォーマンステストすべて含む
- コードが一貫性があり文書化されている
- 90%以上のテストカバレッジを自動達成
GitHub Spec Kit: 仕様駆動開発ツール
GitHubがオープンソースで公開したSpec Kitは、仕様駆動開発を実践するための完全なツールキットです。
インストールと設定
# npmでグローバルインストール
npm install -g @github/spec-kit
# またはnpxで即座に使用
npx @github/spec-kit init
# プロジェクト初期化
spec-kit init my-project
cd my-project
プロジェクト構造:
my-project/
├── spec/
│ ├── constitution.md # プロジェクト憲法 (コーディング原則、スタイルガイド)
│ ├── architecture.md # システムアーキテクチャ
│ └── features/
│ ├── auth.md # 機能別詳細仕様
│ └── api.md
├── plan/
│ └── technical-plan.md # AI生成技術計画
├── tasks/
│ └── task-breakdown.md # 実行可能なタスクリスト
├── scripts/
│ └── validate-spec.sh # 仕様検証スクリプト
└── .speckit/
└── config.json # Spec Kit設定
constitution.md: プロジェクトの憲法
constitution.mdはAIエージェントが従うべき不変の原則を定義します:
<!-- spec/constitution.md -->
# プロジェクト憲法
## 核心原則
### コード品質
- **言語**: TypeScript strictモード有効化
- **スタイル**: Airbnb JavaScript スタイルガイド準拠
- **テスト**: 最低90%のコードカバレッジ
- **ドキュメント**: すべての公開APIにはTSDocコメント必須
### アーキテクチャパターン
- **設計**: ドメイン駆動設計原則準拠
- **依存性注入**: すべての依存関係にコンストラクタ注入使用
- **エラー処理**: エラーを無視しないこと; 常にログ記録後伝播
- **Async/Await**: コールバックや生のPromiseよりasync/await優先
### セキュリティ
- **入力検証**: JoiまたはZodですべてのユーザー入力を検証
- **SQLインジェクション**: 常にパラメータ化されたクエリ使用
- **認証**: すべての認証エンドポイントにレート制限実装
- **秘密鍵**: 秘密鍵ハードコード禁止; 環境変数使用
### テスト戦略
- **ユニットテスト**: すべてのビジネスロジックにJest使用
- **統合テスト**: APIエンドポイントにSupertest使用
- **E2Eテスト**: 重要なユーザーフローにPlaywright使用
- **テスト駆動開発**: 実装前にテスト作成
### Gitワークフロー
- **ブランチ**: GitFlow (main, develop, feature/*, hotfix/*)
- **コミット**: Conventional Commits形式 (feat, fix, docsなど)
- **プルリクエスト**: 2名の承認とCI通過必須
- **コードレビュー**: コードだけでなく仕様準拠を確認
## AIエージェント指示
コード実装時:
1. **明確化質問必須**: 仕様が曖昧な場合は常に質問すること
2. **長所と短所のリスト**: 複数のアプローチがある場合は長所短所提示
3. **テスト駆動開発準拠**: テストを先に作成
4. **可読性最適化**: 賢さより可読性優先
5. **TODOコメント追加**: 既知の制限や将来の改善を明記
ワークフロー: Specify → Plan → Implement
# 1. 仕様作成 (開発者が直接作成またはAI補助)
code spec/features/user-profile.md
# 2. 仕様検証
spec-kit validate spec/features/user-profile.md
# 3. 技術計画生成 (AI)
spec-kit plan spec/features/user-profile.md --output plan/user-profile-plan.md
# 4. 作業分解 (AI)
spec-kit tasks plan/user-profile-plan.md --output tasks/user-profile-tasks.md
# 5. 実装 (AIコーディングエージェント)
# GitHub Copilot、Claude Codeなどと一緒に使用
# AIにspec/、plan/、tasks/ファイルをコンテキストとして提供
実戦適用: 仕様駆動開発のベストプラクティス
1. 良い仕様書の書き方
明確な入出力定義:
❌ 悪い例:
## User Registration
Create a new user account.
✅ 良い例:
## User Registration (FR-001)
**Input**:
- username: string (3-20 alphanumeric chars)
- email: string (valid RFC 5322 format)
- password: string (min 8 chars, 1 uppercase, 1 number, 1 special)
**Process**:
1. Validate inputs (Joi schema)
2. Check username/email uniqueness
3. Hash password (bcrypt, cost factor 12)
4. Insert into users table
5. Send verification email (async job)
**Output**:
- Success: { userId: UUID, username: string, email: string } + HTTP 201
- Failure: { error: string, field?: string } + HTTP 4xx
**Error Cases**:
| Condition | Response | HTTP Code |
|-----------|----------|-----------|
| Duplicate username | "Username already exists" | 409 |
| Invalid email format | "Invalid email format" | 400 |
| Password too weak | "Password does not meet requirements" | 400 |
測定可能な非機能要件:
❌ 悪い例:
## Performance
The system should be fast.
✅ 良い例:
## Performance (NFR-001)
| Metric | Target | Measurement |
|--------|--------|-------------|
| API Response Time | p95 < 200ms | New Relic APM |
| Database Query Time | p99 < 50ms | PostgreSQL EXPLAIN ANALYZE |
| Concurrent Users | 10,000 | Load testing with k6 |
| Error Rate | < 0.1% | Error tracking with Sentry |
**Load Testing Scenario**:
- Ramp-up: 0 → 10,000 users over 5 minutes
- Sustained: 10,000 users for 30 minutes
- Peak: 15,000 users for 5 minutes
- Pass Criteria: p95 response time < 200ms throughout
ツール・エコシステム
主要ツールの比較
| ツール | 用途 | 強み | 弱み |
|---|---|---|---|
| GitHub Spec Kit | 仕様 → 計画 → 作業 | 公式サポート、統合ワークフロー | 初期バージョン (実験的) |
| Kiro | AI仕様検証 | 仕様品質分析 | Spec Kit依存性 |
| BMAD-Method | エンタープライズ仕様管理 | 大規模チーム協業 | 商用 (有料) |
| Claude Code | AIコーディングエージェント | 高いコード品質 | APIコスト |
| GitHub Copilot | AIコーディング補助 | IDE統合優秀 | コンテキスト制限 |
推奨ツールチェーン
スタートアップ/小規模チーム:
├── GitHub Spec Kit (無料)
├── GitHub Copilot (個人: $10/月)
└── GitHub Actions (CI/CD, 無料)
中大型企業:
├── BMAD-Method (エンタープライズ)
├── Claude Code (チームライセンス)
├── Kiro (仕様検証)
└── Jenkins/GitLab CI (既存インフラ)
成果測定: Before & After
実際のプロジェクト事例
プロジェクト: E-commerce API (50エンドポイント、3名開発チーム)
| 指標 | 従来の開発 | Vibe Coding | 仕様駆動開発 |
|---|---|---|---|
| 開発期間 | 12週 | 8週 (初期は速い) | 10週 |
| バグ発見 | スプリント中平均45個 | スプリント中平均80個 | スプリント中平均15個 |
| リファクタリング時間 | 全体の20% | 全体の40% | 全体の5% |
| コードレビュー時間 | PR当たり平均2時間 | PR当たり平均3時間 | PR当たり平均30分 |
| テストカバレッジ | 75% | 45% | 92% |
| 技術的負債 | 中間 | 高い | 低い |
| チーム満足度 | 7/10 | 6/10 | 9/10 |
核心インサイト:
- 仕様駆動開発は初期の仕様作成時間が追加されるが、全体プロジェクトでは時間節約
- バグが70%減少 (仕様が明確ならAIが正確なコード生成)
- リファクタリング時間75%減少 (最初から構造が明確)
- コードレビューが「仕様準拠確認」に単純化
限界と注意事項
仕様駆動開発が適していない場合
-
迅速なプロトタイピング
- MVPやPoCはVibe Codingがより速い
- 仕様作成のオーバーヘッドが不要
-
明確でない要件
- 探索的プロジェクトはアジャイルアプローチがより適切
- 仕様を頻繁に変更すると逆に非効率
-
1人開発者 + 小規模プロジェクト
- 協業の利点がなければ過度なプロセス
- 簡単なスクリプトやツールは直接コーディングがより速い
結論: 開発者の役割再定義
仕様駆動開発は単なる方法論ではなく、AI時代の開発者の役割の根本的な変化を意味します。
変化する開発者の役割
Before (従来の開発):
- コード作成70% + 設計20% + テスト10%
After (仕様駆動開発):
- 仕様作成40% + AI管理30% + 検証20% + 最適化10%
核心スキルの変化
| 従来のスキル | 重要度変化 | 新しい核心スキル |
|---|---|---|
| コーディング速度 | ↓↓ | 要件明確化能力 |
| 文法知識 | ↓ | アーキテクチャ設計能力 |
| デバッグ | → | AIプロンプトエンジニアリング |
| アルゴリズム | → | システム思考 (System Thinking) |
| コードレビュー | → | 仕様レビュー |
始める
1週目: 学習
# GitHub Spec Kitチュートリアル
npx @github/spec-kit tutorial
# サンプルプロジェクトをクローン
git clone https://github.com/github/spec-kit-examples
cd spec-kit-examples/todo-api
2週目: 小規模適用
- 既存プロジェクトの1つの機能を仕様駆動でリファクタリング
- constitution.md作成 (チームコーディング原則)
- 簡単なAPIエンドポイント1-2個を仕様 → コードで実装
3週目: チーム導入
- チームメンバーに概念を共有
- 次のスプリントの1つのストーリーを仕様駆動で試行
- 振り返りで改善点を議論
1ヶ月後: 全面導入決定
- 成果測定 (バグ減少率、開発速度、チーム満足度)
- ツール選択 (Spec Kit vs 商用ツール)
- 長期ロードマップ策定
参考資料
公式ドキュメント
- GitHub Spec Kit 公式ドキュメント
- Spec-Driven Development 紹介 (GitHub Blog)
- Microsoft: Diving Into Spec-Driven Development
深化学習
- The New Stack: Spec-Driven Development for Scalable AI Agents
- Medium: Specification-Driven Development (SDD) by noailabs
- InfoWorld: Spec-driven AI coding with GitHub’s Spec Kit
コミュニティ
次回記事: AIエージェント協業パターン: 5つの専門エージェントでフルスタックアプリを構築するでは、Architecture Agent、Coding Agent、Testing Agent、Security Agent、DevOps Agentをオーケストレーションして複雑なアプリケーションを構築する実戦事例を扱います。
他の言語で読む
- 🇰🇷 한국어
- 🇯🇵 日本語(現在のページ)
- 🇺🇸 English
- 🇨🇳 中文
この記事は役に立ちましたか?
より良いコンテンツを作成するための力になります。コーヒー一杯で応援してください!☕