ANDPAD フロントエンドエンジニアの小泉( @ykoizumi0903 )です。主に Vue で開発を行っています。
2025 年も終わりに近づいていますが、ここ 1 年のホットなトピックといえば、何とも言っても AI コーディング。
これまでの開発スタイルでは考えられない速度が出せるため、いかに AI を使いこなすかがエンジニアの生産性を大きく左右するようになっています。1 年前の今頃は、まだ Cline も Claude Code も GitHub Copilot Agent Mode も登場していなかったというのが信じられないですね……。
この記事では、そんな AI エージェントを上手く活用する方法である「仕様駆動開発」を、 Vue ならではの方法 で取り入れるアプローチについて紹介します。
【PR】
本題に入る前に宣伝です。いよいよ今週末(10/25)に迫った Vue Fes Japan 2025、アンドパッドは今年もスポンサーします。スポンサーブースではノベルティやクイズ企画などを用意していますので、参加される方はぜひお立ち寄りください!

「仕様駆動開発」を実際に取り入れる難しさ
さて、AI 開発において、ここ数ヶ月で「仕様駆動開発(Spec-Driven Development)」というワードを耳にする機会が増えました。「振る舞い駆動開発(Behavior-Driven Development)」と呼ばれることもあります。
チャットベースで曖昧な指示を渡す代わりに、最初に厳密な仕様(スペック)を定義し、その仕様書を AI と人間の共通言語として開発を進めるこのアプローチは、AI の出力の品質や保守性を担保しやすく、特に GitHub Copilot Coding Agent のような自律型 AI エージェントを活用する上で効果を発揮します。
一方で、仕様駆動開発では、タスクのゴールや全体の設計をコードを書く前にイメージし、それを正確な自然言語に落とし込む能力が求められます。これは、実際に手を動かしてコードを書くことで、曖昧な要件を少しずつ明確にしていくボトムアップの開発スタイルとは真逆の流れです。この根本的な考え方・進め方の違いに上手く馴染めていないという方も多いのではないでしょうか。
仕様駆動開発ツールは銀の弾丸ではない
この導入をサポートするものとして、 OpenSpec や Kiro、Spec Kit などの仕様駆動開発ツールも次々にリリースされて盛り上がりを見せていますが、それも決して銀の弾丸ではありません。というのも、ツールが提供するのはあくまで「仕様を記述するための文法や型」であり、「何を仕様として記述すべきか」という思考の部分は、依然として人間に委ねられているからです。
私自身、Spec Kit のリリース直後に少し試してみましたが、Spec Kit によって一気に出力された仕様書をレビューする作業自体がなかなか負荷が高く、場合によっては自分が書くよりも大変で、仕様駆動開発の大変さを根本的に解消してくれるわけではないと感じました(ツールが悪いのではなく、支援してもらってもまだ難しい、という意味です)。
さらに、既存のプロジェクトに後から仕様駆動開発を導入する場合、仕様書が存在する箇所と存在しない箇所が混在してしまったり、Spec Kit などによって自動作成されるファイルやディレクトリが既存の規約と衝突したりすることもあります。
支援ツールが想定する理想の開発フローに上手く乗れないと、ツールがかえって開発の足枷になってしまうことすらあるのです。
このように、「仕様駆動開発」は AI 開発の理想的な手法ではあるものの、実際に導入するのは容易ではありません。仕様駆動開発のメリットは十分に理解した上で、「正直、ハードルが高くてついていけない……」と悩んでいるエンジニアも少なくないのではないでしょうか。
この記事では、そんな悩みに対する一つの答えとして、Vue のカスタムブロック機能を活用して、「気軽に」仕様駆動開発を取り入れるアイデアを紹介します。
Vue のカスタムブロックとは
Vue の単一ファイルコンポーネント (SFC) は通常、 <template> <script> <style> で構成されますが、これ以外にも任意の名前でブロックを追加することができます。
これがカスタムブロックであり、この仕様を利用するプラグインとして Vue I18n の <i18n> や、vite-plugin-vue-gql の <gql> などがあります。
そしてこのカスタムブロックは当然ながらユーザーが自由に追加することもできます。そこで、このカスタムブロックとして <spec> ブロックを追加して、そこにコンポーネントの仕様を書く、というのが、今回提案する手法となります。
独自カスタムブロックを扱うための事前準備
カスタムブロックも Vue の処理対象になるので、追加の設定を行わないとコンパイルエラーが発生します。が、対応は簡単で、 nuxt.config.ts の vite.plugins プロパティに以下のコードを追加するだけです。(Nuxt 以外での導入は公式ドキュメントを参照してください)
export default defineNuxtConfig({ vite: { plugins: [ { name: "vue-spec-plugin", transform(_, id) { if (/vue&type=spec/.test(id)) { return `export default {}`; } return; }, }, ], }, });
type=spec のブロックを JavaScript で解釈できるように中身を無視して export default {} に置き換え、それ以外では何もしません。プラグインファイルを別に作成する方法もありますが、今回の場合は単に無視するだけなのでインラインでも十分でしょう。
動かすために必要なのはこれだけですが、 eslint-plugin-vue を導入している場合、vue/block-order ルールで順番を固定するとさらに見通しが良くなります。
{ files: ["**/*.vue"], rules: { "vue/block-order": [ "error", { order: ["spec", "script", "template", "style"] }, ], }, }
これで <spec> ブロックが常に一番上に固定されます。 <script> と <template> の順番も、AI がどう実装しても自動修正されるように固定しておきます。
<spec> ブロック駆動開発を試してみる
では、実際に <spec> ブロックを使った開発の流れを紹介します。
今回は例として、特定の要素を明るくハイライトし、その要素以外の画面全体を暗くする「スポットライト」的なコンポーネントを作ります。ライブラリを導入するほどではないものの、自分で汎用的に実装しようと思うと地味に面倒なコンポーネントです。
最初は <spec> から書く
SpotlightOverlay.vue というコンポーネントファイルを作成したら、script や template などは書かず、コンポーネントに仕様だけを記述します。
<spec lang="md"> ## slot で囲まれた部分だけを残して、それ以外を覆って暗くするスクリーンを表示します。 - モーダルダイアログの背景などに利用します。 - スクリーンの背景色は `rgba(0,0,0,0.3)` です。 - スクリーンはその位置を囲むように `<teleport>` タグを通して body 直下に配置されます。 - スクリーンは svg path で描画されます。 </spec>
このファイルを保存したら、Copilot に 「<spec> ブロックに従って app/components/SpotlightOverlay.vue を実装してください」と伝えます。
出来上がったコードは長いので、 Vue SFC Playground にコピーして動かしてみました。
画像の通り、囲んだコンポーネントだけにスポットライトを当てる実装が完成していました。仕様通り SVG や Teleport で実装されています。

