はじめに
こんにちは。プロダクト開発部 テックリードの夕暮おこはです。
現在、私たちのチームでは生成AIを開発プロセスに本格的に統合し、AIエージェントに実装の大部分を任せる、いわゆる「Vibe Coding」に近い開発スタイルを取り入れています。
AIにコードを書かせることで、確かに開発速度は向上します。しかし、その代償としてシステムの「変更容易性」が失われるリスクがあります。
AI任せの開発には、常に3つのリスクがあります。
-
ビジネス(現実)からの乖離: ドメイン知識を持たないAIは、表面的なロジックだけで実装し、ビジネスルールを形骸化させ、新要件を満たすために平気で既存のビジネス要件をデグレさせて来る。
-
「違法建築」の加速: 既存コードが整理されていない場合、AIはそのパターンを模倣し、複雑性を増幅させる傾向がある。
これらが組み合わさった時、システムは保守困難な状態に陥ります。 そこで私たちは、AIを変更容易性を維持したまま協働できるパートナーとするための設計基盤を整備しました。
本記事は、実際の運用を通じて我々がぶつかった課題と、それらに対する具体的なアプローチを共有します。
4月の提言:AIに「ビジネス(現実)」を理解させる
今年(2025年)の4月、私は社内勉強会で「AI-Enhanced Domain-Driven Design」というテーマを発表しました。 目的は、AIが陥りやすい「ビジネスからの乖離」を防ぐことでした。
当時、私はすでに「関数シグネチャやインターフェース、テストケースを人間が書き、中身の実装だけをAIにさせる」という手法は日常的に行っていました。しかし、そこから一歩進んで、昨今話題の「Vibe Coding(AIエージェントに実装を自律的に任せる)」スタイルを取り入れることには、強い危機感を持っていました。
「人間が枠組みを作らず、背景も意図も知らないAIにすべてを委ねたらどうなるか?」 「最初だけ動くコードができても、仕様がブラックボックス化して誰もメンテナンスできなくなる未来が見える」
そんな懸念から、私は本格的なAIエージェント活用に踏み切る前に、まず「AIを受け入れるための設計基盤」を整えることが不可欠だと考えました。 その後、私が所属するSquad(開発チーム)の新システム実装において、この構想を導入し、半年かけてチューニングを行ってきました。 (※なお、当社におけるSquad体制やSpotifyモデル導入の経緯については、こちらの記事をご参照ください)
ドメインモデルをドキュメント構造に落とし込む
AIが期待通りの実装をしない主な要因は、コンテキストの欠如です。 そこで、DDD(ドメイン駆動設計)の概念をMarkdownドキュメントの構成に適用するルールを策定しました(当時は社内でWSDと呼んでいました)。
-
SSOT (Single Source of Truth): 事実の定義を一箇所に集約する。
-
ナビゲーションマップ:
README.mdを起点とし、ドキュメント間の関係性を明示する。 -
ユビキタス言語辞書: 用語の厳密な定義と、それが適用される境界付けられたコンテキストを記述する。
共通サブモジュールによるコンテキストの配布
さらに、これらのMarkdownドキュメントとAPI仕様書(OpenAPIスキーマ)を、全マイクロサービス横断の共通リポジトリとして切り出しました。
これをGitのサブモジュールとして各マイクロサービスに導入することで、以下の効果を狙いました。
-
コンテキストの共有: AIは「自分自身のコンテキスト」だけでなく、「利用しようとしている外部サービスのコンテキスト(仕様)」も参照可能になる。
私たちはこの「AIのための共通基盤」を用意した上で、新システムの開発に取り掛かりました。
実践とチューニング
運用を開始してみると、様々な「AIの癖」に直面しました。 半年間のチューニングは、いかにしてAIにコンテキストを読み込ませるかの試行錯誤でした。
指示の無視と「grep的」挙動への対策
当初、「READMEにナビゲーションマップを用意しておけば、AIが全体像を読み解くだろう」と想定していました。 しかし、LLMはコンテキストの読み込みを省略(ショートカット)する傾向がありました。
ユーザーの質問に含まれるキーワード(例えば特定のクラス名)に反応し、インデックスやディレクトリ構造を無視して、直接そのファイルだけを参照しようとするのです。これは目次を飛ばして索引から単語検索する行為に近く、結果として文脈の欠落したコードが生まれる要因となります。
【チューニング結果】 この課題に対し、現在は以下の多層的な仕組みを共通サブモジュールとして整備し、誰でも使えるようにしています。
(1) メタ情報の埋め込み(ボトムアップの文脈付与)
トップダウンのナビゲーションだけでは不十分でした。そこで、各ドキュメントファイル自体にメタ情報を埋め込むアプローチを追加しました。 各ドキュメントの冒頭に、「自身がどのコンテキスト(Bounded Context)に属するか」や「どの親ドキュメントを参照すべきか」を明記します。 さらに重要なのが、「どのガイドラインに従うべきか」というメタ情報の付与です。
# ユーザー登録仕様書
このドキュメントは[ドキュメント作成ガイドライン](./documentation.md)に準拠する必要があります。
_[ガイドラインに戻る](../README.md)_
このように記述することで、AIが直接このファイルを開いたとしても、「自分はガイドラインに従わなければならない」という制約を認識させることができます。
(2) ガイドラインによる統制(LLMへの重要警告)
そして、参照先のガイドライン(documentation.md)には、人間向けの説明だけでなく、AIに対する強力な禁止事項を埋め込んでいます。 実際に使用しているガイドラインの一部を抜粋・要約して紹介します。
# ドキュメント作成ガイドライン
## ⚠️ LLM作業時の重要警告
**ドキュメント更新を行うLLMへ:以下を厳守すること**
- **適当な場所への挿入禁止**: 必ず文書全体の論理的な流れを把握してから配置位置を決定する
- **無計画な構造変更禁止**: 全体の整合性を考慮せずに見出し階層や情報順序を変更しない
- **影響範囲の無視禁止**: 更新が他のドキュメントに与える影響を必ず確認する
- **理由説明の記述禁止**: 「なぜそうするか」ではなく「何ができていればよいか」を簡潔な命令形式で記述する
**配置位置や影響範囲に不確実性がある場合は、推測で進めず人間の開発者にエスカレーションすること。**
「個別のドキュメント」→「ガイドラインへの参照」→「AIへの警告」というチェーンを作ることで、間接的ですが強力にAIの振る舞いを統制しています。
(3) それでもダメな時のアテンション管理(人間による介入)
プロンプトの冒頭で「まずユビキタス言語辞書を読め」と強制するテンプレートも用意しました。 しかし、これらを用意してもスレッドが長くなるとLLMのアテンションが薄れ、指示を無視し始めることがあります。これに対する完全な自動化手段はまだ確立できていません。
現状は、「AIがコンテキストを忘れている」と気づいた時点で、人間が「ユビキタス言語辞書を読みましたか?」と問いかけ、コンテキストを再認識させる運用を行っています。
コード品質との相関:「違法建築」の加速を防ぐ
運用を進める中で判明したのは、「クリーンでないコードをAIに触らせると、システムの複雑化が加速する」ということでした。
「違法建築」の増幅装置としてのAI
既存コードがSOLID原則(特に単一責任の原則)に違反していたり、構造が整理されていない場合、AIはどう振る舞うでしょうか。 AIは「文脈に合わせる」特性があるため、目の前のコードのパターン(悪しきパターンを含む)を学習し、それに似たコードを生成します。
人間なら「ここはリファクタリングすべきだ」と判断できる場面でも、AIは「現状のパターン」を模倣し、さらにロジックを積み上げる傾向があります。 結果として、歪みがAIによって増幅され、変更容易性が損なわれるリスクがあります。
AIに正確に実装させるためには、人間による厳密な設計とリファクタリングが不可欠です。 整った土台(Clean Architecture / SOLID原則)があって初めて、AIは機能します。
テストコードを仕様書として扱う(BDD)
また、AI生成コードの品質を担保し、ブラックボックス化を防ぐために重要だったのが、テストコードのガイドライン策定です。
私たちは元々、TDDにおけるBDD(振る舞い駆動開発)的アプローチを採用しており、「Given(前提)- When(操作)- Then(期待結果)」を明記した日本語のテストケース名を書くことをルールとしていました。 しかし、LLMに自律的にテストを書かせると、この流儀を無視したテストを生成することがありました。
そこで、明確なテストガイドラインを定義し、それをAIに参照させる運用を徹底しました。
# テストガイドライン(抜粋)
テスト関数名は日本語で記述し、Given/When/Thenの条件を明記すること。
これにより、LLMは「日本語のテスト名(仕様)」と「実装コード」の不一致を検知しやすくなります。 結果として、人間は詳細なロジックを追う代わりに「テストケース(仕様)が正しいか」をレビューするだけで済み、変更時の安心感を担保できました。
補足:リードタイムとタスクの並走
最後に一点、運用上のリアルな側面について触れておきます。
この仕組みを整えたとしても、単一のタスクにおけるリードタイム(完了までの時間)は、熟練のエンジニアが自分で書いた方が圧倒的に早いです。AIは推論に時間がかかりますし、レビューと修正の往復も発生するからです。
そのため、私たちにとって重要になるのが「タスクの並走」です。 私の場合は、ターミナルを4つほど切り替えながら、複数のAIエージェントに同時に指示を出して並行作業させています。 「AI待ち」の時間を別のタスクに充てることで、トータルでの生産性を最大化する。これが今の私たちの働き方です。
新たな課題と展望:人間とのインターフェース
AIにとって読みやすい厳密なMarkdownは、人間(特に非エンジニア)にとっては読みづらいという課題も出てきました。 この課題に対しては、現在 Mermaidによる図解の自動化 や、Google Gemsを活用した対話型インターフェース の導入を進めています。ドキュメントを「読む」のではなく「会話する」対象へとシフトさせていく狙いです。
おわりに
半年間の運用を経て、私のSquadでは「ドキュメント構造」と「コード品質」が同期するようになりました。 その結果、冒頭で述べた通り、私自身はここ数ヶ月ほとんど自分でコードを書いていません。 テックリードの仕事は「実装」から「設計とレビュー」へ完全にシフトしています。特にレビューに関しては、Gemini Code Assistの指摘精度が非常に高く、実装を担当するClaudeと議論させることで、人間が細部まで目を光らせる必要はかなり減ってきました。
私が用意したのは、AIという優秀なパートナーが迷子にならず、違法建築を建てないための「地図(ドキュメント構造)」と「ルール(設計原則)」だけです。 この仕組みを共通サブモジュールとして展開することで、チーム全体がVibe Codingを実践し、変更容易性を維持できる環境が整いつつあります。
私たちが実践してきた「ドキュメント(仕様)を中心に据える」アプローチは、最近の研究やトレンドでも「Spec-Driven Development(仕様駆動開発)」や「Executable Specifications(実行可能な仕様書)」として、その有効性が議論されつつあります。 半年前は手探りでしたが、今の視点で見れば、これはAI時代のソフトウェアエンジニアリングとして一つの解であったと言えるかもしれません。