非エンジニアのPMが、AIだけでMLB戦力分析アプリを作るまで #2

前回は、なぜアプリを作ろうと思ったのか、その全体像を書きました。

今回は、アプリ制作を進めるための企画・設計フェーズについて記載しています。

アプリで実際に何を表現したいのかをまとめていき(いわゆる要求定義~要件定義です)、どういう技術スタックを採用するのかという段階になります。

目次

指標はWARを採用するはずだったのに…

アプリの構想を始めたのは、2026年5月上旬です。

このときに書いていた要求内容は、現在、公開しているアプリとは異なります。

  • 30球団のチーム別WAR合計を毎日更新して表示する
  • 攻撃力(打撃WAR)、守備力(守備WAR)、投手力(投手WAR)の3軸レーダーチャート

データベースのテーブル名も、daily_war_snapshots と書いていて、完全にWAR前提に設計していたことがわかります。

元々、WARを選んだのは、それが個人の総合的な評価を表すのに最も浸透している指標だからです。

選手一人ひとりの貢献度が4.8といったような数字で表現され、その合計はチームの戦力に直結するのは、と考えていました。

事前にClaudeにも確認し、データ取得に使う予定だったpybaseballというPythonのライブラリについて、下記の調査結果も出ていました。

pybaseballは pip install pybaseball で入る Python ライブラリで、MLB Stats API と Baseball Reference からデータ取得できます。WAR含む各種指標が取れます。

WARで進めることは可能と判断し、そのまま設計を進めました。

開始直後3日で直面した方針転換

WARを自動取得できない

開発フローの第1週の作業として「pybaseballでデータ取得実験」という項目を入れていました。

本実装に入る前に、正しいデータが取得できるのかを確認しておこうと考えたわけです。

前述の結果もあったので、数値が取得できるかどうかではなく、正しい数値が出るかどうかを検証してみようというくらいの意図です。

「二刀流の大谷選手はどういう形で取得できるのかな? やっぱり例外処理になるかな」と先のことばかり考えていたのですが、問題が発生します。

WARを、安定して取得できませんでした…

WARの算出元となるFangraphsとBaseball Referenceから、データを自動で取りにいく仕組みが、当時は正常に機能していなかったのです。

この時の状況を、開発用のメモに書き残しています。

  • Fangraphs系の関数:403エラーで壊れている
  • Baseball Reference系の関数:list index out of range で壊れている

403エラーはアクセスそのものを拒否された時、list index out of range エラーは、データは返ってきたものの、想定した形になっていない時にそれぞれ表示されます。

原因は、pybaseballがFangraphsやBaseball Referenceからデータを取る際に使っているスクレイピングという方法です。

スクレイピングとは、Webサイトからプログラムなどを使って自動的に情報を集め、必要なデータだけを抜き出して整理する技術ですが、取得先のサイトが表示の仕方を変えたり、プログラムからのアクセスを制限したりすると、それだけで動かなくなります。

手作業でサイトを見にいけばWARの数字自体は分かりますが、日次で自動更新されるアプリを作りたいので、毎日30球団分の数字を手で入力するのでは意味がありません。

これにより、開始早々、開発方針を切り替えざるをえませんでした。

データベース設計の変更履歴を見ると、当時の動きが残っています。

  • v1.0(5月4日)初版作成
  • v1.1(5月4日)投手WARを先発/救援で分割、走塁WARを追加
  • v2.0(5月5日)WARからRun Valueへの切り替え

バイブコーディングによる企画・設計の難しさを体感

この調査結果を見た時、絶望とまではいかないまでもかなり落胆したことを覚えています。

WARをベースにするという設計は、前述の通り、筆者の思い込みではなくAIの調査を経た上で確定していました。

にもかかわらず、値の取得を試してみると、「できない」と言い出す。

pybaseballにWARを取得する機能があるのは事実で、AIが完全に嘘をついていたわけではないのですが、現時点で実際に取得できるかどうかについては、また別の話だったということです。

その後、WARの取得が困難であることが判明した時、AIは代案を出してはくれました。

ただ、その代案は、企画意図にそぐわない、負担を考慮しない人為的な作業を求める、勝手なロジックで発言の一貫性を保とうとする類のものが多く、筆者がこのアプリで表現したいこととはかけ離れていました。

とはいえ、質はともかくとして、何かに行き詰った時に必ず代案を出してくるAIは、文句だけ言って代案を出さない人間よりはよほど素晴らしいと妙に感心することもあります(そうプログラミングされているからですが)。

代わりに選んだRun Value

WARという指標が使えなくなったので、代替として考えた指標が、Statcastが提供するRun Valueです。

普段からデータ指標には触れていたのでRun Valueが使えるだろうという感覚はありました。

Run Valueは、ある選手のプレーがチームの得点にどれだけ貢献したかを、ラン(得点)に換算した指標です。

Run Valueを使えるという感覚があったものの、今回は失敗できないため、AIとともに前回より丁寧に調査、事実確認を行いました。

  • MLB公式のBaseball Savantが提供と出所が明確で算出方法も一つに定まっている
  • 加算可能で、選手個人の値を足し上げれば、チーム合計やポジション・役割別の合計として戦力の表現として意味を持ちうる
  • pybaseball経由で安定して取得できる(実際に取得実験をして確認)
  • MLB関連のデータやアナリティクスサイトを調べた結果、独自のスコアとして差別化になりうる