更新する時は <spec> ブロックもセットで
ただ、 defineProps が型引数ではなく関数の引数を使っているのが少し気になります。そこで、インラインチャットに以下の指示を出しました。
- defineProps の props 定義を、型引数を使ったものに変更してください。デフォルト値は分割代入によって定義してください。
- spec ブロックに ### props セクションを追加し、props の内容とデフォルト値を追記してください。
この結果、ファイルは以下のように修正されました。

このように、修正とセットで <spec> ブロックも更新するように指示を出すことで、仕様と実装の乖離を防ぐことができます。
spec ブロックを活用するメリット
ここまでで <spec> ブロックを活用した開発の流れをざっくり紹介してきましたが、この手法にどんなメリットがあるかを紹介します。
段階的に導入しやすい手軽さ
まず何といっても導入の手軽さが挙げられます。
Spec Kit のようなツールは使いこなせれば確かに強力ですが、冒頭に書いた通り学習コストが高く、それに見合うリターンを得るまでに時間がかかります。
一方、今回紹介したアプローチでは、既存の .vue ファイルに <spec> タグを追加し、Markdown で仕様を記述するだけなので、新たなライブラリやフレームワークを学習する必要が一切ありません。
<spec> ブロックを追加するだけなら、既存のプロジェクトであっても一部のコンポーネントから段階的に導入できます。この手軽さは、チーム開発において新しいルールを浸透させる上でのハードルも大きく下げてくれるでしょう。
Markdown 記法を採用できるので書きやすい
ファイルの冒頭に仕様を書くというスタイル自体は、TSX や TS ファイルでも コメントや JSDoc を使って実現できるかもしれません。
しかし、 lang="md" 属性で Markdown 記法を取り入れた上で、完全に記述を区別できるのは、Vue の Single File Component ならではの利点です。

