2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

勢い(バイブコーディング)でアプリを作ったらAIの限界と仕様の大切さを知った話

2
Last updated at Posted at 2026-06-30

はじめに

Google AI StudioとGAS(Google Apps Script)を使って、
ボードゲームサークルの持参調整アプリを作りました。

きっかけはただのノリです。「バイブコーディングやってみたい!」というそれだけ。

2日間で動くものまでは作れたものの、かなり限界も感じたので過程と所感を書きます。

作ったアプリ

イベント状況一覧画面

image.png

ゲーム所有状況一覧画面

image.png

ゲーム・イベント登録画面

image.png


🛠️なぜGoogle AI StudioとGASだったのか

理由は単純で、とにかく早く動かしたかったからです。

  • サーバーを用意しなくていい(HtmlServiceで即公開可能)
  • AI Studioならプロンプトだけで画面もすぐ作れる

📝 行ったこと・工夫した点

最初はそもそもどうやってバイブコーディングを進めていくかという段階からGeminiを用いて、進めていきました。

Geminiに聞いた内容

ゲームサークル立ち上げていて、所有しているゲーム、日程調整、誰が何をもってくるかを管理できるようなシステムを作りたいです。
バイブコーディングを行う前の要件定義したいのですが、手伝ってくれますか?

ブラッシュアップの末出てきた要件定義

【目的】
ゲームサークルの運営を円滑にするため、Google Apps Script (GAS) を使ってスプレッドシートとSlackを連携するシステムを作りたいです。立ち上げたばかりなので、まずはシンプルな機能から実装します。
【スプレッドシートの構成】
1.「ゲーム管理」シート(列:ゲーム名、所有者、インストできる人、プレイ人数、プレイ時間)
2.「持参リスト」シート(列:開催日、持参ゲーム、持ってくる人、ステータス)
【実装したいGASの機能】
「持参リスト」シートに記載されている開催日が「明日」の場合、その日の夜に「明日持ってくるゲームと担当者一覧」をSlackの特定のチャンネルに通知するリマインド機能。
「ゲーム管理」シートに新しい行(ゲーム)が追加されたら、「新しいゲームが登録されました!」とSlackに通知する機能。
【AI(あなた)へのお願い】
まずは、1つ目の「明日持ってくるゲームのリマインド機能」を実装するための、GASのコード(Webhookを使ったSlack通知)を作成してください。解説は最小限で、コピペして動くコードを出力してください。SlackのWebhook URLはプロパティサービスから読み込む仕様にしてください。

これをそのままGoogle AI Studioへ入れてみましたが、
結果としては動くけど使いにくい画面が出来上がりました。

最初にできた画面

image.png

気づいたこと
単に開催日と持っていくゲームを入力できるだけで、
そもそも何のためのシステムかよくわからない。
目的や背景からしっかり整理しないとダメだと痛感しました。

そこでGeminiに要件定義をしっかりやらせてみることにしました。
これが後の「全体管理」役です。

こちらで要件定義自体はうまくいったものの、今度は技術的な実装で壁にぶつかりました。

ログインはできるのに、自分で登録したゲームの所有者が自分になっていないというバグが発生。

これを修正するため、別のGeminiスレッドを「テックリード役」として立て、
要件定義のブラッシュアップと技術的なアドバイスをもらいました。

最終的にできた要件定義

# 背景と目的
このシステムは、ボードゲームサークルのメンバーが「次回のイベントに各自がどのゲームを持参するか」をスマホやPCからスムーズに調整・登録するためのアプリです。
メンバーが手動でゲーム名を打ち込むと表記ゆれが起きるため、「サークルの全ゲーム(マスター所有一覧)」から検索してボタン一発で登録できる仕組みを目指しています。
サークルの立ち上げ期において、入力ハードルを極限まで下げ、当日の「誰がどのゲームを持ってきて、誰がインスト(ルール説明)できるのか、それがどんなゲームなのか」を明確にすることが目的です。

# 役割
あなたはGoogle Apps Script (GAS) および Webアプリケーション(HtmlService)開発において、バグのない堅牢なコードを生成する「超一流のテックリード」です。
以下の【要件定義】と【データ構造】を満たす、PC・スマホの両方で最適に表示される(レスポンシブ対応)Webアプリケーションの完全なソースコード(コード.gs、index.html)を生成してください。

