AIで作ったアプリに本当のバックエンドは必要か? 追加する前に見極める方法

本当のバックエンドが必要になるのは、決済を処理する、APIキーやシークレットをブラウザの外に隠す、複数のユーザーが同じデータを同時に編集するときの唯一の正しい情報源になる——この3つの場合だけだ。

疑問を持ち始める瞬間

バックエンドとは、ブラウザ以外のどこかで動くコードにすぎない。課金やシークレットの保持など、ブラウザにはやらせるべきでないことをこなし、データベースとやり取りする。AIで作ったアプリの多くは、想像していた形とは違っていても、すでにこれを一部やっている。

アプリはちゃんと動いている。ユーザーは登録してくれている。機能も次々とリリースされている。そんなとき、じわじわとこんな不安が湧いてくる——「本当のバックエンド」があるべきなんじゃないか? みんなバックエンドの話をしている。ちゃんとしたアプリにはバックエンドがある。あなたのビルダーが用意したのはReactの中のTypeScriptのようなもので、それって……プロっぽくないんじゃないかと思い始める。

実のところ、その不安はたいてい的外れだ。バックエンドがやっていることに魔法は一つもなく、AIで作ったあなたのアプリはすでにそれをやっているかもしれない。もしやっていないとしても、バックエンドを追加したところで本当の問題——実際に壊れている何か——は直らない。

この記事は、その違いを見極めるための話だ。

バックエンドは本当は何のためにあるのか

バックエンドが存在する理由は、突き詰めれば3つしかない。お金を扱うこと、シークレットを安全に保つこと、そして複数人が同じデータを編集するときに唯一の正しい情報源として機能することだ。

お金を扱う。 アプリが決済を受け付けたりユーザーに課金したりするなら、決済プロバイダー側がバックエンドを_要求する_。ブラウザからシークレットAPIキーを使って直接Stripeを叩くことはできない(キーをクライアント側のコードに置くことになり、誰でも見られてしまう)。だから、キーを安全に保ち、ブラウザからのリクエストを受け取り、ユーザーの代わりにStripeとやり取りするサーバーが必要になる。それがバックエンドだ。凝ったものである必要はない——ほとんどのアプリではNodeの関数1つで十分だ——が、存在はしていなければならない。

シークレットを安全に保つ。 APIキー、データベースのパスワード、認証トークン——これらはブラウザに置いておくことができない。アプリを使う誰もが読めてしまうからだ。AIで作ったアプリが認証を必要とする外部サービスを呼び出す必要があるなら、ブラウザだけでは完結しない。アプリはバックエンドと通信し、バックエンドがキーを持っていて、外部サービスを呼び出す。これでシークレットはシークレットのままでいられる。

データの唯一の情報源になる。 2人のユーザーが同時にアプリを使っていて、どちらも同じデータを変更しようとしているなら、どちらの変更を採用するかを決める中央の権威が必要だ。ブラウザには審判役は務まらない——2つのブラウザは互いを見ることができない。だから「Aliceの名前変更を採用する。Bobの変更は30ミリ秒遅れて届いたので反映しない」と判断するサーバーが必要になる。そのサーバーがバックエンドだ。データベースの話が重要になるのはこのためだ——すべてのデータが実際に存在する場所が1つ必要なのだ。

このリストに入っていないものに注目してほしい。パフォーマンス、プロっぽさ、スケーラビリティ、「みんな持っているから」。これらは、必要のない複雑さを追加させようとする感情にすぎない。

本当にバックエンドが必要かどうかを見極めるには

本当に必要だと言える兆候は3つある。アプリが遅く、その原因をブラウザだけでは直しようがない場合、ユーザーから見えず中断もされない場所でコードを動かす必要がある場合、そして2人のユーザーが互いのデータを上書きし合っている場合だ。以下は、自分がどれに当てはまるか(あるいは当てはまらないか)を見極める方法だ。

「遅い」。 ユーザーから遅いという声が上がっているなら、原因はたいてい次の3つのどれかだ。ブラウザの処理が重すぎる(CPUがボトルネック、アルゴリズムが悪い、DOMをレンダリングしすぎている)、ネットワークが遅い(悲しいけれど本当のことだ)、あるいはデータベースが遅い(クエリが多すぎる、インデックスが適切でない——AIで作ったあなたのアプリは_すでに_データベースとやり取りしているはずで、たいていは良いものだ)。本当のバックエンドを追加してもブラウザ側のCPU処理は直らない。本当のバックエンドを追加してもネットワークの遅延は直らない(物理法則には逆らえない)。バックエンドはキャッシュや賢いクエリパターンでデータベースクエリを助け_られる_が、あなたのビルダーはおそらくすでにそこまで考えている。