VSCode では Vue Official 拡張機能をインストールするだけで .md ファイルと同等のシンタックスハイライトが効き、Markdown の中にコード例を直接記載して指示に含めることも可能です。複雑な仕様を柔軟に表現できて、人間にとっても非常に読み書きしやすいです。
仕様と実装が 1 つの場所にまとまる
コンポーネントの仕様と実装が単一のファイル内に共存するのは大きな利点です。仕様を確認するために別のファイルやツールを開く必要がなくなり、コンポーネントの全体像を .vue ファイルのみで把握できます。
その実装時の仕様が別ディレクトリにある場合、わざわざ階層を辿って探しに行く必要があります。しかも、必ず Spec Kit を使った開発を徹底できていれば良いのですが、人間が修正した場合や、 Spec Kit の導入以前に開発された部分だと、仕様ファイルがあったりなかったりするので、そもそも探すだけ無駄になることさえあります。
その点、spec があるかどうか、どこにあるかを確実に判断できるのは、開発者の認知負荷の軽減にも繋がり、ファイルを修正する際も参考にしやすいです。さらに、同じファイル内で矛盾があれば気づきやすいので、仕様と実装の乖離を防ぎやすくなるというメリットもあります。
仕様がコンテキストとして AI に必ず提供される
「そのファイルを修正する際に、必ずセットで仕様もコンテキストで渡される」というのは、AI に指示を出す上で極めて大きな利点となります。
多くの AI 開発ツールは、現在アクティブなファイルのコンテキストを最優先で参照します。ファイル内に明確な仕様が Markdown 形式で記述されていれば、AI はそれを直接的に解釈し、仕様に準拠したコードを生成する精度が向上します。
OpenSpec や Spec Kit などの、別ディレクトリに仕様書をまとめる手法や、 Coding Agent のように GitHub issue にタスクを書いて渡す方式の場合、初回の新規開発では問題ないものの、そのファイルを後から修正する際に、仕様書ファイルの場所もセットで渡してあげないと AI がその仕様を参照できません。
仮に、specs ディレクトリや特定のファイルを見に行くように copilot-instructions.md などにあらかじめルールを書いておいたとしても、コンテキスト長の関係なのかしれっと無視されるケースが非常に多く、正直あまり信用できません。ファイル内に書いてあるのが最も確実性が高いです。
<spec> ブロックを使う場合に注意すべきポイント
当然ながら、この手法は万能ではありません。導入するにあたって注意すべきポイントをいくつか挙げておきます。
仕様が陳腐化するリスクがある
実装と仕様が同じファイルにあることはメリットである反面、完全に同期されているわけではないので、実装だけが変更され、<spec> ブロックの更新が忘れられるというリスクも内包します。
別の場所で管理されているよりは気づきやすいとしても、緊急のバグ修正などでロジックを少し変更した際に、仕様書の更新を怠ってしまうケースは容易に想像できます。
仕様と実装の間に乖離が生まれると、その仕様書は AI や他の開発者にとってむしろ誤った情報源となり、混乱を招く原因になりかねません。「実装を変更した際は、必ず <spec> も見直す」というチーム内での規約や、コードレビューでのチェックが重要です。 .copilot-instruction.md や pull_request_template.md などを使って、Copilot Review で明示的にこの観点でのレビューを依頼するのも良いかもしれません。
.vue ファイル以外では利用できない
この <spec> ブロックというアプローチは、Vue の単一ファイルコンポーネントのカスタムブロックという仕組みに依存しているため、当然ながら .vue ファイルでしか利用できません。これは一見すると重たい制約に見えます。
ただし、日付をフォーマットするなどの純粋な関数であれば、その責務が単一であり、仕様が複雑になりづらい傾向があります。状態を持った composable も、単一の責務でカプセル化できていれば、JSDoc や TypeScript の型定義だけで仕様を十分に表現できる場合が多く、本手法が使えないことが大きな問題になるケースは少ないでしょう。
また、それらの composables や utils を実際に利用し、最終的な UI としての振る舞いを決定するのは必ずコンポーネントです。Web アプリケーションにおいて、コンポーネントから一切参照されないロジックはほぼ存在しません。最終的な機能の統合点であるコンポーネントに仕様が明記されていれば、それが呼び出すロジックを含めた機能全体の仕様をある程度カバーしているとも考えられます。
コンポーネントは Props、Emits、Slots、内部の状態、デザイン、ユーザーとのインタラクションといった複数の要素が絡み合うため、その「仕様」が単純な関数の入出力よりも複雑になりやすい傾向にあります。
そのため、仕様を書く価値が高いコンポーネントのみに特化しているのは、むしろこの手法の優れた点でもあると言えます。「コンポーネントとそれ以外を完全に区別する」「1 つのコンポーネントを必ず 1 つのファイルで定義する」という Vue の世界観とも上手く調和しています。
<spec> ブロックはあくまで仕様駆動開発の第一歩
今回紹介した手法は、あくまで単一のコンポーネントが持つべき責務を定義することに特化しています。その構造上、複数のコンポーネントが連携して初めて成立するような、より大きな単位の仕様を記述するには不向きです。
そのため、ここまで何度か Spec Kit や Kiro などと比較してきましたが、実際には支援ツールを通して作成するようなアプリケーション全体の仕様書を置き換えるものではなく、むしろ組み合わせて真価を発揮するものでもあります。
ただ、この <spec> ブロック方式は、仕様駆動開発の考え方に慣れていない開発者が、その成功体験を得るための第一歩となります。特別なツールは不要で、いつもの .vue ファイルの中で「まず、このコンポーネントは何をすべきか」を自然言語で書き出すことから始められます。
これを実践していくうちに、開発者は自然と「実装の前に仕様を定義する」というプロセスに慣れ、仕様駆動開発の有用性を実感できます。その上で、将来的に仕様駆動開発を完全に取り入れる際も、<spec> ブロックはその補助輪として無駄になりません。
いきなり大規模なツールや完璧な開発フローを導入しようとして挫折するのではなく、まずはこの手軽な方法で「仕様を書く文化」を根付かせるのは、将来的に本格的な仕様駆動開発を導入するための、非常に現実的かつ効果的な足がかりになるはずです。
まとめ
<spec> カスタムブロックを使った仕様駆動開発について、いかがだったでしょうか。
私自身、AI コーディングの手法をいろいろ試してみましたが、今回紹介したアイデアはかなり気に入っています。
導入のハードルが低いことに加え、確実にコンテキストを汲んでくれるおかげで「せっかく仕様を書いたのに AI が指示を無視する」というストレスがほとんどないのが嬉しいポイントです。
これまでは Coding Agent に大きなタスクを渡すことに苦手意識を持っていたのですが、少しずつ有用性を実感できるようになりました。
なので、既に AI を使いこなしている方よりも、AI エージェント開発にいまいち乗り切れていない人、Spec Kit を上手く使いこなせず挫折してしまった私のような人にこそオススメしたい手法です。
Vue のカスタムブロックをドキュメントとして活用する、という発想そのものは AI が登場する以前からありましたが、コンテキストが毎回リセットされる AI エージェントの登場によって、ファイル内に仕様が書かれていることの価値が大きく向上しています。
この点で「Vue は AI フレンドリーなフレームワークである」と言えるのではないかと思うほど、高い効果が得られるので、Vue 開発者の方はぜひ一度試してみてください!
おわりに
改めての宣伝になりますが、アンドパッドは Vue Fes Japan 2025 にブース出展します。当日来られる方はぜひこの記事の感想を直接お聞かせ頂けたらとても喜びます。
そしてもちろん一緒に働く仲間も大募集中です! カジュアル面談からお気軽にご応募ください。