実プロジェクトでのテスト駆動型AI開発のススメ (Gemini 2.5 Pro Preview 05-06)

「AIなんてプロトタイプ止まりっしょ？」

「実プロジェクトの保守なめんなよ」

「テトリスwww」

ぶっちゃけAIコーディングについては、僕も最初は懐疑的でした。まだまだ実用的じゃないよな〜なんて。



ただ、AIが特定の狭い範囲、例えば単発のSQL生成みたいなタスクだと、人間を遥かに上回るのも事実なんですよね。
「コンテキストを上手く渡せれば、実プロジェクトでもイケるんじゃね…？」と漠然と思ってました。

この記事では、**Gemini 2.5 Pro Preview 05-06** と **Roo Code** を使い、実プロジェクトの既存機能を壊さずに9割がたAIで開発した手法をお話しします。

先に感想をちょい出しすると。**「テスト環境さえあればどんどん勝手に実装するじゃんすげえ」** という驚きがありました。

AIコーディングなんて、ノンエンジニアが騒いでるだけでしょ？ と思ってるエンジニアに届け！

### AI駆動開発の進め方

### 今回の相棒紹介

今回の開発で使ったのは下記。

*   **Gemini 2.5 Pro Preview 05-06**
*   **Roo Code**

手をいれるプロジェクトのフレームワークはDjango+Django Rest Frameworkです。

### 開発フロー

#### 1. (超重要)準備：正常系テストを1つだけ書く

なにはともあれ、**最初に人間が「正常系テストを1つだけ書く」**。これが一番大事。

例えばこんな感じのテストコードを用意しました。

```python
class TestXXXCSVImport(TransactionTestCase):
    """
    XXXのCSV取り込みのテスト
    """

    databases = "__all__"

    fixtures = [
        ...
    ]

    maxDiff = None

    def test_XXX_update_or_create(self):
        """
        XXXの新規登録・更新のテスト
        """

        # テスト用ファイルを指定
        file_path: str = "xxx.csv"

        # CSVインポート処理呼び出し
        なんとかCSVImporter(file_path).execute()的な

        # XXXが更新されていること
        updated_xxx_master = XXXMaster.objects.filter(
            id=1,
            xxx_group__name="グループA",
            xxx__code="1000",
        ).first()
        self.assertEqual(True, updated_xxx_master is not None)
        self.assertEqual(308, updated_xxx_master.value)

        # 新規XXXが登録されていること
        self.assertEqual(
            True,
            XXXMaster.objects.filter(
                user_id=1,
                xxx_group__name="グループB",
                xxx__code="1000",
                value=30.8,
            ).exists(),
        )
```

これをAIが見れば、

*   入力と期待値が正確に分かる
*   処理のエントリーポイントが分かる

って感じで、AIにとってめちゃくちゃ情報量が多いんです。
さらに、後で説明する「テスト実行方法」も教えることで、AIが自律的にテストを回して実装を進めるようになります。ほぼ人間。

#### 2. 設計：アーキテクトモードで基本設計書を作らせる
次に、RooCodeを「アーキテクトモード」に切り替えて、基本設計書をMarkdown形式で作らせます。AIへのインプットは主にこんな感じ。

*   正常系テストコードのファイルパス
*   **(重要)** テスト実行方法
*   開発したい機能の要求仕様。例えば下記のような情報を渡します。
    *   機能の概要、目的、背景
    *   主要な入力（データ形式、サンプル値）と、期待される出力（データ形式、サンプル値）
    *   関連するデータモデルやDBスキーマの情報（テーブル定義、フィールド定義など）
    *   「たぶんここをイジればイケるはず」みたいな、開発者からのヒント
*   **(重要)** 適用すべき既存のコーディング規約

地味に超重要なのが、この「コーディング規約」。
あらかじめこれをインプットしておかないとグチャグチャにされます。

例えば、Django Rest FrameworkのSerializerはすぐカオスるので下記に従うよう厳命します。

