logoChibiham
cover
🤔

仕様駆動開発への懐疑

導入:既視感のある主張

SNSや技術記事で「仕様駆動開発」という言葉を目にするようになった。コーディングエージェントを使った開発において、「仕様をすべて確定してから開発する」「人は仕様を読み変更するだけにし、コーディングエージェントは仕様を読み取ってコードを修正する」「人はコードを触らない」といった主張だ。

この主張を目にした瞬間、私は強い違和感を覚えた。これは、ウォーターフォールではないか?

ソフトウェア開発の歴史を振り返れば、ウォーターフォールからアジャイル、エクストリームプログラミング(XP)へと移行してきた経緯がある。実装からのフィードバックの重要性、仕様と実装の相互作用、継続的な改善——これらの知見を無視して、再び「仕様を先にすべて書く」という方向に向かうのは、歴史の逆行に等しいのではないか。

仕様駆動開発とは何か

「仕様駆動開発(Specification-Driven Development, SDD)」は、2025年7月にAmazon Web Services(AWS)がリリースした統合開発環境「Kiro」で提唱された開発手法だ。同年9月にはGitHubが同様の思想に基づく「GitHub Spec Kit」を公表し、日本でもKDDI系企業が導入を開始した。

Kiroのワークフローは要件 → 設計 → 実装という3段階で構成される。仕様を「真実の源(Source of Truth)」と位置付け、「ドキュメント第一」のアプローチを採用している。人間とAIの共通言語として仕様を扱い、そこからコードを生成させる。

この手法が注目される背景には、「バイブコーディング」への危機感がある。AIに曖昧な指示を出してコードを生成させる手法は、大規模コードベースで混乱を招き、品質とメンテナンス性の懸念を生む。仕様駆動開発は、**「無秩序なAI活用」vs「仕様駆動開発」**という二項対立の構図で語られることが多い。

歴史の皮肉:なぜ2025年にウォーターフォールが復活したのか

ソフトウェア開発の歴史を振り返ると、ウォーターフォールモデルは長らく主流だった。要件定義 → 設計 → 実装 → テストという順次的なプロセスは、建築や製造業から借用されたアプローチだ。

しかし、このモデルには致命的な問題があった。実装段階で初めて発見される問題が多すぎるのだ。仕様が曖昧だったり、技術的に実現不可能だったり、ユーザーのニーズと乖離していたり——こうした問題は、実際にコードを書いてみないと分からない。

アジャイルやXPが隆盛した理由は、まさにこの点にある。仕様と実装の間には複雑なフィードバックループがあり、両者を往復しながら改善していくことが、良いソフトウェアを作る鍵だと理解されるようになった。

ところが2025年、「要件 → 設計 → 実装」というウォーターフォール的なワークフローが再び登場した。これは歴史の皮肉としか言いようがない。

なぜ人は「仕様を先に書けば解決する」という幻想に繰り返し陥るのか

この現象の背後には、人間の心理が関係していると私は考えている。

複雑性への恐怖——ソフトウェア開発は本質的に複雑だ。その複雑性と向き合うことは、不確実性や混沌に耐えることを意味する。「仕様を先にすべて書く」というアプローチは、この不確実性を排除できるという幻想を与える。

予測可能性への渇望——プロジェクト管理者やステークホルダーは、予測可能なプロセスを望む。「仕様が確定すれば、あとは機械的に実装するだけ」という図式は、非常に魅力的に映る。

極端から極端への振れ——「バイブコーディング」という無秩序への反動として、「仕様駆動開発」という秩序への逃避が起こる。しかし、適切な複雑性との向き合い方は、往々にして中庸にある。

Martin Fowlerの懐疑論:権威からの批判

興味深いことに、著名なソフトウェアエンジニアであるMartin Fowlerのブログには、Birgitta Böckelerによる仕様駆動開発の批判的評価が掲載されている。

彼女は実際にKiroとspec-kitを試用し、以下の問題を指摘している:

過剰な複雑性

小さなバグ修正に対して、Kiroは4つのユーザーストーリーと16個の受け入れ基準を生成した。彼女はこれを**「釘を打つのにハンマーを使う(sledgehammer to crack a nut)」**と表現している。

つまり、小さなタスクには過剰で、大きなタスクには不十分——適用範囲が極めて狭いということだ。

非決定性の問題

さらに致命的なのは、LLM(Large Language Model)の非決定性だ。どれだけ詳細な仕様を書いても、実行のたびにAIが異なるコードを生成する可能性がある。

"Because of the non-deterministic nature of this technology, there will always remain a very non-negligible probability that it does things that we don't want"

