100点の「神話」を解体する:Remix + Vite が直面する PSI の実用上の上限と賢い撤退戦略

はじめに
Remix + Viteスタックでサーバー応答2msを達成しても、PSIモバイルではLCPが1秒を切れません。
PSIのSlow 4Gシミュレーション(RTT 150ms)とReactのランタイムコスト(約300〜360 KiB)が構造的な上限を作っており、実装で解決できない領域です。
サーバー応答は爆速なのになぜスコアが伸びないのか?
ここで言う「神話」とは、「説明なき信念」、あるいは「無条件の最適化信仰」のことです。
理想的なEdge環境を構築し、サーバー応答時間はわずか2msを達成した。
しかし、Lighthouse (PSI) のモバイル測定では、LCPが1秒を切ることがどうしてもできない。
微細な最適化を繰り返したが、ツールの指摘事項は「自分の実装」ではなく「フレームワークの根幹」に及び、
出口のない迷宮に迷い込んでしまった。
この記事をお勧めしない人
- 数値上の100点満点こそが唯一の正義だと信じている人
- 実際のユーザー体験よりも、測定ツールの判定を最優先したい人
- 技術的な制約を理解した上での「戦略的撤退」を敗北だと感じる人
もし一つでも当てはまらないなら、読み進める価値があるかもしれません。
終わりのない最適化という蟻地獄
- ☠ 解決不可能なスコアを追い求め、本質的ではないコードの削り込みにエンジニアの貴重な時間が溶け続ける。
- ☠ スコアを1点上げるためにアーキテクチャの柔軟性が損なわれ、新機能追加のたびにパフォーマンス劣化を恐れるようになる。
- ☠ ついに、開発チームが疲弊し、ビジネス価値の創出よりも「ツールの機嫌を取ること」が目的化して、プロダクトの成長が止まってしまう。
100点の呪縛から解放される未来
- この記事を読めば、測定ツールの内部仕様という「不可避の境界線」を知ることで、無意味な努力から解放されます。
- 具体的には、PSIのシミュレーション仕様やReactのランタイム構造から導き出された「論理的な上限値」を
目標に据える設計図を手に入れられます。 - この考え方は、実際にClaudeMixブログで100点を目指し、その果てに到達した限界を分析した結果に基づいています。
このブログもそうでした
筆者もこのブログで「完璧な100点」を目指していましたが、ある一点から先は実装ではなく「環境料」の問題であることに気づきました。
この記事で、数値に裏打ちされた健康的な妥協点と、本質的な開発にリソースを集中させるための基準を持ち帰れるように書きました。
【測定条件の前提】: 本記事の結論は、Lighthouse標準のモバイル相当(Slow 4G: RTT 150ms / Down 1.6Mbps / Up 750Kbps、mobile CPU想定)かつ JS総量300–360KiB級、測定3回・分散0という条件で再現された観測に基づきます。
PSIモバイル測定におけるネットワーク遅延の再現性
Lighthouse (PageSpeed Insights) のモバイル測定において、サーバー応答速度を極限まで高めても、ある一定の遅延からは逃れられないように見えます。これはなぜでしょうか。
工夫したこと:シミュレーション環境下での下限値の観測
PSIがエミュレートするモバイル環境(Moto G Power, 4G回線シミュレーション)で、TTFBが2msという理想的なサーバーから最小限のHTMLを配信する実験を行いました。
【測定環境と結果(中央値)】:
- 【ネットワークプロファイル】: Simulated Slow 4G (RTT 150ms / Down 1.6Mbps / Up 750Kbps)
- 【CPUボトルネック】: なし (TBT = 0ms)
- 【測定安定性】: 3回測定でスコア分散 0 (決定論的に近い挙動)
その結果、何度測定してもLCP(最大コンテンツの描画)が「約600ms」を下回ることはありませんでした。これはCPU処理待ちではなく、通信帯域とRTTの物理的な制約が支配的であることを示しています。
ぶつかった壁:スロットリングモデルという物理的制約
この「約600ms」は、PSIが固定で加算するペナルティではありません。Lighthouseは、RTT(往復遅延時間)、通信帯域、CPUスローダウンを組み合わせた「スロットリングモデル」で動作します。
つまりこの数値は、TCPハンドシェイクやSSLネゴシエーションといった、シミュレートされた低速回線上で発生する物理的な通信遅延の合計が、我々の測定条件下で再現性高く観測された「下限値」なのです。
この下限はLighthouseのシミュレーションモデルに依存するものであり、現実のネットワーク物理とは異なる「Googleの評価モデルの中での物理」です。
これを「実装の不備」と捉え、さらに100msを削ろうとすることは、シミュレーションの物理法則に逆らう試みでした。
解決方法:観測された下限値を「ベースライン」と捉える
この約600msを「特定条件下での再現性の高い下限値」として認識し、最適化の目標設定における「ベースライン」として扱うことにしました。これにより、我々が制御可能な実装部分(アセットサイズ、実行順序など)の改善に集中できるようになりました。
Reactの初期実行コスト(フレームワーク税)との向き合い方
Remixを使用している以上、Reactのランタイムをクライアントに送り、ハイドレーションを実行することは避けられません。これがLighthouseのスコアにどう影響するのでしょうか。
工夫したこと:ランタイム影響の数値化
インタラクティビティがほぼ不要な静的ページにおいて、React + React-DOM のバンドルが LCP や TBT に与える影響を数値化しました。Lighthouseは、ハイドレーション直後に使われないReactの内部コードを「未使用のJavaScript」として指摘します。
ぶつかった壁:「フレームワーク税」は固定費か、変動費か
実測データ(JS総量 330-360KiB、未使用転送量 約25KiB)において、Reactのvendorバンドルのうち「37-41%」が常に未使用と判定されました。これを「tree-shaking不可能なフレームワーク税」と断定するのは早計です。
実際には、この「税率」は可変であり、以下のようなアーキテクチャの工夫によって変動します。
- コード分割境界の最適化
- Islands Architectureのような遅延ハイドレーション
- React Server Components (RSC) の導入
- インタラクティビティが不要な領域のno-JS化
しかし、これらは現在のRemix + Viteの標準構成から大きく逸脱する可能性があり、相応の設計コストを伴います。
解決方法:現状のアーキテクチャにおける「費用対効果の限界」として受容
現在のアーキテクチャにおけるJavaScript実行コスト(0.1〜0.2秒相当)を「固定費」と断じるのではなく、「観測された構成において最小化しても残存したコスト(構造的傾向)」として受け入れることにしました。
これはLighthouseの測定モデルがJS実行コストを重く評価する側面も考慮した上での、戦略的な判断です。
5層CSSアーキテクチャのトレードオフ
堅牢な設計を目指して構築した CSS アーキテクチャも、PSIのモバイル環境では牙を剥くことがあります。
工夫したこと:レイヤー化CSSの並列取得測定
レイヤー化されたCSSファイルが、エッジ環境での並列取得時に LCP に与える影響を測定しました。
ぶつかった壁:4Gシミュレーションでの輻輳
4Gシミュレーション下では、ファイルの並列ダウンロード数が増えるほど、1ファイルあたりの通信完了が後ろに倒れます。
合計転送量が一定でも、分割数増加により完了時間が遅延します。ネットワークは「速さ」ではなく「分配」だからです。
私たちの 5層 CSS 構成は、デスクトップでは高速ですが、
PSIの極限環境では「918ms〜2,310ms」の LCP 遅延として跳ね返ってきました。
解決方法:アーキテクチャ維持とスコアのトレードオフ
「設計の美しさ」と「PSIのスコア」のトレードオフを検討した結果、アーキテクチャの維持を優先しました。
PSIスコア向上のためのCSSフルインライン化は、2ページ目以降のキャッシュ効率を悪化させ、長期的なメンテナンス性を損なうため、採用しませんでした。
コード抜粋
PSIのシミュレーション遅延を考慮した、現実的な目標値の算出ロジックの考え方です。
以下の計算は、【Lighthouse標準モバイル設定(Slow 4G)かつJS総量300–360KiB級】の条件下で成立するモデルです。
// パフォーマンス目標の論理的な計算例(Moto G Power / 4G Simulation)
const PSI_NETWORK_BASELINE = 601; // ms (観測された下限値)
const REACT_HYDRATION_OVERHEAD = 150; // ms (現状構成での最小コスト)
const CONTENT_LOAD_TIME = 200; // ms (最適化された実装)
// 実用上の LCP の限界値
const LOGICAL_LCP_LIMIT = PSI_NETWORK_BASELINE + REACT_HYDRATION_OVERHEAD + CONTENT_LOAD_TIME;
console.log(`この環境での LCP 理論限界値: ${LOGICAL_LCP_LIMIT}ms`);
// 結果: 951ms (1秒を切るのがいかに困難かがわかる)構造的限界を「チームの共通認識」にする
この分析結果を受けて、私はプロジェクトの運営方針に2つの重要な変更を加えました。
1. 開発ドキュメントへの明記
「なぜ100点が取れないのか」を個人の感想ではなく、プロジェクトの公式見解としてドキュメント化しました。
これにより、未来の自分やAIが「あと10ms削れるはずだ」という亡霊に取り憑かれ、無意味なリファクタリングに走るのを防ぎます。
2. Lighthouse改善スキルへのガードレール追加
AIエージェントが使用する最適化スキル(lighthouse-fixer)に、以下のガードレール(制約)を追加しました。
- 「LCP 1.0秒未満を目指す提案をするな」: この条件下では費用対効果が負になる可能性が高く、コードの可読性を破壊するリスクがあるため。
- 「Reactのハイドレーションコストをゼロにしようとするな」: それはフレームワークの否定であり、Remixを使う意味がなくなるため。
AIは放っておくと「理論上の100点」を目指してコードを破壊し始めます。「ここまでやったら、それ以上はやらなくていい」という「撤退ライン」を教えることこそが、AI時代のエンジニアリングにおける最も重要な設計作業です。
今回の学びと感想
「100点をとるための Tips」は溢れていますが、「なぜ 100 点がとれないのか」を構造的に理解している人は多くありません。
Remix + Vite という強力なスタックを使っていても、測定ツールの仕様という土俵の上では、私たちは常にハンデを背負って戦っています。
100点満点という呪縛を捨て、論理的に導き出された「実質的な上限値(/blogなら97点、/loginなら99点)」を公式目標に据える。
この「賢い撤退」こそが、エンジニアリングリソースを本質的な機能開発へと 100% 解放するための、最強の戦略であると確信しました。
この構造的限界を理解した上で、それでも達成した最高スコアの記録はLighthouseモバイル全カテゴリ100点を達成した時の実装にあります。また、「100点を追わない」という判断のプロセス自体はLighthouse 99点で止めた理由でより実践的な文脈として記録しています。TTFBを極限まで削るアプローチとして、Jamstackプレビルドアーキテクチャによるリクエスト時処理の排除も有効な補完戦略です。
