AZASLAB
← Journal
8 min read 開発個人開発インフラクリシンクエスト

連載「クリシンクエストをつくる」· 第3編/全8編

「動く」から「届く」へ — 個人開発で本番環境をつくった話

手作りのサーバーから Vercel + Supabase へ。土台は入れ替えても、コードの“呼び方”は変えませんでした

自分のPCで動くのと、誰でも使えるのは別物です。クリシンクエストを手作りのサーバーから Vercel と Supabase へ引っ越した記録。サーバーレスの制約、データベースの方言、同期から非同期への書き換え——移行を支えた“互換アダプタ”という考え方を、開発の裏側からやさしくお話しします。

安宅春樹 (Ataka Haruki)

— AZASLAB代表 ・ AIエージェント開発ディレクター

— Shimizu Fumio Architects Co., Ltd. 取締役

「動く」と「届く」は、別物です

ここまで2回、嘘の設計答えの守り方を書いてきました。どちらも「中身」のお話でした。今回は土台——このゲームを、誰でも触れる場所にどう置いたか、のお話を一緒に見ていきましょう。

個人開発をしていると、ある段階で必ずこの壁にぶつかります。自分のPCでは完璧に動く。でも、それを「誰でも、いつでも使える」状態にするのは、まったく別の山なんですね。動くものをつくるのと、届くものにするのとでは、必要な手間がぜんぜん違う。あなたも、手元では動いていたものを公開しようとして、急に勝手が変わって戸惑ったこと、ありませんか。

出発点は、手作りの構成

最初のクリシンクエストは、思いきりシンプルな構成でした。

  • サーバーは、フレームワークを使わない素のNode.js(自分で1から書いたもの)
  • データベースは、SQLite——ファイル1個で完結する、軽量な仕組み
  • 画面は、素のHTMLとJavaScript

これは試作には最高です。速いし、軽いし、全部が手の内にある。仕組みを理解しながらつくるには、これ以上ない構成でした。

でも「本番」となると話が変わってきます。24時間落ちないこと、複数の人が同時に使えること、データが消えないこと、直したらすぐ反映できること——こうした“運用”の部分が、手作り構成では一気に重くなるんです。そして個人開発では、この運用こそが最大のボトルネックになりがちです。つくる時間より、保守する時間に押しつぶされてしまう。心当たり、ありませんか。

なぜ、引っ越しが必要だったのか

公開先に選んだのは Vercel というサービスなのですが、ここに最初の落とし穴がありました。

Vercel のような今どきのホスティングは、サーバーレスという仕組みで動きます。ざっくり言うと、「リクエストが来るたびに、使い捨ての小さな実行環境がパッと立ち上がり、仕事を終えると消える」という仕組みです。常に同じ1台が動き続けているわけではないんですね。

ここで SQLite が詰まってしまいます。SQLite は「サーバーのディスクにあるファイル1個」にデータを書く仕組みです。ところが、実行環境が毎回使い捨てだと、書き込んだファイルもろとも消えてしまう。これでは記録が残りませんよね。

つまり、必要なのは「どの実行環境からでも、ネットワーク越しにつなげる、ちゃんと残るデータベース」でした。それが Postgres(を提供する Supabase)だったんです。引っ越しは好みの問題ではなく、土台の都合だったんですね。

選んだ土台:Vercel と Supabase

そこで、自分で運用しない方向に舵を切りました。専門の会社が面倒を見てくれるサービス(マネージドサービス)に乗せる、という判断です。

  • Vercel:公開とホスティングを担います。コードを更新して送るだけで、自動で本番に反映されます。
  • Supabase:ログイン(認証)とデータベース(Postgres)を担います。アカウント管理やデータ保管を任せられます。

あわせて、バラバラだった構成も一本化しました。以前は「素のサーバー+別の画面配信」を内部でつなぎ合わせていたのですが、これをひとつのアプリ(Next.js)に統合し、30本あったサーバー側の処理もその中に畳み込みました。部品が減るほど、壊れる場所も減りますからね。

個人開発で「自分で全部やる」は、一見かっこいいですよね。でも、続かないんです。持たない勇気——運用を手放し、つくることに集中する。これが、結果的にいちばん効いた判断でした。

いちばんの山:データベースの引っ越し

移行で最大の難所が、データベースを SQLite から Postgres へ 移すことでした。

両者は同じ「SQL」を話すのですが、方言が違います。日付の書き方、数の数え方、使える関数の名前——細かいところが、いちいち食い違うんです。とはいえ、これまで書いてきた大量のコードを全部書き直すのは、現実的じゃありませんよね。

そこで取った作戦が、“互換アダプタ”でした。

古いコードがデータベースを呼ぶときの「呼び方」は、そのまま変えません。その裏側だけを、こっそりPostgres用に差し替える薄い層を一枚かませます。壁のスイッチにたとえるなら、スイッチの位置も押し方もこれまで通り。でも壁の裏では、配線も電源もそっくり別物に引き直している——そんな発想です。コードから見た「呼び方」が変わらないので、アプリ全体を作り直さずに、土台だけを入れ替えられました。

