メインコンテンツへスキップ

一晩で作ったwebアプリが、その日のうちに使われた話

100人規模の経営者会の席決めを支援するWebアプリを、Claude Codeと一晩で作った。そして翌朝には、その現場で使われはじめた。今日は、その一日の記録を残しておきたい。

その夜、突然始まった

ある経営者会の集まりで、100人以上の参加者をどう席に配置するか、毎回半日以上かけて手作業で頭を悩ませているという話しを聞いた。テーブルごとの進行役、参加者どうしの属性のかけ合わせ、過去に同じ卓になった人との重なり回避、席替えでの動き方。制約が多くて、手で組み立てるのはなかなかしんどい。

経営者会の運営をしている方からお話を聞いたのが夜。使いたい日は目前。動くものを見せるとしたら、その夜しかなかった。「Claude Codeでなんとかできないかな?」と直感して、パソコンに向かった。

一晩の工程

Claude Codeと一緒に、ざっくり以下の順番で進めていった。企画から共有Web化まで一気通貫、というやつだ。

Step 1
企画整理
Step 2
UIモック
Step 3
席割りエンジン
Step 4
データ層
Step 5
共有Web化

要件をテキストエディタにざっと書き出したあと、「席割りのアルゴリズムから作ろう」とClaudeに頼んで、まずはロジック部分から組んでもらった。フレームワーク非依存の純粋なTypeScriptで、UIから切り離して独立したモジュールにする。こうしておくと、テストもしやすいし、あとで手直しするときに他への影響が少ない。

※「フレームワーク非依存の純粋なTypeScript」とは、Next.jsやReactといった枠組みに寄りかからず、素のTypeScriptだけで動く形にしておくこと。料理で言えば、盛りつけの器(フレームワーク)から中身のレシピ(ロジック)を切り離しておく感じです。

うかがたt話しでは制約は複数あった。テーブルごとに進行役を1人配置する、初対面の組み合わせを増やす、定員を守る、2回目の席替えでは進行役は据え置き。全部を厳密に満たそうとすると解けない場面もあるので、優先順位を決めて「まずこれは絶対」「次にこれを最大化」というふうに層を重ねた。

100人分の席割りを1秒以内で返す、というのを目標に置いたら、Claudeが「じゃあこういう探索の絞り方でやりましょう」と組み立ててくれた。試しに動かしたら400ミリ秒ほどで返ってきた。ここまでで深夜0時前後。

つまずき①:ビルドが動かなくなる

途中で1回、開発サーバーを立ち上げたまま裏で npm run build を走らせたら、両方が同じ出力ディレクトリを触りに行ってしまって、先に進めなくなった。「動いてる方を止めて、掃除してから、もう一度どちらかを走らせる」という当たり前のことに気づくまでに、10分ほど遠回りをした。

あとで思い返すと、これは今後もやりそうな回り道だ。開発サーバーと本番ビルドは同じ土俵に立たせない、というのを頭の片隅に貼っておこうと思った。

つまずき②:問題なく動いている予定だったけど、独立QAが4件見つけた

ロジックが動いたので、「動作確認したら全部通ったよ」とClaudeが報告してきた。自分でも試したら、たしかに緑(テストが通っている状態)だった。ここで満足して次に進みそうになったが、いつもの手順で独立QAをかけてもらうことにした。

※「独立QA」とは、コードを書いた本人とは別の視点で品質を確認する役回りのこと。作った人が「動く」と思ったものを、外の目でもう一度たしかめる仕組みです。npcの司令塔まわりでは、書き手のエージェントと、検証専任のエージェントを分けて回しています。

作成者の自己検証
全部通った。テストも通ったし、画面上でも席が並んで見えた。このまま出せそうな空気だった。
独立QA
重大な問題を4件発見。うち3件は、コード上は通るのに、実際の動きでは席が壊れる、または最適化が途中で止まる、というものだった。

独立QAが挙げてきた4件は、こんな内容だった。

検証関数が本流から呼ばれていない:「席割りの妥当性チェック」の関数は書かれていたが、実際の席割り経路からは通っていなかった。書いたけど使ってなかったやつ。
定員オーバーがそのまま通る:卓に定員以上を詰め込んでも、エラーにならず素通りしていた。少人数のときは表面化しにくい。
NaN混入で最適化が止まる:スコア計算にNaNが混ざると比較が全部falseになり、より良い解を探し続けているつもりが実際には動けていなかった。
少人数のときにクラッシュ:想定より少ない人数を入れると内部の配列が空になり、その先の処理が破綻していた。

どれも、コードを書いた本人(Claude)が「動きました」と言った状態で残っていたもの。正常に動作しているという認識だった。だけど本番で使いはじめたら、たぶんどこかで気づかないうちに席が壊れていて、「あれ、なんで定員オーバーしてる?」という現場でしか出会えない静かな不具合になっていたと思う。

この4件をClaudeに戻して修正してもらってから、もう一度独立QAをかけた。今度は本当に全部通った。ここに来て、ようやく本番に出せる状態になった。

共有Web化と、翌朝

その後、参加者データを扱えるようにデータ層を整えて、CSVから名簿を取り込めるようにして、招待制のログインを載せて、共有できるWebアプリの形にした。データベースにはFirebaseのFirestoreを、認証にはGoogleログインを組み合わせた。「関係者だけがアクセスできる」という条件を、URLを渡すだけで満たせるようにしたかったからだ。

Googleログインの動作確認が終わったのが、明け方の3時半ごろ。試しに関係者の方にURLを送ってみて、翌朝には向こうから「見えたよ」の連絡が来た。そこから、実データを流し込んで、席を組んで、当日のたたき台として使いはじめてもらった。

作ったその日のうちに使われる、というのは、個人開発をやっていて一番うれしい瞬間だった。用途がハッキリしている道具は、動きはじめる速度が違う。

気づいたこと

今回の一晩で、あらためて実感したことがいくつかある。

ロジックとUIを最初から分けておく:ロジック単体で動く形にしておいたおかげで、UIを差し替えても、独立QAを回しても、テストを書いても、影響が閉じていた。急ぎの時ほど分けておくと後が楽。
作った本人の「動きました」は鵜呑みにしない:これはClaudeでも人間でも同じで、自分が書いたコードを自分で確認すると、どうしても緑に見えがちだ。別の目を通す工程を、面倒でも挟むと結果が違う。
用途がハッキリしていると、道具は速く育つ:「明日、あの現場で使う」というゴールが決まっていると、優先順位が自然と定まって、いらない機能を作らずに済む。抽象的な「あったら便利」は、逆に手が止まりやすい。

読者のみなさんへ

個人開発でAIと一緒に何かを組むとき、「動いた」を最終判定にしてしまうと、あとで静かな不具合が顔を出してくる。書いた本人と、確認する人は分けたほうがいい、というのは、AIと組むときも同じだった。忙しい時ほど省きたくなる工程だけれど、一晩でひととおり組む場面でこそ、独立QAをかける15分が全体を守ってくれる。

同じように「一晩で仕上げないといけない小さなツール」を作る人がいたら、ロジックとUIを最初から分けておくこと、そして書いた人と確認する人を分けること。この2つだけ頭の片隅にあると、少し楽になるかもしれない。

その後

本格的に動き出すことになり、僕も運営に入ることになった。
蓋を開けたてみたら、やることが盛りだくさん。まだまだ追加開発しないと、みんなの忙しさが落ち着かないようだ。頑張るべ!