# 初回本番26名開催で見つかった5つのバグ——「本番でしか分からない」の洗礼
2026年6月2日、ある支援機関の連合会での研修本番当日。参加者26名、オンライン・オフライン混在のハイブリッド開催。私はファシリテーターとして進行しながら、裏でコードを直し続けていた。
開発中に何十回とテストしても、「本番でしか分からない」バグというのが必ず存在する。この記事はその記録だ。
バグ1:採用3人以上でゲームが弾かれる(422エラー)
朝9時すぎ、最初の参加者から「仕入れが送信できない」という声が上がった。
確認するとHTTPステータス422。Unprocessable Entity——つまり入力値の検証エラーだ。
原因を追うと、APIスキーマの制約だった。
production: int = Field(0, ge=0, le=130)
procurement: int = Field(0, ge=0, le=130)
従業員1人あたり45個の生産能力があり、3人雇用すると135個まで生産できる。ところがAPIが130を上限として弾いていた。
修正内容:
production: int = Field(0, ge=0, le=225) # 5人×45個=225
procurement: int = Field(0, ge=0, le=225)
これは設計ミスとも言えるが、テスト環境で従業員を最大まで雇用してから生産量を上げるという操作を一度もやっていなかった。本番の参加者が教えてくれた。
バグ2:採用後もスライダーの上限が130のまま
バグ1を直しても、参加者からまた声が来た。
「仕入れを増やそうとしてもスライダーが130までしか動かない」
APIは修正したが、フロントエンドのスライダーUIが採用ボタンを押しても上限を更新していなかった。
// 修正前
function updateHireButton() {
// 採用後の処理はあったが、スライダーmax値の更新がなかった
}
// 修正後
function updateHireButton() {
const emp = state.employeeCount;
const maxProd = emp * 45;
document.getElementById('sl-prod').max = maxProd;
document.getElementById('sl-proc').max = maxProd;
document.getElementById('prod-max-label').textContent = maxProd;
}
バックエンドとフロントエンドの整合性は、単体テストでは見つかりにくい。両方を繋いで人間が実際に操作して初めて発覚する類のバグだ。
バグ3:AI相談タブが404
「AI相談ボタンを押すと何も表示されない」
当日の朝まで実装予定だったAI相談機能が、実は未実装のままデプロイされていた。フロントエンドには「AI相談」タブのボタンが存在するが、バックエンドのエンドポイントが存在しなかったのだ。
急遽、本番中にEC2にSSHしてエンドポイントを追加した。
@router.post("/games/{game_id}/companies/{company_id}/ai_consult")
async def ai_consult(game_id: uuid.UUID, company_id: uuid.UUID,
db: Session = Depends(get_db)):
# 現在の経営状態を取得してGPT-4o-miniに渡す
company = db.get(Company, company_id)
context = build_company_context(company)
response = call_openai(context)
return {"advice": response}
本番中にコードを書いてデプロイする経験は、エンジニアとして貴重だが二度とやりたくない。
バグ4:緊急融資モーダルが管理画面から開けない
管理者画面(ファシリテーター操作画面)から緊急融資を実行しようとすると、モーダルウィンドウが開かなかった。
原因は構造上の問題だった。緊急融資モーダルが管理者ダッシュボードの子要素として定義されており、CSSのoverflow:hiddenにより見切れていたのだ。
<!-- 修正前:ダッシュボード内に入れ子 -->
<div id="admin-dash" style="overflow:hidden">
<div id="emergency-loan-modal">...</div>
</div>
<!-- 修正後:body直前に移動 -->
<div id="emergency-loan-modal">...</div>
</body>
DOMの構造が見た目に影響する典型例。実際にモーダルを開く操作をしなければ気づかなかった。
バグ5:緊急融資完了後にalert()が出てブラウザが止まる
緊急融資自体は動くようになったが、完了後にalert()ダイアログが出てブラウザの操作が止まった。
// 修正前
alert('緊急融資を実行しました');
// 修正後
showToast('緊急融資を実行しました', 'success');
本番環境ではポップアップがUIを壊す。alert()は開発中のデバッグには使っても、本番には残してはいけない。
本番が最良のテスト環境
5つのバグを修正しながらの本番進行は、正直かなり慌ただしかった。それでも最終的にゲームは完走し、参加者満足度は4.7(5段階)だった。
本番でしか見つからないバグがある。それは「実際の人間が、実際の操作をする」という環境でしか再現しないからだ。
ステージング環境でどれだけテストを重ねても、人間の操作パターンは想定を超える。
この経験から私が学んだのは、「バグが出ることを前提に、修正できる体制で本番に臨む」という姿勢だ。EC2にSSHできる状態でファシリテーションする、という準備がなければ今回の本番は成立しなかった。
ゲーム開発は本番を重ねるたびに強くなる。 次回はこの5つは絶対に出ない。