実際にあった「遅さ」の話。あるTodoアプリはリストの読み込みが重かった。開発者は「本当のバックエンドが必要だ」と考えた。実際の問題は、最初の50件だけを読み込んで「もっと見る」ボタンを出すのではなく、毎回5,000件のTodoを全部読み込んでいたことだった。バックエンドには一切手を触れず、半日で直った。バックエンドは問題ではなかったのだ。

「ユーザーに見えないコードを動かしたい」。 これだけが実際に筋の通った理由であり、思っているより珍しいケースだ。例を挙げると、ユーザーの登録後にメールを送る(タブを閉じられてもそのコードは動いてほしい)、一晩かけてファイルを処理するバックグラウンドジョブを動かす、スケジュールに沿って外部APIを呼び出す、などだ。これらは正当な理由になる。どこかのサーバーで何かを動かす必要は確かにある。しかし、認証やルーティング、データベースを備えたフルセットのバックエンドである必要はない。スケジュールで動くか、Webhookから呼ばれる単一の「クラウド関数」で十分だ。バックエンド一式よりずっとシンプルにできる。

「複数のユーザーが同時に同じデータを変更していて、更新が失われている」。 これは本物の問題だ。「Aliceの編集が消えた」とか「2人が同じフォームを編集して、後から編集した人の変更が前の人のものを上書きしてしまった」といった状況が起きているなら、競合(コンテンション)の問題を抱えている。データベースによってこの扱いには得手不得手があり、AIビルダーの中にはデフォルトで不得手な方を使っているものもある。しかし、その修正が常にバックエンド一式を要するとは限らない——データベースを変える、ロックを追加する、あるいは楽観的並行性制御(「古いバージョン番号を保持しておき、更新を許可する前に照合する」という手法の気取った呼び名)を加えるだけで済むこともある。ビルダーに、データベースを切り替えられるか、バージョン管理を追加できるか聞いてみるといい。必要なのはバックエンドではなく、もっと賢いデータベース構成かもしれない。

バックエンドの問題に見えて、実はそうではないもの

バックエンドの問題だと誤解されがちだが実はそうではないものが3つある。JavaScriptが1か所にまとまっていること、独立したAPI層がないこと、そして具体的な問題を伴わない漠然としたセキュリティへの不安だ。

「コードがJavaScriptで、全部1か所にまとまっている」。 成功しているアプリの多くは、ブラウザで動くJavaScriptが本物のデータベース(Firebase、Supabase、MongoDB Atlasなど、あなたのビルダーが用意したもの)と直接やり取りしている。「本当のバックエンド」のサーバーは存在しない。それでちゃんと動いている。コードが1つの言語で1か所にまとまっているからといって、本物でないわけではない。JavaScriptはちゃんと機能する。

「独立したAPI層がない」。 ブラウザがデータベースと直接やり取りしている。多くの人が最初に感じるのは「これはおかしい、間にAPIがあるべきだ」という直感だ。しかし、そのAPIが文字通り「このテーブルからselectして返す」とか「このテーブルにinsertする」だけのものなら、中間層は何も付け加えていない。ただのオーバーヘッドだ。データベース自体がすでにAPIなのだから、可能なら直接呼び出せばいい。

「セキュリティが心配だ」。 AIで作られたアプリの多くには、理にかなったデフォルト設定が備わっている。パスワードはハッシュ化され、SQLインジェクションは(データベースライブラリによって)防がれ、シークレットはクライアントの外に置かれている。本当に心配なら、やるべきことは反射的にバックエンドを足すことではなく、ビルダーにこうした対策がなされているか確認することだ。作りの悪いバックエンドは、作りの良いフロントエンドよりも脆弱になり得る。

正直な判断フローチャート

