最近、コーディングエージェントと協力して開発する手法が主流になりつつあると思います。
ただ、ざっくりした指示だと思ったような実装にならないことも多いですよね。
そんな中で気になっていたのが、SDD(Spec-Driven Development)という開発スタイルです。
「仕様を先に固めてから実装する」アプローチで、AIとの協業と相性が良いとされています。
今回はこのSDDを使って、実際にWebアプリを開発してみました。
今回の検証で分かったのは、SDDは強力だが、仕様レビューの負荷が想像以上に高いということでした。
そこで、レビュー体験を改善するSDDツールへの切り替えも試しています。
この記事では、以下の3点についてお伝えします。
検証環境
| 項目 | 内容 |
|---|---|
| SDDツール | cc-sdd → spec-workflow-mcp |
| AIエージェント | Codex(gpt-5.2-codex) |
題材としたアプリ
今回は「RSSニュースをユーザーの趣味嗜好、フィードバックに合わせてスコアリングするWebアプリ」を題材にしました。
アプリ自体が目的ではないので、シンプル(かつギリギリ役に立ちそう)なアプリとしています。
主な機能
- RSS URLとユーザーの趣味嗜好を入力
- 記事ごとにGood/Bad評価をつけられる
- 各ニュース記事についてAIエージェントに質問可能
- ニュース取得ボタンで収集とスコアリングを実行
成果物のイメージは以下です。
・メイン画面

・AIエージェントとのチャット画面

