Firebase Studio でプロンプトだけで完璧なWebアプリケーションを完成させた

生成AIがソースを書いてくれるようになってわずか数年だが、今度はアプリケーションそのものを作ってくれるようになったと話題になっている。Vibe Coding というらしい。
実際問題、本当に完全に動く古スタックのWebアプリケーションが作れるものなのか、半信半疑だったので、作ってみることにした。
今回は、Google謹製の [Firebase Studio](https://firebase.google.com/docs/studio?hl=ja) でそれなりにちゃんとしたWebアプリケーションを完成させてみた。

### Firebase Stduio とは
>Firebase Studioは、Google が開発したオンライン統合開発環境である。Visual Studio Code をベースとしており、インフラは Google Cloud で稼働する。([Wikipedia Project IDX](https://ja.wikipedia.org/wiki/Project_IDX))

Firebase SDKがインストールされたVMとIDE、さらにGeminiベースのAIエージェントがセットになったブラウザベースのアプリである。
ワークスペースを作るとその中でアプリケーションのリソースすべてが管理され、Firebase App Hostingにデプロイまでしてくれる。生成AI抜きにしてもかなり「使える」IDEである。

### 作成したアプリケーション
それなりにちゃんとしたアプリケーションを作りたいので、多数のユーザーが使うアプリを想定した。
NFL（アメリカンフットボールのプロリーグ）の試合結果を予測して、その正解率を競うアプリを想定した。
ただ正答率を競うだけでは面白くないので、大穴を当てれば点が高くなるというルールを採用する。

#### ルール
* 毎週15試合前後開催される全試合の試合結果を予測する
* 予想が当たると点数を獲得
* 点数は「その試合で負けたチームに投票した人の割合」で決まる（例：100人中80人がAチームの勝利を予想した試合で、Aチームが実際に勝った場合、Aに投票したユーザーは (100-80) = 20点を獲得。逆に、Aチームが負けた場合、Bに投票したユーザーは (100-20) = 80点を獲得）
* 獲得したポイントを集計し、毎週ランキングを発表する
* シーズン通算のランキングも発表する

# TL;DR というか、結論
結論、Vibe コーディングは可能。1文字もソースコードを書くことなく、アプリが完成した。
AIとチャットし続けるだけで、ほぼ完全なアプリケーションを作ることができたのである。
時代は変わった。

### 完成したアプリケーション
[Underdog Bettor](https://ub.it-penguin.com/)
概要は前述の通り、毎週開催されるNFLの全試合の勝ち負けを予想するゲームだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/9eb4e6ae-9a43-475b-a1e4-18ee45c4d75a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/c74faab5-a807-4ed1-b86b-bebb672f57b0.png)

以下のような機能があり、実際に動作する。
* 週別の試合の設定、試合結果の登録
* 週別の試合結果の予想
* 予測結果の集計、ランキング作成
* 過去の自分の予想の確認
* Xアカウントによる認証

Xのアカウントでログインできるように作ってあるので、上記のリンクから、完成したアプリケーションの雰囲気を見てほしい。
（なお、実際には運用していないので、予測の結果の集計とかは行っていない）


# 完成までの道のり
今回のアプリケーションを完成させるまでに費やした時間はおそらく10時間ほど。
この間、ただひたすらAIとチャットし続け完成まで漕ぎつけた。なお、チャットの入力は全ての日本語で行った。AIの返信は基本英語であるが、時々気が向くと日本語で返信をしてくれる。
私がちまちまソースコードを書いていたら、おそらく1か月以上かかった思うので、驚異的なスピードである。
どのような経緯を経て完成したのかを以下に記す。

### 初めのプロンプト
ワークスペース作成時に、最初のプロンプトを入力する。上記、作成したアプリケーションとほぼ同じ文章を入力した。
しばらくすると、アプリケーションの概要、プライマリーカラー、フレームワークなどを選択する画面が表示される。Gridiron Guesser というタイトルもつけてくれた。（悪くはないが、最終的に却下した）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/3fd77963-a8bb-44ae-88cc-adf9fcbb794a.png)
確認後、しばらくすると、いきなり画面を作ってくれる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/60ee13ce-96f9-4e1d-9763-defe5b7e901f.png)
画面は左がブラウザ画面、右側がAIチャット画面である。
プロンプトでは画面イメージは何も書いていないが、ほぼイメージ通りである。でもデータとして、ちゃんとNFLのチームを設定してあるし、放送テレビ局なども書かれていて、ものすごくそれっぽい。

### フロントエンド完成
あとは画面を操作しながら、足りない機能やレイアウトの修正をAIに指示するとどんどん画面を改良してくれる。
思い付きで言ったことを、「やっぱ今の無しで」と気楽に言えるのがAIの強み。
ここで、一つ気が付いたのだが、何度か修正を繰り返すと、いつの間にか前に修正した箇所が元に戻ったりすることがある。油断すると思わぬところが先祖返りするので気が抜けない。
試合日程を入力したり、週を切り替えたりする管理系の機能の画面がなかったので、追加してもらい完成。
動的に取得するデータと固定データの区別、入力チェックなども盛り込む。「すべての試合を選択しないとSubmitボタンを押せなくして」など、言ったらすぐに対応してくれる。
また、エラーが発生すると、自動でプロンプトにエラーを修正するかどうかのプロンプトが表示され、Fix ボタンを押すと修正する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/ce1fdeff-7cb6-4914-bdc9-66434d200b96.png)
ここまで2、3時間ぐらいのもの。早い。