# 開発における絶対ルール
- コードの省略(「// ここに処理を書く」「// 省略」など)は一切禁止します。コピペするだけでそのまま100%動作する完全なコードを出力してください。
- エラーハンドリング、ローディング制御、排他制御(LockService)を網羅的かつ厳格に実装してください。

# 要件定義
1. ユーザー認証と表示名登録(実行ユーザー前提)
   - アプリ起動時、Googleアカウントのメールアドレスを取得する(Session.getActiveUser().getEmail())。
   - 「ユーザーマスター」シートに該当メールアドレスがない場合、初回のみ「表示名(ニックネーム)登録画面」を表示し、登録を強制する。
   - 登録後は表示名を使用して画面表示を行う。
   - ※システム内部には、サークル共有ゲームの所有者として扱うための固定メールアドレス「shared@circle.com」があらかじめ存在する前提とする。

2. イベント管理(選択・表示・新規登録)
   - イベント選択:ログイン後、「イベント一覧」から直近のイベント(本日以降)を表示・選択できるようにする。
   - 日付のバグ対策:本日以降の判定は、タイムゾーンのバグを防ぐため、時・分・秒を切り捨てた「YYYY-MM-DD」の文字列、または純粋な日付のみで厳密に比較すること。
   - イベント新規登録(排他制御):アプリ内に「新しいイベントを開催する」フォームを設置。
     - 入力項目:イベント名、開催日(カレンダー選択)、場所。
     - 重要(重複対策):バックエンド処理では LockService.getScriptLock() を使用し、IDが重複しないよう自動採番(E001, E002...)して「イベント一覧」シートに1行追加すること。

3. ゲーム検索と持参登録(所有権・先着1名の排他制御)
   - 「マスター所有一覧」からキーワード、プレイ人数、プレイ時間、難易度、ジャンルでゲームを検索(またはフィルタリング)できる。
   - 重要(持参制限ロジック):検索結果のゲームカードにおいて、ログインユーザーがそのゲームを持参できるかどうかの判定を以下の通り行う。
     - 既に自分が持参登録済みの場合 ➔ 「持っていく!(登録済み)」と表示し、その横に「キャンセル」ボタンを表示。
     - 未登録かつ、ゲームの所有者が「自分(ログインユーザー)」または「shared@circle.com(サークル共有)」の場合 ➔ 「これを持っていく!」ボタンを表示。
     - 未登録かつ、ゲームの所有者が「自分以外の第三者」の場合 ➔ 持参ボタンは非表示(または無効化)とし、「[所有者の表示名]さんの所有物」とテキスト表示する。
   - 重要(先着1名の排他制御):ボタン押下時は LockService.getScriptLock() を使用し、同一イベント内で「すでにそのゲームIDが登録されていないか」をチェックする。サークル共有物も含め、先着1名のみが持参登録でき、既に登録されている場合は二重書き込みをブロックしてフロントにエラーを返すこと。
   - 登録成功・キャンセル時は非同期(画面リロードなし)で画面をリアルタイムに更新する。

4. 所有ゲームの新規登録機能(排他制御)
   - 「所有ゲームを新しく登録する」フォームを設置。
   - 入力項目:ゲーム名、インスト担当、プレイ人数、プレイ時間(分)、難易度(例:軽量級/中量級/重量級など)、ジャンル、および「所有区分(自分が所有 / サークル共有財産)」を選択させる。
   - 所有区分が「自分が所有」の場合はログインユーザーのメールアドレス、「サークル共有財産」の場合は「shared@circle.com」を所有者列に書き込む。
   - 重要(重複対策):バックエンド処理では LockService.getScriptLock() を使用し、ゲームID(例:G001, G002...)を自動採番して「マスター所有一覧」シートにデータを追加する。

5. キャンセル(取り消し)機能
   - 自分が持参登録したものにのみ「キャンセル」ボタンを表示し、バックエンドでも本人確認の上、非同期で削除を実行する。

# データ構造(1つのスプレッドシート内の4つのタブ)
1. 「マスター所有一覧」シート(ゲームID, ゲーム名, 所有者(メールアドレス), インスト担当, プレイ人数, プレイ時間, 難易度, ジャンル)
2. 「イベント一覧」シート(イベントID, 開催日, イベント名, 場所)
3. 「持参リスト」シート(イベントID, ゲームID, 持ってくる人(メールアドレス))
4. 「ユーザーマスター」シート(メールアドレス, 表示名)
   ※「shared@circle.com」の表示名は「サークル共有」としてマッピングしてください。

# 出力要件
- 「コード.gs」と「index.html」の2つのファイルに分けて出力。
- レスポンシブ対応:Tailwind CSS(CDN)を利用し、スマホでは縦1列、PCの大画面では横並びのグリッドや2カラム構成になる、ボードゲームカフェ風のモダンで美しいデザインにする。ゲームカードには、プレイ時間・難易度・ジャンルが視覚的にわかりやすく(タグ風など)表示されるようにしてください。
- 通信中はローディングスピナーを表示し、処理完了時はトースト通知(Success/Error)を出すUXを実装すること。

しかし、その後に発生した細かい修正を行う際、
「完成した数百行のフルソースコード」をそのままGeminiに丸ごと投げ、
そこで修正案を作らせて、またコードを戻す…という往復をやってしまいました。

これをやったあたりで、
Google AI Studio側でコードの膨大なトークン(コードの文字数)を消費し、
あっという間に1日の利用上限(レートリミット)に達してエラーが頻発するようになり、
ここで断念しました。

教訓
ソースコード自体をAI間で往復させるのはよくない
「日本語の指示書(プロンプト)」だけ往復させるべきでした。


🏁 所感:PoCにはいいが、本番運用にはまだ早い

「AIにコードを書かせる」こと自体はもう普通にできるようになりましたが、
コードが大規模化していくと、
AIの出力をコントロールし続けるのが難しくなると実感しました。

まだ「全部AIに任せればいい」段階ではなく、
人間がAIの周りの環境をどれだけ上手く設計できるかが鍵だと痛感しました。

今回の「仕様をしっかり固めないと破綻する」という学びを活かし、
今後は「仕様駆動開発」として、Claude CodeやKiroを用いて、
もう少しスマートな方法で挑戦してみたいですね。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?