```
# 開発ルール

## Serializer内

Serializer内で新規実装する場合は、以下のように実装すること

- validationは値の妥当性確認のみに留める。ビジネスロジックにかかるものはドメインサービス層で記述し、ドメインサービス層で明示的にErrorをraiseする
- この層では直接Djangoのモデルを操作しない
- ドメインサービス `domain/services/<ドメイン名>/*` とドメインモデル `domain/models/<ドメイン名>/*`を使って組み立てる
- Serializerはドメインサービス層に、値を渡すだけ
- Serializerはドメインサービス層から受けた値を適切に整形し、レスポンスを返すだけ
```

#### 3. レビュー＆修正：人間が基本設計書をブラッシュアップ
AIが作った基本設計書を人間がレビュー。AIの解釈がズレてたり、考慮漏れがあったら、ここで人間が手を入れて、AIと話し合いながらより精度の高い設計書に仕上げます。

#### 4. 実装：Codeモードで実装させる
FIXした基本設計書と、例の正常系テストを元に、Geminiに実際のコードを書かせます。RooCodeは「Codeモード」で。

#### 5. 対話的コーディング：逐一、細かい要求を出す
一度に全部を作らせようとせず、こんな感じで対話的に進めるのがコツ。

「この部分の処理、もうちょいこうして欲しいんだけど」
「この変数、変えてもらっていい？」
「ここの命名、イマイチな気がするんだけど、どう思う？」
「ここ、Serviceクラスに切り出してくれる？ ディレクトリはここでお願い」

#### 6. 意図の伝達：困ったら、人間がちょっとだけ書く
AIがどうにも明後日の方向に進み始めたら、人間が少しだけ正しいコードを書いて、「この方針で続きを頼む！」と具体的なコード例で伝えます。コメントだけ書いてCopilotのサジェストを待つ、みたいなノリ。

#### 7. 検証：テストを実行させる（勝手にやるけど）
テストを「実行させる」と書いたけど、実装が終わったタイミングでAIが勝手にテストを回し始めます。これは、設計書作成時にテストコードのパスと実行方法を教えてるから。

1点だけ注意。テスト実行コマンドは、**終了コードがちゃんと分かるもの**を渡すこと。「exit 0」とか「exit 1」とかのアレです。

例えばDocker環境なら、こんなコマンドを教えました。

```
docker compose run app pytest -s -p no:warnings <ファイル名>
```

これが常に「exit 0」で返るようなコマンドだと、テストがコケててもAIは「成功！」と勘違いしちゃうんで、マジで注意です。

#### 8. テスト拡充：準正常・異常系テストのリストアップ
正常系の実装に目処が立ったら、RooCodeをアーキテクトモードに戻して、「この機能で考えられる準正常系や異常系のテストケース、洗い出して」とお願い。

#### 9. テスト自動生成：AIにテストを書かせる
リストアップされたテストケースから必要なものを選んで、AIに実際のテストコードを書かせる。

#### 10. 調整：人間がテストを最終チェック
AIが作ったテストの中には、冗長なものや、ちょっと意図と違うものもあるんで、そこは人間が最終的にチェックして、削除したり調整したり。

#### 11. 完成！
全てのテストが通って、コードの品質もOKなら、晴れて完成です。

### 【最重要】AIが自律的に学ぶ「テスト実行環境」の構築

ここまでのステップで「AIにテストを実行させる」とサラッと書きましたが、これが今回の開発フローで**一番と言っていいほど重要なポイント。**
AI自身が「テスト結果を正しく認識できる」環境を作ることで、AIは人間みたいに自律的に実装と検証のサイクルを回し始めます。

実際、実装中にテストでコケると、AIが自分でデバッグログを仕込みながらテストを何回も実行して、勝手に修正する、なんて光景も見られました。人間がやってたら30分はかかりそうな作業を1分で終わらせてたりして、「あ、僕の(エンジニアとしての)失職も近いな…」とやや危機感を覚えましたw

### で、AIコーディングって結局どうなの？

**開発スピード：慣れれば速い。ただし…**

慣れればAIの方が速い。でも、コンテキストをうまく渡せないカオスコード部分は人間がやった方が速い場面も、正直あります。ただ、既存コードで真似できるものがある場合は、かなり強いですね。

何より、**テストコード作成は本当に爆速。** 既存テストの書き方を一瞬で学習して、似たようなテストを秒で量産。「今までテスト書くのに何時間かけてたんだっけ…？」と虚無感に襲われるレベルです。

**コードの質：最初の正常系テストとコーディング規約が命**

結局のところ、**最初に人間が書いた正常系テストの質が、AIが生成するコードの質にモロに直結します。**
お手本のテストが期待値をしっかり定義していて、何をどう検証したいかが明確なら、AIもそれに倣って質の高いコードを書く。

また、明確なコーディング規約、今回は軽量DDDみたいなことをやらせてますが、そういうのがある場合はちゃんと渡しておくのがミソ。

**AIとの付き合い方：「一度に全部やらせない」のが鉄則**

AIと上手くやっていくコツは、**「いっぺんに全部やらせようとしない」** こと。これに尽きますね。
人間相手だって、新人にいきなり「この機能ぜんぶよろしく！」なんて丸投げしたら、だいたい炎上するじゃないですか。AIもそれと同じ。

「まずこの部分の設計からお願い」
「OK、じゃあ次はここのロジックをこの方針で書いてみて」
「あー、そこちょっと違うから、こう修正してくれる？」

みたいに、タスクを細かく分解して、一つ一つ対話しながら進めていくのが、結局一番早いし、品質も安定します。

**要求の伝え方：Markdownファイルでコンテキストを共有する**

今回、要求仕様や設計メモの類は全部Markdownファイルで書いてたんですが、これが良かった。ファイルをもとにコンテキストを共有できるんで、AIの暴走を防ぐガードレールになる感じ。

これまで、チャットで雑に要求を伝えて、AIも人間も「？？？」ってなって爆死する、みたいなことが多かったんですが、設計書のような「拠り所」となるファイルがあると、それをベースに会話できます。

### まとめ：AIコーディング、懐疑論から一歩踏み出してみませんか？

AIを使ったリアルな開発フロー、少しは伝わりましたでしょうか。

ぶっちゃけ、AIコーディングって聞くと「どうせ大したことないんでしょ？」とか「AI驚き屋が騒いでるだけじゃん」みたいに思う気持ち、僕も最初はめちゃくちゃありました。
でも、実際にやってみると、**「あ、これ、やり方次第でマジで武器になるわ」** と本気で思いましたね。

今回の肝をまとめると、

1.  **最初に人間が「正常系テスト」を1個だけ書くこと。**
2.  **AIに「検証可能なテスト実行環境」を教えること。**
3.  設計書みたいな**ファイルをベースにAIと会話すること。**
4.  **一度に全部やらせないこと。**

この辺を意識するだけで、AIは「暴走しがちなポンコツで空気読めないエンジニア」から、「超優秀な開発アシスタント」に化けるポテンシャルを秘めてると思います。

もちろん、プロジェクトの特性やチームの状況によって、合う合わないは絶対あるはず。
でも、もしこの記事を読んで、「うちでも部分的に試せるかも…」とか「テスト書くの爆速になるなら、ちょっとアリかもな…」なんて、少しでも思ってもらえたなら、めちゃくちゃ嬉しいです。

この記事が、あなたのAIコーディングへの、最初の一歩を踏み出すきっかけになれば、ハッピーです！

ではまた！