技術スタック
- Frontend: TypeScript + React / Vite + TailwindCSS
- Backend: Python + FastAPI
- Infra: AWS CDK + API Gateway + Lambda
SDDとは
本題に入る前に、SDDについて簡単に説明します。
SDDとは、要求・設計・タスクを先に固めてから実装に進むことで、曖昧さを減らす開発スタイルです。
今回は、以下のような手順を採用して開発を進めました。
| フェーズ | 内容 |
|---|---|
| Steering | プロダクト/技術/構成の前提をそろえる(product.md / tech.md / structure.md) |
| Requirements | ユーザーストーリーと受け入れ基準を定義する |
| Design | UI・API・データモデルなどの設計を詰める |
| Tasks | 実装タスクに分解し、順序と依存を整理する |
| Implementation | タスクを順番に実装する(必要に応じて設計へ戻る) |
コーディングエージェントに開発を任せられる粒度まで仕様を先に固める分、レビュー対象となるドキュメントの量が多くなりやすいのが特徴です。
1. cc-sddでSDDを試す
まずはCLIベースのSDDツールであるcc-sdd( リポジトリ:https://github.com/gotalab/cc-sdd )を試してみました。
cc-sddは、Kiro1の設計思想(AWSでは、AI-DLCと呼んでいます)を他のAIエージェント上で再現するためのCLIツールです。成果物はMarkdownで出力されます。今回はCodex CLIと組み合わせて運用しました。
ツールの仕組みについては詳しい記事がたくさん出ていますので、ここでは実際に使ってみた所感を中心に書きます。
良かった点
- 設計漏れが起きにくい: 不完全な設計書を渡しても、LLMが不足分を指摘してくれる
- 仕様を充実させるほど安定する: 前提条件を詳しく書くほど、出力のブレが減る
課題に感じた点
- 「仕様として十分か」の判断が難しい: どこまで設計すればAIに渡す前提として足りているのか、慣れが必要
- AIの柔軟さが損なわれる可能性: スクラッチで書く場合、最初から仕様を詰め切れないことも多い。中途半端に詳しい仕様を渡すと、かえってAIの判断を縛ってしまう
- Markdownのレビューが大変: 自動生成された詳細な仕様を読み通すのに時間がかかる
特に負担だったのが、Markdownベースのレビューでした。
仕様として十分な設計をAIが生成してくれるため、ドキュメント量が多くなるのは仕方ないかもしれません。しかし、指摘箇所を日本語で記述するコストが想像以上にかかりました。
たとえば「2.4章の〇〇について、××と書いているが、△△と書いてほしい」のような書き方をしなければならず、レビューのたびにこの作業が発生します。
そこで、SDDの流れは維持しつつ、仕様レビューをGUIで完結できる「spec-workflow-mcp」に切り替えてみました。
2. spec-workflow-mcpを試してみる
spec-workflow-mcp( リポジトリ:https://github.com/Pimzino/spec-workflow-mcp )は、SDDワークフローをMCP(Model Context Protocol)経由で進めるための仕組みです。成果物はMarkdownで管理しつつ、レビューや承認はWebのGUI上で完結できます。
実際に使ってみた流れ
基本的な流れ(requirements → design → tasks → implementation)はcc-sddと同じです。
また、コーディングエージェントとのやり取りも、他のSDDツールと同様、CLIから行います。
他のCLIツールと異なるのは、MCPサーバーとしてローカルにWebフロントエンドが立ち上がることです。
Webからはプロジェクトの様々な情報が確認できるのですが、特に注目すべきなのは、コーディングエージェントの作業をGUI上でレビューできる機能です。
まず、requirementsなどの各ワークフローが完了すると、Web上に承認依頼が届きます。

承認依頼を開くと、Markdownがレンダリングされた状態で仕様を確認できます。

気になる箇所があれば、範囲選択をしてそのままコメントを残せます。


修正依頼を出すと、AIが仕様を直してくれます。変更点はDiffで確認できます。

requirements → design → tasks と順番に承認していき、最後にタスク開始を依頼すると、カンバンボードで進捗が自動更新されていきます。

良かった点
- 行範囲に対してコメントを残せる: 指摘箇所を日本語で説明する必要がなく、直接該当部分にコメントできる
- 通知が分かりやすい: 視覚化されるべき情報が適切に視覚化されている
課題に感じた点
3. SDDで実装品質をどこまで引き上げられるか
ここからは、SDDを使って実装品質をどこまでコントロールできるかを検証します。
3.1 バックエンドのリファクタリング
今回の検証では、要件定義に重きを置き、実装方針はほとんどAIに任せていました。
Steeringでクリーンアーキテクチャに準じることをルールとして明記していたのですが、実際に生成された実装は以下のような構造でした。
backend/ ├── rss_news_agent/ │ ├── __init__.py │ ├── adapters.py │ ├── api.py │ ├── errors.py │ ├── llm.py │ ├── logging_service.py │ ├── models.py │ ├── repositories.py │ ├── services.py │ └── settings.py ├── tests/ ├── app.py ├── lambda_handler.py ├── pyproject.toml ├── requirements.txt └── uv.lock
たとえば repositories.py を見ると、本来Provider層に書くべき実装が混在していました。
class DynamoDbClient(Protocol): def put_item( self, TableName: str, Item: dict[str, dict[str, str]], **kwargs: object, ) -> dict[str, object]: ... def delete_item( self, TableName: str, Key: dict[str, dict[str, str]], ConditionExpression: str, ) -> dict[str, object]: ...
また llm.py を見ると、LLMに関連する様々なデータ型やクラスが一つのファイルに詰め込まれていました。
class LlmFeedbackSummaryDto(BaseModel): good_ids: list[str] = Field(default_factory=list) bad_ids: list[str] = Field(default_factory=list) class BedrockRuntimeClientAdapter: def __init__(self, model_id: str, region: str | None = None) -> None: self._client = boto3.client("bedrock-runtime", region_name=region) self._model_id = model_id
これは良い実装とは言いにくいと思います。そこで、SDDを使ってリファクタリングを依頼してみました。
Steeringの改善
方針として、Steering(今回は structure.md)のコード規約を詳しく追記し、具体的な実装はAIに任せます。
structure.mdの変更(抜粋)
## Directory Organization project-root/ ├── backend/ # FastAPIバックエンド -│ ├── rss_news_agent/ # ドメインロジック(API/サービス/リポジトリ) +│ ├── rss_news_agent/ # ドメインロジック(Clean Architecture) +│ │ ├── handler/ # ルーティング/入力変換 +│ │ ├── model/ # ドメイン/DTOモデル +│ │ ├── repository/ # 永続化インターフェイスと実装 +│ │ ├── service/ # ビジネスロジック +│ │ ├── provider/ # 外部依存(DB/HTTP等)の生成 +│ │ └── usecase/ # エンドポイント単位のユースケース │ ├── tests/ # pytestテスト │ ├── app.py # 本番/デプロイ用エントリ │ ├── local_app.py # ローカル開発用エントリ │ └── lambda_handler.py # Lambdaハンドラ ## Code Organization Principles 5. **クリーンアーキテクチャ指向**: バックエンドは層分離を意識し、依存性注入(DI)を徹底する +6. **責務の抽象化**: 外部依存はproviderに閉じ、ビジネスロジックはservice/usecaseに集約する +7. **境界の明確化**: 抽象(Port/Protocol等)は内側のレイヤーに置き、実装は外側に限定する ## Module Boundaries * **Repositories vs Services**: 永続化ロジックとビジネスロジックを分離 +* **Endpoint vs Usecase**: 原則として1エンドポイントに対して1usecaseを用意する +* **Provider vs Service**: providerは外部アクセスに限定し、ビジネスルールはserviceに置く +* **Port vs Implementation**: 抽象は内側、実装は外側に配置する
生成されたタスク
この変更を反映すると、以下のようなタスクが生成されました。
- [-] 1. 現状のAPI/サービス/リポジトリの依存関係を整理して移行方針を確定する - [-] 2. model層を新設し、ドメインモデル/DTO/入力出力スキーマを整理する - [-] 3. repository層を新設し、永続化インターフェイスと実装を整理する - [-] 4. service層を新設し、ドメインロジックを整理する - [-] 5. usecase層を新設し、1エンドポイント=1usecaseの実装を追加する - [-] 6. handler層を新設し、ルーティングと入出力変換を移行する - [-] 7. provider層を新設し、外部依存の生成を集約する - [-] 8. 既存エントリポイントの接続を更新する
リファクタリングの結果
実装を進めてもらった結果、各レイヤーがしっかり整理された構造になりました。
backend/ ├── rss_news_agent/ │ ├── handler/ │ ├── model/ │ ├── provider/ │ ├── repository/ │ │ ├── __init__.py │ │ ├── article_feedback_repository.py │ │ ├── rss_source_repository.py │ │ └── user_profile_repository.py │ ├── service/ │ ├── usecase/ │ │ ├── __init__.py │ │ ├── add_rss_source.py │ │ ├── delete_rss_source.py │ │ ├── fetch_articles.py │ │ ├── list_rss_sources.py │ │ ├── save_article_feedback.py │ │ ├── save_user_profile.py │ │ ├── send_article_chat.py │ │ └── update_rss_source.py │ └── __init__.py ├── tests/ ├── app.py ├── lambda_handler.py ├── pyproject.toml ├── requirements.txt └── uv.lock
実装内容も改善されました。たとえば user_profile_service.py では、純粋なビジネスロジックだけが実装されています。
class UserProfileService: def __init__( self, repository: UserProfileRepository, clock: Callable[[], datetime] | None = None, ) -> None: self._repository = repository self._clock = clock or datetime.utcnow def save_personal_text(self, personal_text: str, user_id: str = "default") -> UserProfile: profile = UserProfile( user_id=user_id, personal_text=personal_text, updated_at=self._clock(), ) return self._repository.save(profile) def get_personal_text(self, user_id: str = "default") -> str | None: profile = self._repository.get(user_id) if profile is None: return None return profile.personal_text
この検証から、Steeringの質がコーディングエージェントの出力品質に直結することが確認できました。
3.2 UI/UX課題の改善を要件定義からAIに任せる
基本的なアプリケーションは構築できましたが、実際に触ってみると使いにくい点がいくつかありました。
発見した課題
1. RSSソースの編集ボタンが効かない
「Edit」ボタンを押しても何も起きません。これは使いにくい点というより実装漏れですね。

2. パーソナル情報の保存状態が分からない
ユーザーの趣味嗜好を入力する部分で、「SAVE」を押すと入力した文字が消えます。保存できたのか分かりません。

「SAVE」を押すと、バックエンドには保存処理が飛んでいるものの、画面上では文字が消えただけに見えます。

3. チャットの応答待ちが分かりづらい
各記事に対してAIエージェントと会話できる機能がありますが、返答を待っている間のインジケータが分かりにくいです。

AIに要件定義から任せる
これらの課題を具体的には指摘せず、「UI/UX上の課題を自分で見つけて改善方針を仕様化せよ」と指示を出してみました。
以下は、生成された requirements.md の抜粋です。
## Requirements ### Requirement 1: RSSソース管理の編集体験強化 (中略) ### Requirement 2: パーソナルシグナルの保存状態の可視化 (中略) ### Requirement 3: 記事一覧の可読性とスキャン性向上 (中略) ### Requirement 4: 収集アクションと進行状況の明確化 (中略) ### Requirement 5: チャット導線の発見性と文脈維持 (中略) WHEN チャット送信中 THEN システム SHALL 送信中の状態を明確に示し、二重送信を防ぐ。
こちらが指摘しなくても、明らかに使いにくい部分は要件として言及されていました。それ以外の項目も、改善案として納得できる内容でした。
改善結果
このまま機能設計 → タスク設計 → 実装と進めました。実装段階では期待どおりでない部分もあったため、都度フィードバックを与えています。
1. RSSソースの編集
UIにはまだ改善の余地がありますが、編集できるようになりました。

2. パーソナル情報の保存状態
保存されたことが分かりやすくなりました。

3. チャットの応答待ち
インジケータが表示されるようになり、分かりやすくなりました。

この検証から、明らかに問題がある挙動については、AIに要件定義から任せても一定の改善が可能なことが分かりました。
ただし、今回の検証アプリはコンポーネント数が少なく、比較的シンプルな構成でした。より複雑なアプリケーションでは、同じようにうまくいくとは限りません。
おわりに
今回SDDを試してみて、個人的に印象に残ったことを書いておきます。
レビューの負荷は想像以上だった
Markdownで生成された仕様を読んで、日本語で指摘を書いて…という作業は、耐えがたいものがありました。spec-workflow-mcpに切り替えてからは、行を選択してコメントするだけで済むようになり、だいぶ楽になったと思います。SDDを続けるなら、レビュー体験は重要だと感じました。
Steeringは可能な限り詳細に書いたほうが良い
当たり前かもしれませんがSteeringはプロジェクトの根本となる部分であり、品質を担保する意味で非常に重要です。
「クリーンアーキテクチャで」と書くだけでは不十分で、ディレクトリ構成や各レイヤーの責務まで書いてあげると、期待に近い実装が出てきました。ここは試行錯誤しながら調整していく部分かなと思います。
AIに要件定義から任せるのは、ものによる
明らかな問題点は拾ってくれましたが、細かいUI/UXの調整は結局人間がフィードバックを重ねる必要がありました。
まだ手探りな部分も多いですが、SDDとAIエージェントの組み合わせには可能性を感じています。
同じようにSDDを試している方の参考になれば幸いです。
執筆:@kita.ryota
レビュー:@yamashita.tsuyoshi
(Shodoで執筆されました)



