🧭 「Rails MPA は遅い」という前提は、本当に正しいのか
Web アーキテクチャの議論では、いつの間にか次の前提が常識のように扱われています。
MPA は遅い
SPA は軽くて速い
大規模になったら SPA に移行すべき
私はこの前提を、実運用と実測値の両面から何度も検証してきました。
そして今は、かなり明確な結論を持っています。
**少なくとも EC を含む多くのプロダクトでは、
Rails + Hotwire MPA は「遅いどころか、最初に選ぶべき構成」**です。
🧭 単純な Rails モノリスで学んだこと

Rails を使い始めた頃、私は特別な設計をしていませんでした。
- モノリス構成
- サーバーサイドレンダリング(SSR)
- Rails が用意する標準スタック
この構成で、最大 6 つの Rails サイトを同時に運用し、
そのうち 1 つは 60 万ユーザー規模まで成長しました。
- オートスケールなし
- フロントエンドフレームワークなし
- API 分離なし
イベント時にはアクセスが急増し、サーバー負荷は確かに上がります。
それでも私は AWS のメトリクスを眺めているだけで、サイトが壊れることはありませんでした。
この経験から学んだことは、今も変わりません。
高度なリアルタイム性が必須でない限り、
モノリス + SSR は「遅い」のではなく、最も安定した構成である
🧨 EC スケールが Web アーキテクチャの前提を壊す
しかし、EC では話が変わります。

現在私は、数百万ユーザー規模の EC サイトを運用しています。
このスケールに到達すると、Web アーキテクチャの前提が一気に変わります。
- SEO が売上に直結する
- トラフィックの大半は CDN 経由
- 価格・在庫は「準リアルタイム」が必須
問題は、この条件を 従来型の MPA でどう扱うかでした。
🧩 キャッシュという、EC 最大のジレンマ
EC では必ず次の矛盾に直面します。
| 要素 | キャッシュ |
|---|---|
| 商品名・画像・説明 | 可能 |
| 価格・在庫 | 不可 |
- 要件:Fastly などで 1 時間キャッシュしたい
- 現実:価格・在庫はキャッシュできない
すべてを 1 HTML に含めると、ページ全体がキャッシュ不能になります。
結果として何が起きるか。
- 全リクエストが Rails に到達
- DB が読み取りで枯渇
- 水平方向にスケールしない
❌ なぜ「クラシックな MPA」は破綻するのか
重要なのは、Rails が悪いのではないという点です。
問題は「同期ポーリング」という設計です。
def stock_check
product = Product.find(params[:id])
render json: { stock_count: product.inventory_count }
endコード自体に問題はありません。
しかし、負荷モデルが完全に破綻しています。
📉 フラッシュセール時の現実
- 同時閲覧ユーザー:10,000 人
- ポーリング間隔:2 秒
RPS = 10,000 ÷ 2 = 5,000 req/sec在庫確認だけで 毎秒 5,000 リクエスト。
HTML レンダリング以前に、DB の Read だけでシステムが窒息します。
🤔 「だから SPA にすべきでは?」という発想

ここで多くのエンジニアはこう考えます。
- App Shell は CDN キャッシュできる
- API はスケールしやすい
- クライアントで状態を持てる
この判断は、論理的には正しい。
実際、多くのチームが React SPA への移行を選びます。
しかし、ここで失われるものがあります。
🚀 SPA は何を解決し、何を失うのか
SPA は「操作中の体験」を改善します。
しかし、EC において最も重要な指標を犠牲にします。
📊 実測パフォーマンス比較(実運用)
前提条件
- トラフィックの約 70% がモバイル
- 低〜中価格帯 Android 端末を含む
- 本番相当データ量・通信条件
初期ロード性能
| 指標 | Rails + Hotwire | React SPA |
|---|---|---|
| JS サイズ(gzip) | 約 42 KB | 約 620 KB |
| JS パース時間 | 約 50 ms | 約 800 ms |
| FCP | 約 0.8 s | 約 1.8 s |
| TTI | 約 0.9 s | 約 3.5 s |
JavaScript のコストは ダウンロードだけではありません。
圧縮された 600KB の JS を、低〜中性能 CPU が解析・実行する間、
画面は 1 秒以上フリーズします。
📉 Core Web Vitals 実測(Google データ)
Google は Core Web Vitals を基準にウェブサイトを評価・ランキングしています。
以下は、p75/p95 の指標において、2 つのアーキテクチャ間で差が見られるポイントです。
| 指標 | Rails / Hotwire | React SPA |
|---|---|---|
| LCP | 🟢 0.8s – 1.5s | 🔴 2.5s – 4.0s |
| TBT | 🟢 < 100ms | 🟡 300ms+ |
| CLS | 🟢 0.01 | 🟡 0.1 – 0.25 |
特に TBT は、Hydration がメインスレッドを占有することが原因です。
⚡ 操作応答性(INP)の真実
- SPA:画面遷移は < 50ms
- Hotwire:80〜150ms
ただし Turbo Drive の プレビューキャッシュにより、
体感速度は SPA にかなり近づきます。
🧠 SPA が抱える構造的な負債
1️⃣ メモリリークと長時間利用問題
SPA はページ遷移してもブラウザがリセットされません。
- イベントリスナーの積み上がり
- 数時間後にブラウザが重くなる
- 原因特定が困難
Hotwire はページ遷移ごとに <body> が再生成されるため、
この問題が起きにくい構造です。
2️⃣ SEO とレンダリングキュー
Google は JavaScript を実行できますが、
HTML に比べ 数倍のクロールコストがかかります。
結果として:
- 新商品ページのインデックスが遅れる
- SEO 上の不利が発生
SSR を導入すると、今度は Node.js 運用コストが増え、
Rails の単純さが失われます。
サンドイッチ構造
ブラウザ <--> [ 🟢 Node.js (Next.js) ] <--> [ 💎 Rails (API) ] <--> DB3️⃣ ロジックの二重管理
- フロント:UI 用バリデーション
- バックエンド:保存用バリデーション
ズレた瞬間に、
「画面では OK なのに保存できない」
Rails + Hotwire では、Model に集約できます。
🧪 ケーススタディ
- GitHub:HTML-over-the-wire
- Twitter(X):React SPA
両者は思想が真逆です。
GitHub は「初期表示」を、Twitter は「操作感」を最優先しています。

🧮 私の技術選定基準
https://digi-archive.com/uploads/d/7400f04b-1e6f-4b54-91ec-521a3587894e/3826a35a-8a7b-42d4-8dd1-fd8a40df3469/pc-79f072a7400aeb689cfd70d92437730e.webp
| 条件 | Rails + Hotwire | SPA |
|---|---|---|
| 最重要指標 | SEO / 初期表示 | 操作感 |
| チーム規模 | 小〜中 | 大 |
| ロジック | 複雑 | 単純 |
🏁 結論

Rails + Hotwire は、
- SPA の 8 割の体験
- 2 割の複雑さ
- SEO と初期表示を維持
まず選ぶべきは Rails MPA です。
❓ 最後に残る問い
私たちはこう思いがちです。
SPA は軽い。JSON だけをやり取りすればいい
しかし実際には:
- HTML は そのまま描画できる
- JSON は 解釈・構築・描画が必要
100KB の HTML が 200ms で表示され、
100KB の JSON が 1.2 秒かかることは、珍しくありません。
私たちは本当に「軽量な通信」をしているのか
それとも、
ブラウザが得意だった仕事を
JavaScript にやり直させているだけなのか