このアダプタが、裏で地味な変換をいくつもこなしてくれています。たとえば、SQLite と Postgres では「ここに値を差し込む」という記号の書き方が違うので、それを自動で書き換えます。返ってくる日付の形もそろえます。呼ぶ側からは何も変わって見えないのに、中ではきちんと翻訳が走っている——よくできた通訳のような層なんですね。

同期から非同期へ:型チェッカーに探させます

地味ですが大きかったのが、「同期」から「非同期」への切り替えでした。

SQLite は、結果がその場ですぐ返ってきます(同期)。一方、ネットワーク越しの Postgres は、「ちょっと待ってね」と一拍おいて返ってきます(非同期)。プログラム上は、待つべき場所すべてに「待つ」という印(await)を付けて回らないといけません。数百か所のうち、一つでも付け忘れると、データが届く前に処理が進んでしまってバグになります。

これを目視で探すのは、無理がありますよね。そこで、型チェッカー——コードの型の不一致を見つけてくれる道具——に「待ち忘れ」を機械的に洗い出させました。人間が勘で探すのではなく、道具に「ここが変だ」と指摘させる。退屈な全数チェックは、人より機械のほうがずっと得意なんです。

方言の段差を、ひとつずつ埋めます

それでも、細かな食い違いは手で埋めるしかありませんでした。たとえば——

  • 列の名前:Postgres は引用符で囲まないと名前を勝手に小文字化してしまいます。大文字混じりの名前が、知らぬ間に別物になってしまうんですね。
  • 件数の合計:合計や件数が、数ではなく「文字」として返ってくることがあります。そのまま計算すると、おかしな結果になってしまいます。
  • 関数名:「2つのうち大きいほうを返す」関数の名前が、そもそも違います。
  • 日付の表記:空っぽの日付の扱いや、日時の整形のしかたがそろいません。

どれも派手さはまるでありません。けれど、一つ取りこぼすと表示が崩れてしまいます。地味な段差を、つまずく前に一段ずつ埋めていく——移行の現実は、その繰り返しでした。

ローカルでも、本番と同じDBで

もうひとつ効いたのが、手元のPCでも本番とそっくりな環境を再現したことでした。

専用のツールを使って、自分のPCの中に本物の Postgres を立て、そこで開発します。本番に送る前に「同じ土俵」で試せるので、「ローカルでは動いたのに本番で壊れる」という、いちばん消耗する事故を減らせるんですね。データベースの変更も、思いつきで直接いじるのではなく、変更を1枚ずつのファイルとして記録して積み上げます。あとから「いつ何を変えたか」をたどれるようにしておくわけです。

テストという、命綱

これら全部を支えてくれたのが、テストでした。

「前と同じ入力で、前と同じ結果が返るか」を自動でチェックし続け、それが全部緑(成功)であることを確かめながら、土台を入れ替えていきます。すでに動いているシステムの土台を入れ替えるのは、本来とても怖い作業ですよね。それでも、命綱があれば踏み込めます。正しさを機械に見張らせるから、安心して大胆になれる。テストは、慎重さのためではなく、むしろ大胆さのための道具だったんだと思います。

認証は、自前でつくりません

最後にもう一つの判断です。ログインのしくみを、自分で作るのをやめました。

パスワードの保管は、事故が起きれば被害が大きく、正しくやるのは想像以上に難しいものです。ここは迷わず Supabase の認証に任せました。自分で抱えるほど危ないものは、専門家に預ける。これも「持たない」判断のひとつですね。(認証まわりの設計は、また別の回でくわしくお話しします。)

おわりに:手放すほど、軽くなります

個人開発というと「全部ひとりで作る」イメージがあるかもしれません。でも実際にやってみて学んだのは、逆だったんです。手放せるものを手放すほど、つくることに集中できる。運用はマネージドに、認証は専門サービスに、移行コストは互換アダプタに、退屈な確認は型チェッカーとテストに——そして実装そのものは、AIエージェントたちに。預けられるものを預けて、自分は「中身」だけに向き合う。

この移行も、手を動かしたのは私ではありません。「互換アダプタでいく」という方針を決めたのが私で、何百か所もの方言の書き換えやawaitの付け回しは、AIエージェントたちが手分けして進めてくれました。私の仕事は、どこをAIに任せ、どこは自分でテストして確かめるかの線引きをし続けること。コードを一行も書かなくても、土台は載せ替えられる——ディレクションとは、そういう仕事なんですね。もしあなたが個人開発で「全部ひとりで」と力んでいるなら、まず一つだけ手放してみるところから始めてみませんか。

こうしてクリシンクエストは、ようやく「動く」から「届く」へと一歩進みました。

——ところが、本番に出してすぐ、奇妙なことが起きたんです。なぜか、表示がやたら遅い。同じコード、同じデータベースなのに。次回は、その犯人を突き止めた話——「8秒が0.3秒になった」たった1行の設定について書きます。一緒に見ていきましょう。

— クリシンクエスト開発記・第3回