当てずっぽうではなく、こうやって見極めよう。

  1. 今のアプリは、バックエンドなしで今やっていることができているか? イエスなら2へ。ノーなら、あなたはすでにバックエンドを持っている(か、これから作る必要がある)。そのまま進んでいい。(AIで作ったアプリにはすでにあるかもしれない。)

  2. 追加したいものは、ブラウザには原理的にできないことか? 課金? もちろんできない。メール送信? 無理。シークレットキーを使った外部API呼び出し? 無理。それ以外? たぶんできる。ブラウザでもできるけど遅いだけなら3へ。ブラウザに原理的にできないことなら、バックエンドが必要だ。

  3. 本当の問題を直せば、その遅さは消えるか? 読み込む量を減らす、キャッシュを賢くする、リクエストをまとめる、もっと良いデータベースを使う——こうした対策で解決するかもしれない。コツは、まず何が本当に遅いのかを突き止めることだ。分かりやすい対策をやり尽くしてから、初めてバックエンドの追加を検討する。バックエンドを足しても遅いアルゴリズムが直るわけではなく、ただ別のマシンに移動させるだけだからだ。

  4. バックエンドを追加したら、本当に問題は解決するか? これが落とし穴だ。「パフォーマンスを改善する」ためにバックエンドを追加した結果、レイテンシがかえって_悪化する_ことがある。ブラウザからバックエンドへのネットワーク呼び出しが発生し、そのバックエンドがさらにデータベースへネットワーク呼び出しをする——本来ならブラウザから1ホップで済んだはずのことをだ。まず測定し、追加はそのあとに。

必要なのはフルのバックエンドか、それともクラウド関数だけか

やりたいことが、数秒動いて止まる1つの関数の中に収まるなら、必要なのはフルのバックエンドではなくクラウド関数だ。以下は、それを見分けるための簡単なテストだ。

バックエンドにやらせたいことを考えてみよう。それを、呼び出されると数秒だけ動いて止まる、1つのJavaScript関数(せいぜい100行くらい)として書くところを想像してほしい。その箱の中に収まるだろうか?

  • 決済のWebhookを処理する? 収まる。
  • ウェルカムメールを送る? 収まる。
  • アップロード前にファイルを検証する? 収まる。
  • 毎晩のレポートを実行する? 収まる(まあ、スケジュールで呼び出す形にはなるが)。

答えがイエスなら、「本当のバックエンド」は必要ない。必要なのはクラウド関数だ。Vercel、AWS Lambda、Google Cloud Functions、何でもいい。その方が安く、シンプルで、サーバーの面倒を見続ける必要もない。

答えがノーなら——常時稼働していて、何千ものリクエストをさばき、複雑なビジネスロジックを持つものが必要なら——そのときは本当のバックエンドを検討していることになり、その話し合いの方がずっと重要になる。とはいえ正直なところ、AIで作るアプリではそれは稀なケースだ。「バックエンドの仕事」に見えるものの大半は、実は「このAPIを呼ぶ」とか「このデータを保存する」といったことにすぎず、それはビルダーがすでに面倒を見てくれているはずだ。

ビルダーに投げかけるべき本当の質問

何かを追加する前に、ビルダーに一つだけ質問してほしい。「今、実際に壊れていて、バックエンドが直してくれるものは何か?」と。

「決済を扱う必要がある」「シークレットキーを使ってAPIを呼ぶ必要がある」「データの競合が起きている」といった具体的な答えが返ってくるなら、それでいい。何に向かって作っているのかが分かっているということだ。

もし答えが「まあ、ちゃんとしたアプリにはバックエンドがあるものだから」なのだとしたら、それは理由ではなく感情だ。誰も共有していないアプリにユーザーアカウントを追加したくなったり、本当は3つしかないものに15テーブルのデータベーススキーマを組みたくなったりするのと同じ感情だ。それは、バックエンドという衣装をまとったスコープクリープの匂いだ。

成功している個人開発アプリの多くは、あなたが想像しているような意味での「本当のバックエンド」を持っていない。データベースはある(あなたのビルダーがすでに用意しているはずだ)。スケジュールで動く関数が1つか2つあるかもしれない。しかし実際に仕事をしているのはブラウザで動くコードで、データベースと直接やり取りし、中間層なしで機能をリリースしている。

あなたのアプリは、おそらく今のままで大丈夫だ。そうではないという不安は、たいてい真実の声ではなく野心の声だ。バックエンドを追加するのは、それが本当の問題を解決するときにすべきであって、「そうすべきな気がするから」ではない。


次に機能を考えるときは、こう自問してみてほしい——これはブラウザに原理的にできないことなのか? それとも、その言葉を聞きすぎたせいで「バックエンドが必要な気がする」だけなのか? この2つの問いへの答えはまったく別物であり、あなたが取り組むべきなのはそのうちの一方だけだ。