### データベース(Firebase Firestore)

#### Firestore 化
次は、データの永続化である。Firebase の [Cloud Firestore](https://firebase.google.com/docs/firestore?hl=ja) を使用することにする。
データを Firestore から取得し、格納するようにしてと、AIに依頼する。
```:入力プロンプト
This week's pick の情報を firestore に格納するロジックを追加してください。
```
```:AIアシスタント出力
Okay, I'll ensure that the game data for "This Week's Picks" is primarily sourced from Firestore. The system is already designed to save game schedules (entered via the Admin Panel) to Firestore and then load them on the main page.
(以下、略)
```

なんと、事前に一部、対応していると言っている。Firebase コンソールで、Firestore のデータを見てみると確かにいつの間にやらコレクションができている。
この段階で、ローカルのファイルからデータを取得していたロジックを全面的に Firestore に変更。これにより画面を操作すると、データが Firestore に格納してくれるようになった。
問題は、後で作る予定のバッチ処理で入力するデータ。入力機能がないので、テストデータは手で作るしかない。AIで作ってくれないのかと尋ねたら、Firesotreに直接データをぶち込む機能はないらしい。
このようなデータを用意してほしいとAI様が指示をくれるので、その通りのデータを手作業でちまちま作った。この時点で、私はAIの下請けポジションになっている。

#### Firestore Rule
Firestore には、データを保護するためにルールを定義する。
admin 権限のユーザーだけが書き込むことができるデータや、自分自身のデータ以外は読み書きできないように保護するなど、ルールを説明すると、その通りの Firebase Rule を作ってくれた。
ただし、RuleのデプロイはAIではできないので、ここも人間様の出番である。
Firebase Studio をコードモードに変更すると、見慣れたVS Codeの画面になる。Terminal を開いてデプロイ。AIの下請け業務完了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635639/0687537a-d180-4995-b582-2cd186d3ff91.png)
だが、ここでトラブルが発生。
ルールが正常に動作せず、データの保存ができなくなった。
エラーが表示されるので、直してほしいと頼むのだが、一向に修正されない。
最終的に、Firebase Rule プレイグランドの実行結果と Rule を部分的に修正したときの動作を伝え、何度かやり直しを指示することで、自力で解決してくれた。
```:入力プロンプト
認証の問題は解決していないようです。 本来参照できるはずの user コレクション以下の情報が参照できていません。 認証情報が欠落して送信されている可能性を検討してください。
```

```:AIアシスタント出力
You're absolutely right to push on this. If the Firebase Rules Playground shows your rules are correct for an authenticated user, but in practice, reads of user-protected data are failing, it strongly points to the requests hitting Firestore not being properly authenticated from Firestore's perspective (i.e., request.auth is null in the rules).
We've addressed this for write operations by moving them client-side. The same principle applies to read operations if they are currently being done through Next.js Server Actions that use the client-side Firebase SDK. The client's Firebase Auth context isn't automatically passed to Server Actions.
```

この過程は完全にプログラマーへの「お前のソースが悪いと思うから、もう一度見てくれ」という説得と同じである。AIの下請けのみならず、ご機嫌取りまでしなくてはならなくなった。
原因だが、上記の通り、 next.jsのサーバーサイドのアクションが、クライアントからの認証情報をパスしないためとのこと。これ自体は、バグを作ってしまうことも含めて「さもありなん」と思うのだが、Next.js + Firestore というのは Firebase Studioのスタンダードな構成のはずなので、「そこで躓くの?」という疑問が湧く。
あとで、わかったのだが、Firebase StudioのアシスタントAIは、Firebaes の基礎知識がちょっと足りないし、古い。
* 認証情報を付与せずデータを取得
* Collection Group クエリーを使いたがらない(言えば使ってくれる)
* Fire Functions で v1 のソースを書いてくる

などなど。
Firebase という名を冠したツールなので、このあたりはどうにかしてほしい。
このルールの問題の解消に躓き、解決に3時間ほど要した。