この結果から、Run Valueの指標利用に舵を切る判断ができました。

特に、「pybaseball経由で安定して取得できる」ことは、まさにWARで問題となった部分を解消手段であり、実際に毎日の自動更新も達成できています。

技術スタックの選定

指標が決まったので、次は技術です。

正直なところ、私に技術の良し悪しを判断する力はありません。
HTMLとCSSでサイトを作った経験はありますが、JavaScript、Python、TypeScript、SQLはオンラインで基本講座を学習した程度です。

そこで、構想を伝えて候補を出してもらい、選定理由を説明してもらった上で、自分で決めるという進め方をしました。

最終的な構成は下記です。

  • フロントエンド:Next.js + TypeScript + Tailwind CSS + Recharts
  • データ取得:Python + pybaseball
  • データベース:Supabase
  • ホスティング:Vercel
  • 日次自動更新:GitHub Actions

なぜこの構成にしたのか

今後、このアプリをどう拡大していくかは確定できていませんが、現時点の規模や管理する情報をふまえると、大掛かりなシステムやデータベースは必要ではなく、「安く」かつ「軽く」動けばよいと考えていました。

まずは動くものを作って、必要になったら課金すればよいという発想です。

このような基準とともに、自分の学習状況やこれまでの業務での経験もAIに伝えて相談したところ、次のような説明を受けました。

  • Next.jsは、Vercelに無料でデプロイでき、かつ、AIが書き慣れている
  • Tailwind CSSは、CSSを書く量が減る
  • Rechartsは、レーダーチャートや折れ線グラフが簡単に書ける
  • Supabaseは、無料枠が手厚く、Pythonからもフロントからもデータをやりとりしやすい

今思えば、別案も出させて比較検討しながら進めてもよかったかもとも思いますが、筆者が聞きなじみ技術で、かつ、こちらの意図にもあっていたので、この提案を受け入れることにしました。

まあ、自分がコードを書くわけではないので、もうAIが慣れている技術がいいかなという考えもあってね…

なお、当初の案にはバックエンドのAPIサーバーを立てる構成も含まれていましたが、これは早い段階で外れています。

データの取得はPythonスクリプトを自動実行し、画面側はデータベースから直接読めば事足りると思い、サーバーを一つ減らしました。

日次で動くPythonスクリプトが、球団ごとの合計まで計算し終えた状態でデータベースに書き込み、画面側はその完成した数字をそのまま読むだけなので不要です。

というAIの言葉を信じました。

ブログと同じドメインに置く

技術選定から少しずれるのですが、一応、Vercelに関わるところなので書いておきます。

アプリをどこに置くかという判断も必要で、選択肢は2つ。

  • 統合パターンtacticsonhomebase.site/app/ という形で、このブログと同じドメインに置く
  • 分離パターン:別のドメインを取得して、アプリを完全に独立させる

結果として、統合パターンを選んでいます。

分離すればアプリ単体のブランドは立てやすくなりますが、このブログとの相乗効果は薄れ、検索エンジンからの評価もドメインが分かれることで積み上げができません。

Vercelを使うことで、このブログとのドメイン接続も可能ということも調査の中で確認できました。

作らないものを先に決める

技術選定と同じくらい時間を使ったのが「作らないものを決める」作業ですが、このあたりは普段のPM業務とほぼ同じです。相談すべき開発者がAIという点を除けば。

企画ドキュメントに、MVP(最小機能版)に含めないものをリスト化しています。

  • ユーザー登録
  • お気に入り機能
  • 選手個別ページ
  • 過去シーズンとの比較
  • スマホアプリ化
  • コラム機能

こういうドキュメント管理はNotionが使いやすいですね。

データベース設計でも同じように、順位のような計算で求められる二次情報はデータベースに保存しないようにしています。

元データとの不整合が起きやすくなりますし、カラムも際限なく増えていきます。

加えて、過去シーズンのデータも持たず今シーズンに集中するなど、シンプルさを優先しています。

個人的な思想ではあるのですが、「あったらいいな」を全部入れると、いつまでも完成しません。
何を作るかを決めることは、何を作らないかを決めることでもありますね。

設計完了、いよいよ実装へ

紆余曲折を経て、ようやく、何をどう作っていくかを固めることが出来ました。

アプリを完成させて開発工程一連の振り返りができる今になると、AIを使って進める開発において、企画から実装への接続が最も難しいのではないかという感想を持っています。

開発者の頭の中にあるイメージの共有、そのイメージの実現のために必要なタスクと情報の確認、これを適切に実行ができるかが、開発の質を上げる最も大事なポイントだったということです。

記事内で述べましたが、今回のRun Valueのような別の角度からのアプローチをAIはしてくれません(少なくとも執筆時点では)。

AIというテクノロジーが日進月歩で発展していく中で、人間に求められるのは、AIとは異なる思考回路でよりよい着想を持てるかであり、その着想のためにどういうインプットを持つことが出来ていて、そのインプットに対してどういう解釈を持つことが出来ているかという点にあると感じています。

いよいよ、次回は実際に手を動かし始める段階の話です。

  • 非エンジニアが開発環境を整えるまでに、何につまずいたのか
  • Claude Chat、Claude Code、Codexの3つを、どう分担させたのか
  • AIへの指示文を、どうやって作っていたのか

ここから、開発が本格的に始まります。

よろしければシェアお願いします!
  • URLをコピーしました!
目次