この指摘は本質的だ。仕様がどれだけ完璧でも、LLMの非決定性により再現性が保証されないなら、結局のところ実装を見て修正するフィードバックループが不可欠になる。

過去の失敗:モデル駆動開発(MDD)

記事では、モデル駆動開発(Model-Driven Development, MDD)という過去の試みにも言及されている。モデル駆動開発もまた「モデル(仕様)から自動的にコードを生成する」というアプローチだったが、広く普及することはなかった。

歴史は繰り返す。

抽象化の進化:Next.jsから学ぶ教訓

抽象化は一度で完璧にできるものではない。実装からのフィードバックを受けて、段階的に改善していくものだ。

React開発者であるSebastian Markbåge氏の格言がある:

「間違った抽象化から回復するより、抽象化がない状態から回復する方が簡単」

この洞察は、Next.jsのキャッシング戦略の変遷に如実に現れている。

Next.jsの3段階の進化

v13-14: 暗黙的なCache(開発者が意識しない抽象化)

当初、Next.jsは「開発者がキャッシュを意識しなくても、自動的に最適化される」という理想を掲げた。しかし現実には:

  • 予想外のデータの鮮度問題
  • デバッグの困難さ
  • 認知負荷の増大

v14-15: 段階的な改善(ドキュメント、デフォルト変更)

コミュニティのフィードバックを受けて、段階的に改善:

  • fetch()のデフォルトCache廃止
  • ドキュメントの透明性向上
  • staleTimesオプションの追加

しかし、これらは対症療法に過ぎなかった。

v16: "use cache"による明示的な抽象化

根本的な設計変更として、"use cache"ディレクティブを導入:

javascript
export async function getData() {
  "use cache";
  return fetch("...").then((res) => res.json());
}

暗黙的(Opt-out)から明示的(Opt-in)への転換により、開発者が意図した場所でのみキャッシュが働くようになった。

教訓:完璧な仕様は事前に書けない

Next.jsチームは当初、「開発者が意識しない」という仕様(設計思想)を掲げた。しかし、実装してみて、実際にユーザーが使ってみて、初めてその仕様の問題が明らかになった。

抽象的には成立していた設計が、具体化しようとすると矛盾や曖昧さが露呈する——これは多くの開発者が経験的に知っていることだ。

数年かけてコミュニティのフィードバックを受け、PPR(Partial Pre-Rendering)という新しい発見を経て、ようやく優れた抽象化に到達した。

もしNext.jsチームが「仕様を先に完璧に確定してから実装する」アプローチを取っていたら、この進化は起こらなかっただろう。

非決定性とスケールの問題を掘り下げる

ここで、私自身も「本当に不可能なのか?」という疑問を持った。何らかの工夫で、仕様駆動開発を機能させることはできないだろうか。

全生成 vs 差分マイグレーション

例えば、仕様からすべてのコードを宣言的に生成するのではなく、仕様の差分に対する既存コードのマイグレーションとして捉えればどうだろうか?既存のコードベースに対して、仕様の変更を反映する形でAIがマイグレーションを行う。

これなら、ある程度は実現可能な気がする。

しかし、すぐに問題が見えてくる。仕様の変更が既存の実装構造と矛盾する場合、どう対処するのか?構造上の致命的な矛盾が入り込むリスクがあり、結局のところ人間による検証が必須になる。

マイクロサービスレベルなら?

では、スケールを限定すればどうだろう?例えば、EventDrivenアーキテクチャにおける1サービスレベルの粒度——十分に小さく、独立性が高く、境界が明確なコンポーネント。

このサイズなら、仕様駆動が機能するのではないか?

だが、これこそがFowlerの指摘する**「スケールの問題」**だ。小さすぎるタスクには過剰で、大きなタスクには不十分。適用範囲が極めて狭い。

「でも、こうすれば...」と考えたくなる心理——これ自体が、銀の弾丸を求める欲望なのかもしれない。複雑性から逃れたいという欲望が、私たちを繰り返し同じ幻想に導く。

本質的な問い:何が変わって、何が変わらないのか

コーディングエージェントの登場によって、確かに何かが変わった。しかし、すべてが変わったわけではない。

この問いに答えるため、開発者の認知構造を考えてみよう。開発者の認知は**コンセプトモデル(あるべき姿)プロダクションモデル(現状の理解)**という2つのモデルから構成される。仕様駆動開発における「仕様」は、まさにこのコンセプトモデルを指している。

仕様駆動開発の問題は、コンセプトモデルを先に完璧に構築してから一気にプロダクションモデルに反映しようとする点にある。これはフィードバックが得られず、両者の乖離が拡大し、不確実性が最大化される——ウォーターフォールが失敗した理由そのものだ。

