効果のあるパフォーマンス予算:高速なウェブアプリのための実践的な上限
パフォーマンス予算は、読み込み時間、JS サイズ、Core Web Vitals に明確な上限を設け、簡易監査と「先に修正」ルールでウェブアプリを高速に保つ手法です。

なぜパフォーマンスには「予算」が必要か
パフォーマンス予算は、ビルド前に合意する一連の上限です。時間(ページがどれだけ速く感じられるか)、サイズ(送るコード量)、あるいは単純な上限(リクエスト数、画像、サードパーティスクリプト)にできます。上限を超えたら「後で直すべき」ではなく、壊れた要件として扱います。
遅くなるのは大抵、出荷が加算的だからです。新しいウィジェットが増えるたびに JavaScript、CSS、フォント、画像、API 呼び出しが増え、ブラウザの仕事が増えます。小さな変更でも積み重なってアプリが重く感じられ、特に中堅スマホや遅いネットワークでは顕著です。
感覚や意見は守ってくれません。「自分のラップトップでは問題ない」と「遅い」の議論が続くだけです。予算は議論を終わらせ、パフォーマンスを測定・強制できるプロダクト制約に変えます。
Addy Osmani の考え方と同様に、パフォーマンスをデザイン制約やセキュリティルールのように扱うべきです。セキュリティを「頑張って保つ」やレイアウトが「良く見えることを期待する」のではなく、基準を設定し継続してチェックし、壊す変更をブロックします。
予算は実用的な問題を同時に解決します。トレードオフを明示化し(機能を追加するならどこかで代償を払う)、回帰を早期に捕まえ、全員に「十分に速い」の共通定義を与えます。リリース直前のパニックを減らす効果もあります。
例えばリッチなチャートライブラリをダッシュボードに追加すると、全員に配信されメインバンドルが膨らみ、最初の有意味な画面が遅れます。予算がなければ「機能が動く」ので通ってしまいますが、予算があればチームは遅延読み込みにするかライブラリを替えるか表示を簡素化するかを選ぶ必要があります。
これは Koder.ai のように素早くアプリを生成・反復できる環境で特に重要です。速く作れるのは良いことですが、気づかないうちに余分な依存や UI の装飾を出荷しがちです。予算は速い反復が遅いプロダクトになるのを防ぎます。
シンプルな目標から始める:ページ、デバイス、ネットワーク
パフォーマンス作業は「全部を測るが誰も責任を持たない」と失敗します。実際のユーザーに重要な一つのページフローを選び、予算の基準にします。
良い出発点はコンバージョンや日常作業に影響する主要なジャーニーです(例:「ホーム→サインアップ」「ログイン後のダッシュボード最初の読み込み」「チェックアウトと確認」)。代表的で頻度の高いものを選び、エッジケースは避けます。
ユーザーに合ったターゲットを選ぶ
アプリはあなたのラップトップで動いているわけではありません。高速マシンで問題なく見えても、中堅スマホでは遅く感じることがあります。
まず一つのデバイスクラスと一つのネットワークプロファイルを決め、それを文章にして誰でも繰り返せるようにしておきます。
例:過去2〜3年の中堅 Android スマホ、移動中の 4G(オフィス Wi‑Fi ではない)、コールドロード測定と主要なナビゲーション1回、ユーザーが多い地域で測定。
これは最悪ケースを選ぶという意味ではなく、実際に最適化できる代表的なケースを選ぶという意味です。
ベースラインのテスト設定を固定する
数値は比較可能でなければ意味がありません。ある実行が「拡張機能ありの Mac の Chrome」で、別の実行が「スロットルしたモバイル」だとトレンドはノイズになります。
予算チェック用に基準環境を一つ決めて守ってください:同じブラウザバージョン、同じスロットリング設定、同じテストパス、同じキャッシュ状態(コールドかウォームか)。実機を使うなら同じ機種を使います。
「十分速い」を行動で定義します(完璧なデモではなく)。例えば「ユーザーが素早くコンテンツを読み始められる」や「ログイン後にダッシュボードが応答的に感じられる」です。それをこのジャーニー向けの一つか二つの指標に翻訳し、そこに予算を設定します。
予算の種類:タイミング、サイズ、リクエスト、ランタイム
予算はユーザーの体感(何を感じるか)とチームが制御できる要素(なぜ遅くなるか)を両方カバーすると効果的です。良いセットは体験指標とリソース/CPU の上限を混ぜます。
タイミング予算(ユーザー体験)
これらは実際の人にとってページがどう振る舞うかを追います。最も有用なのは Core Web Vitals に直結するものです:
- LCP(Largest Contentful Paint):主要コンテンツがどれだけ早く表示されるか。
- INP(Interaction to Next Paint):タップやクリック、入力時の応答性。
- CLS(Cumulative Layout Shift):レイアウトのズレの量。
タイミング予算はユーザーの不満に直結するため北極星になりますが、何を直すかは示さないことがあるので、以下のタイプの予算も必要です。
サイズ、リクエスト、ランタイム予算(遅さの原因)
これらはビルドやレビューで強制しやすく、具体的です。
ウェイト予算は総 JavaScript、総 CSS、画像の重さ、フォントの重さなどを上限で縛ります。リクエスト予算は総リクエスト数やサードパーティスクリプトを制限し、ネットワーク上のオーバーヘッドやタグ・ウィジェットからの「驚き」を減らします。ランタイム予算はロングタスク、メインスレッド時間、ハイドレーション時間(特に React)を制限し、なぜページが中堅機で「重く感じる」かを説明する手がかりになります。
実用的な React の例:バンドルサイズは問題なさそうでも、新しいカルーセルがクライアントレンダリングを重くし、ページは読み込まれてもフィルタ操作が引っかかることがあります。起動時のロングタスクを制限したり、ハイドレーションが中堅機で Y 秒以内に終わるようにするランタイム予算を設けると、ウェイト予算だけでは見えない問題を捕まえられます。
最も強いアプローチはこれらを一つのシステムとして扱うことです:体験予算が成功を定義し、サイズ/リクエスト/ランタイム予算がリリースを正直に保ち、何が変わったかを簡単に答えられるようにします。
実際に強制できる具体的なスタータ予算
上限を多くしすぎると誰も気にしなくなります。まずはユーザーが最も感じる点に合い、かつ各 PR やリリースで測れる 3〜5 個を選んでください。
実用的なスターターセット(後で調整):
- LCP:警告 2.5s、失敗 3.0s(モバイル、コールドロード)。
- INP:警告 200ms、失敗 300ms(メニュー開閉やフォーム送信などの一般的な操作)。
- CLS:警告 0.10、失敗 0.15。
- JavaScript バンドルサイズ(初期ルート):警告 170KB gzip、失敗 220KB gzip(アプリコードのみ)。
- 画像(初期表示):警告 800KB、失敗 1.2MB(最初の画面で転送される画像の合計バイト)。
二段階の閾値にすると運用が現実的になります。警告は「ズレてきている」を知らせ、失敗はリリースをブロックするか明示承認を要求します。これで限界が現実のものになりますが、常時の火消しにはなりません。
予算は共有場所に短く具体的に書いておいて、どのページ/フローをカバーするか、どこで測るか(ローカル監査、CI、ステージビルド)、どのデバイスとネットワークを使うか、指標の定義(フィールド vs ラボ、gzip vs 生データ、ルート単位 vs アプリ全体)を明確にしてください。
現行アプリから予算を設定する手順
繰り返せるベースラインから始めます。キーとなるページを一つ二つ選び、同じデバイスプロファイルとネットワークで毎回テストします。テストは少なくとも3回実行し中央値を記録して、一回の外れ値に方向性を左右されないようにします。
シンプルなベースラインシートにユーザー指標とビルド指標を入れます。例:ページの LCP と INP、それにビルドの総 JavaScript サイズと総画像バイト数。こうするとラボの推定だけでなく「実際に何が出荷されたか」が見えるので予算が現実味を帯びます。
現在の中央値より少しだけ良くなる設定にしてください。一気に非現実的な数値にするのは避けます。例えば LCP がベースラインで 3.2s なら、いきなり 2.0s を目指すのではなく 3.0s を最初の目標にします。守れることを証明してから徐々に厳しくします。
各リリース前に簡易チェックを追加します。人がスキップしない程度に速くすることが重要です。シンプルな運用例:合意したページで単一ページの監査を実行し、JS や画像が予算を超えたらビルドを失敗させ、コミットごとに結果を保存していつ増えたかがわかるようにし、常に同じ URL パターンでテストする(ランダムデータは避ける)。
違反は週次でレビューし、誰かが苦情を言うまで放置しないでください。違反をバグとして扱い、原因となった変更を特定し、今直すものと後で予定するものを決めます。徐々に厳しくし、数リリースにわたって守れたらさらに引き締めます。
プロダクトの範囲が変わったら予算を意図的に更新します。新しい解析ツールや重い機能を追加したら、何が増えたか(サイズ、リクエスト、ランタイム)、その後でどう返済するか、いつ予算を元に戻すかを書き留めます。
クイック監査:何が変わったか素早く見る方法
予算は素早くチェックできて初めて役に立ちます。10 分監査の目的は完璧な数値を出すことではなく、最後に良好だったビルドから何が変わったかを見つけ、何を先に直すかを決めることです。
10 分監査の流れ
代表的な一ページから始め、毎回同じ簡易チェックを実行します:
- LCP 要素を特定する(ヒーロー画像、見出しブロック、製品ギャラリーなど)。LCP 要素が変わると結果が大きく変わることがあります。
- JavaScript の重量と最大のチャンクを確認する。新しい依存、重複ライブラリ、大きな機能が全員に出荷されていないか探します。
- 折り返し領域の画像に、圧縮不足、誤った寸法、レスポンシブソースの欠如といった共通ミスがないか見る。
- サードパーティタグ(解析、チャット、A/B テスト)を確認する。新しいタグ一つが大きなダウンロードやロングタスクを追加することがあります。
- ネットワークと CPU を合わせて見る:バイト数が増えたから遅いのか、ページがやり過ぎているから遅いのかを見極めます。
数分で最大の問題を見つける方法
通常はネットワークウォーターフォールとメインスレッドタイムラインの2つのビューで数分内に答えが出ます。
ウォーターフォールで重要なのはクリティカルパスを支配するリクエストです:巨大なスクリプト、ブロッキングフォント、遅れて開始する画像など。LCP リソースの要求が早くないと、サーバが速くても LCP 目標は達成できません。
タイムラインではロングタスク(50ms 以上)を探します。起動周りでロングタスクが固まっていると、初回読み込みで JS が多すぎることが多いです。一つの巨大なチャンクはルーティングの問題か共有バンドルの肥大を示します。
次回の比較を可能にするメモ
クイック監査は毎回変わる実行で失敗します。基本情報を残して差分が明確になるようにしてください:ページ URL とビルド/バージョン、テストデバイスとネットワークプロファイル、LCP 要素の説明、追跡する主要数値(例:LCP、総 JS バイト、リクエスト数)、最大の原因に関する短いメモ。
デスクトップテストはクイックなフィードバックや PR チェックに有用です。予算に近づいたとき、見た目が引っかかるとき、ユーザーがモバイル寄りのときは実機でテストしてください。モバイル CPU はロングタスクを明確にし、「ラップトップでは問題ない」が崩れる箇所を浮き彫りにします。
何を先に直すか:時間を節約するシンプルなルール
予算が破られたときに「すべて最適化する」のは最悪です。再現可能なトリアージ順を使い、各修正に明確な効果が見えるようにします。
実用的なトリアージ順
ユーザーが最も気にするところから始め、細かな調整に進みます:
- ファーストビューの主要要素を速くする。 最も大きく描画される要素を見つけ、その資産やレンダリング経路を修正します。画像のリサイズと圧縮、適切なフォーマットの使用、最初の表示をブロックする遅延フォントの回避など。
- CSS を磨くより先に JavaScript を削る。 JS が多すぎると解析が遅く、メインスレッド作業が増え、描画が遅れます。未使用コードの削除、重いルートの分割、単純な UI はサーバレンダリングや静的化を優先します。
- サードパーティスクリプトを早めに手なずける。 チャットウィジェット、解析、タグマネージャー、A/B ツールは大きなダウンロードやロングタスクを生むことがあります。初回表示に不要なら遅延読み込み、価値が低ければ削除します。
- レイアウトシフトは設計で止める。 画像・広告・埋め込みのスペースを予約し、width/height を設定し、安定したプレースホルダを使い、読み込み後に既存コンテンツの上に UI を注入しないようにします。
- 操作が重いならロングタスクを減らす。 INP が悪ければ大量のクライアント処理、大きな状態更新、高コストな JSON 処理を探します。処理を小さく分け、再レンダを減らし、非 UI 作業をクリティカルパスから外します。
具体例
あるチームが新しいダッシュボードを出し、一気に LCP 予算を外しました。キャッシュヘッダを調整する前に LCP 要素を調べると、フル幅のチャート画像が原因でした。画像をリサイズして軽いフォーマットで配信し、必要な部分だけ先に読み込みます。さらに大きなチャートライブラリが全ルートで読み込まれているのを見つけ、そのライブラリは分析ページだけで読み込み遅延するようにしました。サードパーティのサポートウィジェットも最初の操作後まで遅延させ、1 日でダッシュボードは予算内に戻りました。次のリリースでは「何が変わったか」が明確になりました。
よくある質問
パフォーマンス予算とは、平易に言うと何ですか?
パフォーマンス予算とは、チームがビルドの前に合意する厳格な上限(時間、サイズ、リクエスト、CPU 作業)です。
変更がその上限を超えたら、壊れた要件として扱います:修正するか、スコープを削るか、責任者と期限を付けて例外を明示的に承認します。
「パフォーマンスを気にする」だけではなく予算が必要なのはなぜですか?
パフォーマンスは徐々に悪化します。各機能が JavaScript、CSS、画像、フォント、API 呼び出し、サードパーティタグを追加していくためです。
予算があれば、その重みや作業を増やすならどこかで“支払う”(遅延読み込み、ルート分割、UI の簡素化、依存削除)必要があると強制できます。
最初にどのページやフローを予算化すべきですか?
まずは実際のユーザーの一連の操作と一貫したテスト設定を選んでください。
良い出発点は頻繁かつビジネスに重要な流れ(例:ログイン後のダッシュボード初回読み込み、ホーム→サインアップ、購入確定)です。最初はエッジケースは避け、毎回測れるフローを選びます。
デバイスとネットワークのターゲットをどう選べばいいですか?
典型的なユーザーに合う単一のターゲットを決めます。例えば:
- 中堅クラスのスマホ
- 4G(社内 Wi‑Fi ではなく)
- コールドロードと主要なナビゲーション一回
これを文にして書き留め、安定して使い続けてください。デバイスやネットワーク、キャッシュ状態、テストパスを変えるとトレンドがノイズになります。
どの指標がパフォーマンス予算に向いていますか?
ユーザーが感じる部分とチームが制御できる部分をカバーする小さなセットを使います:
- タイミング:LCP, INP, CLS
- サイズ:初期ルートの JS gzip(必要なら CSS)
- メディア:ファーストビューの 画像バイト数
タイミング指標は体感を示し、サイズ/ランタイムは原因を特定しやすくします。
予算の現実的な初期数値はどれくらいですか?
実践的なスターターは次のとおりです:
- LCP: 警告 2.5s, 失敗 3.0s(モバイル、コールドロード)
- INP: 警告 200ms, 失敗 300ms(一般的な操作)
- CLS: 警告 0.10, 失敗 0.15
- 初期 JS(アプリコード): 警告 170KB gzip, 失敗 220KB gzip
- 画像(初回表示): 警告 800KB, 失敗 1.2MB
まずは 3~5 個に絞り、後でベースラインに合わせて調整してください。
「警告」と「失敗」の閾値を分ける意味は何ですか?
2段階に分けます:
- Warn(警告):少しずれていることを知らせます。マージはできるが調査すべき。
- Fail(失敗):リリースをブロックするか、明確な承認が必要。
これにより常時の緊急対応を避けつつ、線を越えたときに実効力が出ます。
予算が破られた瞬間は何をすべきですか?
やるべき順序:
- LCP 要素が変わっていないか確認(ヒーローが変わると結果が変わる)。
- 何が増えたかを確認:JS バイト数、画像バイト、リクエスト数、サードパーティタグ。
- 起動付近の ロングタスク(50ms 以上) を探す。
- 最大の原因を最初に直す(たいていは上部表示の画像、新しい依存、コード分割が外れたルート)。
違反はバグとして扱い、コミットを特定して修正またはスコープ縮小し、再発を防ぎます。
バンドルサイズの予算を満たしているのに React アプリが遅く感じるのはなぜですか?
バンドルサイズが許容範囲でも体感が遅くなる理由は main thread の負荷です。
React の一般的な原因:
- 重いハイドレーション処理
- 最初の操作での高コストな再レンダリング
- 大きなコンポーネントがクライアント側で過度に処理する
このクラスの問題を捕まえるために、起動時のロングタスク制限やハイドレーション時間の上限といったランタイム予算を加えてください。
Koder.ai のようなツールで素早く作るチームに、パフォーマンス予算はどう馴染みますか?
高速に生成・反復できると、依存や UI の飾りが気づかれずに増えがちです。
対策:
- 企画段階で予算を書き込む(どのページ、どの上限、どのテスト設定)
- 各リリースや PR で簡易チェックを実行する
- スナップショットやロールバックで、変更が上限を越えたら戻せるようにする
ツールは手段です。重要なのは、各機能が“重さを支払う”か、支払わないなら出荷しない習慣をつけることです。Koder.ai のような環境でも同じです(Koder.ai)。