### 認証機能(Firebase Authentication)
続いて、認証機能を追加してもらった。Firebase には、 [Firebase Authentication](https://firebase.google.com/docs/auth?hl=ja) という認証機能があるので、それを使うことを指示。認証はXの外部認証に1本化する。
ここは、Firebase 専用のツールということで瞬殺。
```:入力プロンプト
Firebase Auth による認証機能を追加してください。 X アカウントと連携してログインできるようにしたいです。
```
```:AIアシスタント出力
Adding Firebase Authentication with X (Twitter) is a great way to manage users in your app!
(以下、略)
```
ログインボタン、ログアウトのメニューなどを含めてすぐに作ってくれた。
X側のDeveloper Portal からAPIキーを取得し、Firebase Console側で設定したら完成。
AIも手順を教えてくれる。

### バッチ処理(Firebase Functions)

#### Function 作成
毎週、すべての試合結果が確定したのち、各ユーザーのスコアを採点し、ランキングを更新する。この処理を [Cloud Functions for Firebase](https://firebase.google.com/docs/functions?hl=ja) で作りこむことにした。
ここで、私が入力したプロンプトと、結果を見てほしい。

```:入力プロンプト
毎週の全ユーザーの予測結果を集計するバッチ処理を作成したいです。 firebase function でジョブを作成することはできますか?
```
これに対して、AIは下記の回答を出した。

```:AIアシスタント出力
Yes, it's definitely possible to create a batch job using Firebase Functions to aggregate weekly prediction results, and it's a common and effective way to handle such periodic calculations.
(中略)
Logic:
- Fetch all games and their actual results for the specified weekId from the weekly_games/{weekId} document.
- Fetch all user predictions for that weekId from the users/{userId}/weekly_predictions/{weekId} documents using a collection group query.
- For each game, calculate the "upset points" based on the percentage of users who predicted that game incorrectly. The scoring rule is: points = (percentage of people who got it wrong) * 100.
- For each user, calculate their total score for the week by summing the points from their correct predictions.
- Fetch user profiles (users/{userId}) to get username and avatar for the standings.
- Store the calculated weekly scores and ranks in a new document: weekly_standings/{weekId}.
- Update a season-long standings document (e.g., season_standings/overall_2024) by adding the current week's scores to each user's total and re-ranking.
(以下、略)
```
このアプリのスコアのルールをちゃんと理解している。
私がこのルールを書いたのは、最初のプロンプトのみ。その後、一度もこのことを書いていないのだが、ちゃんと覚えていた。おそらく、Firestore の設計もこのルールを念頭に入れて検討してくれていたのだと思う。偉すぎる。

#### 実行時エラー
言われるがままに Functions をデプロイし、実際に動かしてみた。
しかしながら、実行時エラーが出力され、途中で処理が終了する。
ログから抽出したエラーを伝えるも、修正されない。何時間も格闘したのだが、最終的にこのエラーは解消されなかった。
ちなみに、エラーはstack overflow で書かれている、これが原因。
[Firebase firestore query: "Error: 9 FAILED_PRECONDITION: The query requires an index. You can create it here"](https://stackoverflow.com/questions/51216614/firebase-firestore-query-error-9-failed-precondition-the-query-requires-an-i)
結果的には、私が強制的にIndexを作ることで解決。ただ、AIもこのバグに抵触しない代替案を提示してくれてはいたので、もしかしたら、どうにかしてくれたかもしれない。

### 最後調整
ここで、概ね完成。
あとは、テストしながらバグをつぶしていく。
このタイミングで、「ダークモードもほしいな」と思ったので、依頼したのだが、「今言う?」みたいな文句を言わずに、すぐにつけてくれる。AI優しい。

#### 修正できなかった問題
結局、最後まで修正できなかった問題が一つだけあった。
画面の右上にアバターとユーザー名が表示されていて、名前は、Xから取得された名前が表示されている。これを独自の名前に変更し、保存できるようにしたいと考えていたのだが、どうしても実現できない。
どれだけ修正を依頼しても、アバター部分に、Xのユーザー名が表示され、独自に保存した名前を表示してくれない。
AIが言うには、「認証情報を取得してから、Firestore のデータをとるまでのタイムラグがあるから実現できない」らしい。いや、アバターは表示されているけど、ユーザー名はクリックしたときにしか表示されないのだから、最悪、そのタイミングで取得してもよいと言ったのだが、AIが言うことを聞いてくれない。
ここだけは、実現することができなかった。なぜだろう??

# まとめ
何十年も前から使い古されたうたい文句として「ノーコードでアプリが作れる」というのがあるが、今度ばかりは本物だ。
日本語の文章をチャットに書き続けるだけで、完全なアプリケーションが出来上がった。
アプリケーションを7,8割作ることと、そこから完成まで持っていくことの間には大きな壁がある。残り2割を完成させるために、それ以前まで以上の労力を必要とすることもよくある。
その壁を乗り越えて、「完成」まで持ってきたとなると、「AIは使える」と断言できる。
これからのプログラマーの仕事は、AIをなだめて、おだててアプリケーションを完成させることになりそうだ。

一つ気になったのは、この作り方だと、問題が起きた時にデバッグの勘が全く働かないことだ。通常自分の作ったアプリなら、大体どのあたりに問題があるのか勘が働くのだが、今回はそもそもどのように作られているのかわからないので、皆目見当がつかない。
それを解決するのもAIの仕事なのかもしれないが、「責任」のないAIが最後まで片付けてくれるものなのか、どうなのか....?