変わったこと

仕様→実装の変換コストが劇的に低下した。

以前なら数時間かかっていた実装が、数分で完了する。つまり、コンセプトモデルをプロダクションモデルに反映するサイクルが爆速になった

プロトタイピングが高速化し、試行錯誤のサイクルが短縮された。

変わらないこと

しかし、以下は変わっていない:

  • 良い仕様(コンセプトモデル)を書く難しさ — 曖昧さのない、実装可能なモデルを構築することは、依然として難しい
  • 実装から得られるフィードバックの価値 — 実際に動くコード(プロダクションモデル)を見ることで初めて分かる問題は多い
  • ドメインモデルの構築の難しさ — 複雑なドメインを適切にモデル化することは、コードを書く以前の問題だ
  • LLMの非決定性という新しい制約 — むしろ新たな不確実性が加わった
  • コンセプトモデルとプロダクションモデルの間に差分が存在し続けるという構造的な問題

本質:コンセプトとプロダクションの高速往復

コーディングエージェントの本質は、「コンセプトモデルを先に確定する」ことではなく、「コンセプトモデルとプロダクションモデルを高速に往復できる」ことにあると私は考えている。

以前は、仕様(コンセプトモデル)を変更してから実装(プロダクションモデル)を更新するまでに時間がかかった。だから「仕様を先に確定する」ことが重視された。

しかし今は、両者を何度でも高速に往復できる。だからこそ、フィードバックループを回しながら両者を共進化させるアプローチが可能になった。

差分をゼロにすることは原理的に不可能だ(常に外部から新たなフィードバックが入り、コンセプトモデルが先に進んでしまう)。重要なのは、差分を小さく保ち、扱いやすくすることなのだ。

DDDとコンテキストの制約

ドメイン駆動設計(DDD)の観点からも、仕様駆動開発には疑問がある。Eric Evansが強調したユビキタス言語は、ドメインエキスパート、開発者、そしてコードの間で共有される。これは仕様とコードが分離していないことを意味する。

コードベースと文書の両方をメンテナンス対象とすると、「どちらが正しいか」という認知負荷の源泉が生じる。 この観点から、正の情報源(Source of Truth)はコードベースに限定すべきだ。設計案やADRなどの文書は「検討の記録」として残すが、継続的なメンテナンス対象とはしない前提を明確にしておくことで、差分負荷の増大を防げる。

コーディングエージェントとの対話においても同様だ。LLMにはコンテキストサイズの制約があり、仕様書とコードの両方を読ませるのはコンテキストを浪費する。コードベースに十分な構造と意図が表現されていれば、AIはコードベースを読むことでプロダクションモデルを理解できる。

第三の道:仕様・実装共進化のアプローチ

「バイブコーディング」でも「仕様駆動開発」でもない、第三の道がある。仕様と実装を高速に往復するアプローチだ。

例えば、認証機能を追加する場合。ウォーターフォール的には「OAuth 2.0、JWT、リフレッシュトークン、セッション管理...」をすべて先に設計する。しかし仕様・実装共進化では、まず「ユーザーはメールアドレスとパスワードでログインできる」という最小限のコンセプトモデルから始める。コーディングエージェントに実装させ、生成されたコードを見て「トークンの保存はlocalStorageか。でもXSS対策でhttpOnlyクッキーにすべきでは?」と気づく。コンセプトモデルを更新し、再実装。このサイクルを高速に回すことで、差分を小さく保ちながら段階的に洗練させていく。

プロダクションモデルからのフィードバックがなければ、良いコンセプトモデルは書けない。

結論:フィードバックループこそが本質

仕様駆動開発の主張者たちが見落としているのは、複雑性は仕様と実装の相互作用の中にこそ存在するということだ。仕様を詳細化しても複雑性は消えない。

銀の弾丸はない。 Fred Brooksが1986年に指摘したこの真理は、2025年の今でも変わらない。コーディングエージェントという強力なツールを手に入れた私たちは、「仕様を書けば自動的にコードができる」という幻想に飛びつきたくなる。しかし歴史が教えてくれるのは、極端から極端への振れは失敗するということだ。

Next.jsのキャッシング戦略の進化、ウォーターフォールからアジャイルへの移行、そして今回の仕様駆動開発論争——これらすべてに共通するのは、フィードバックループの重要性だ。実装からのフィードバックを受けて仕様を改善し、仕様の意図を実装に反映し、またフィードバックを受ける。この循環こそが、良いソフトウェアを生み出す鍵である。


参考文献