[ニュース解説]GitHub Issuesが”instant”になるまで——IndexedDBキャッシュ・プレヒーティング・Service Workerで変えたナビゲーション体験

目次

はじめに

 GitHubが2026年5月14日、GitHub IssuesのナビゲーションパフォーマンスをIndexedDBベースのクライアントキャッシュ・プレヒーティング戦略・Service Workerの組み合わせで大幅に改善した取り組みを、エンジニアリングブログで詳細に公開しました。本稿では、その設計思想・アーキテクチャ・実測結果を解説します。

解説論文

関連記事

あわせて読みたい
[ニュース解説]GitHubが明かす4月の2件の障害と「30倍スケール」への対応戦略 はじめに  GitHubのCTO・Vlad Fedorov氏が2026年4月28日、プラットフォームの可用性に関するアップデートをブログで公開しました。本稿では、4月に相次いだ2件の障害の...
あわせて読みたい
[開発者向け]GitHubがeBPFでデプロイの安全性を高める:循環依存を検知・防止する仕組みとは? はじめに  GitHubのエンジニアリングブログが2026年4月16日、自社のデプロイシステムにおける循環依存の問題をeBPFを活用して解決した取り組みを公開しました。本稿で...

要点

  • GitHub IssuesチームはHPC(Highest Priority Content)という独自の体感速度指標を用い、200ms未満を「instant」、1,000ms未満を「fast」、それ以上を「slow」と分類して改善目標とした
  • クライアントサイドにIndexedDBベースの永続キャッシュを導入し、Stale-While-Revalidate(SWR)パターンで「まず即座に表示→バックグラウンドで最新データと照合」を実現した
  • プレヒーティング(事前キャッシュ投入)によりキャッシュヒット率が約33%から約96%まで向上し、Reactソフトナビゲーションの約70%がinstantバケットに達した
  • Service Workerがハードナビゲーション(フルページロード)時にもキャッシュを利用可能にし、サーバーへ「薄いHTMLシェルのみ返却」を指示することでTurboナビゲーション速度も向上した
  • P50(中央値)のHPCが約1,200msから700msへ改善し、ナビゲーションの中央体験が「slow」バケットから「fast」バケットへ移行した

詳細解説

The speed of thought: Web performance in 2026(思考の速さ:2026年のウェブパフォーマンス)

 記事冒頭でGitHubのエンジニアリングチームは、「2026年において『十分速い』はもはや競争優位性の基準にならない」と述べています。開発者ツールにとってレイテンシ(応答遅延)はプロダクト品質そのものであり、ユーザーはローカルファーストツールのような「瞬時に感じられる」体験を基準に評価します。「1秒以内に読み込まれる」ではなく「感覚上ゼロ遅延」が目指すべき標準だという考え方です。

 GitHub Issuesは毎週世界中の数百万人が利用する大規模なサービスです。IssuesがAI支援ワーク(AI Copilotを使った計画・管理など)の基盤として位置づけられる中、意図とフィードバックの間に生じる遅延が全体を遅く感じさせるとして、パフォーマンスは一層重要視されています。4月の障害報告でもGitHubは「30倍スケール」への対応を公開しており、継続的なインフラ改善への取り組みが続いています。

 計測に使う指標は HPC(Highest Priority Content) と呼ばれます。Web標準の LCP(Largest Contentful Paint) ——ページ上で最も重要なコンテンツが初めて表示されるまでの時間——に近い内部指標で、IssueページではIssueのタイトルまたは本文テキストがHPC要素として選択されます。

 ナビゲーションはHPCの閾値に基づいて3つのバケット(区分)に分類されます:

  • Instant:HPC < 200ms(体感上「即時」)
  • Fast:HPC < 1,000ms(許容範囲)
  • Slow:HPC ≥ 1,000ms(体感上「遅い」)

 従来はP90・P99などの悪い外れ値の改善に注力していましたが、今回の施策では分布全体の質——「何割のナビゲーションがfast/instantバケットに入るか」——へ焦点を移しました。P99を改善しても中央体験が遅いままでは意味がない、という考え方の転換です。

The baseline: Navigation mix before we changed anything(改善前のナビゲーション分布)

 最適化設計の前提として、ユーザーがどのようにIssuesページへ到達しているかを3種類のナビゲーションに分類し計測しました。

  1. Hard navigation(ハードナビゲーション):フルブラウザロード。URLを直接入力・外部リンクからの遷移・タブを新規に開く・ページリフレッシュなどが相当します。ネットワーク通信・サーバーレンダリング・アセット(JS/CSS)のダウンロード・JavaScriptの起動・Reactのハイドレーション(サーバーレンダリングされたHTMLにReactの動作を紐付ける処理)のすべてのコストが毎回発生します。
  2. Turbo navigation(Turboナビゲーション):Ruby on RailsのTurboフレームワークによる遷移で、ページ全体を再読み込みせずターゲット領域だけを更新します。フルリロードのオーバーヘッドは一部避けられますが、依然としてサーバーレンダリングに大きく依存します。
  3. Soft navigation / React(Reactソフトナビゲーション):既存のReactランタイムが起動した状態でのクライアントサイド遷移です。ページ全体の再起動コストを回避でき、データフェッチのレイテンシが主なコストとなります。

 改善前の計測では、ハードナビゲーションが全トラフィックの 57.6%、Reactソフトナビゲーションが 37.5% を占めていました。各タイプの平均HPCはハードが2.05秒・Turboが1.76秒・Reactが1.04秒と、すべてのパスで「fast」基準の1秒を超えています。

 最大シェアを持つハードナビゲーションが最も遅い——この現実が次のアーキテクチャ設計の出発点になります。なお、GitHubはRailsレンダリングからReactフロントエンドへの移行の途中にあり、Rails/React間の境界をまたぐ遷移(例:Railsページ→IssueページへのリンクをクリックするとフルHardナビゲーションが発生)がハードナビゲーションの多さの主要因です。

Step 1: Client-side caching with IndexedDB(ステップ1:IndexedDBによるクライアントサイドキャッシュ)

 第一弾の改善はReactソフトナビゲーションに絞りました。ランタイムが既に起動しているこのパスでは、データフェッチのレイテンシが主なコストです。ネットワーク通信をキャッシュで排除できれば、大量のナビゲーションをinstantバケットに押し込めると推計しました。

 事前分析では「ユーザーはバックログ整理やレビューループの中で同じIssueを繰り返し開く」という強い再アクセスパターンが確認されており、キャッシュヒット率の初期推定値は 約30% と見積もられました。

 選択したストレージは IndexedDB です。ブラウザが提供する非同期のキーバリューストレージで、以下の理由から採用されました:

  • 永続性:タブを閉じたりブラウザを再起動してもデータが残ります(メモリキャッシュと異なり揮発しません)
  • インデックス検索:オブジェクトストアへのキーベース高速ルックアップが可能です
  • 大容量クォータ:localStorage(同期APIかつ容量制限が厳しい)よりも実用的な作業セットを格納できます

 キャッシュ動作の方式は Stale-While-Revalidate(SWR) です。これは「まず古くてもキャッシュデータで即座に画面を表示し、バックグラウンドで最新データを取得して差分があれば更新する」パターンです。ネットワークが不安定なときも、ユーザーはキャッシュから動作するページを得られ、接続回復後に自動的に最新状態へ収束します。「キャッシュか正確性か」という二択ではなく、「レイテンシ優先のレンダリング+非同期整合性チェック」という設計思想です。

 本番全ユーザーへのロールアウト後の計測結果:

  • Reactナビゲーションのinstant比率:4% → 約22%(全リクエストの約15%に相当)
  • キャッシュヒット率:約33%(事前推計の約30%と整合)

 トレードオフとしてサーバーとキャッシュの不一致(ダイバージェンス)は 約4.7% と計測されました。速度向上の対価として意図的に設定された「制御された陳腐化(Controlled staleness)」として許容範囲とされています。

Moving the needle on cache-hit ratios(キャッシュヒット率の改善:プレヒーティング)

 キャッシュはヒット率が高くなければ効果が限定的です。33%のヒット率では、大多数のナビゲーションがキャッシュウォームアップ前に到着してしまいます。ヒット率を上げるための最も直感的な解決策は「すべての可能性のある次のIssueを先読みする(プリフェッチ)」ですが、Issueリスト・ダッシュボード・プロジェクトビューのようなファンアウトが大きい画面では N+1 的なリクエスト爆発を引き起こし、サーバー負荷が急増するため採用しませんでした。

 代わりに選んだのが プレヒーティング(Preheating) です。プレヒーティングの核心は「そのIssueのデータがキャッシュに存在しない場合だけネットワークにアクセスする」という点です。すでにキャッシュにあれば何もしません。これは「最新データを確保するためにネットワークにアクセスするプリロード」とは本質的に異なる「キャッシュ投入ロジック」です。

 このアプローチは鮮度よりも容量効率を優先したトレードオフです。「ナビゲーション時点でキャッシュに何らかのデータがある」状態を最小コストで作ることが目的であり、ナビゲーション後のバックグラウンド再検証で最終的に最新状態に収束させます。

 キャッシュアクセスをさらに高速化するため、IndexedDB の前段に インメモリキャッシュ層 を追加しました。IndexedDBへのアクセスは非同期のため、クリティカルパス(描画に直結する処理の流れ)にわずかながらコストが生じます。インメモリ層でホットなIssueペイロードを同期的に提供することで、この非同期コストを排除し、ソフトナビゲーションが純粋にメモリから描画できる確率を高めています。

 プレヒーティングはIssueリスト・ダッシュボード・プロジェクト・依存関係ビューなどの「高インテント画面(ユーザーが次に何かをクリックする確率が高い画面)」から起動されます。実行は低優先度ワーカーに委ねられ、厳格なレート制限(過剰リクエスト防止)とサーキットブレーカー(過負荷時に自動停止する仕組み)で保護されています。ユーザーが直接操作したリクエストは常に投機的フェッチより優先されます。

 プレヒーティング展開後の計測結果:

  • issues#show全体のinstant比率:約30%
  • Reactナビゲーション限定のinstant比率:約70%
  • キャッシュヒット率:約96%

 少量の制御されたバックグラウンドリクエストを代償に、大多数の実ユーザーナビゲーションをネットワーク待ちのない経路へ誘導することに成功しています。

Expanding the fast path: Optimizing turbo and hard navigations(高速パスの拡張:TurboナビゲーションとHardナビゲーションの最適化)

 Reactソフトナビゲーションの改善だけでは、57.6%を占めるハードナビゲーションには効果がありません。リフレッシュ・新規タブ・直接URL入力・外部リンクからの遷移は常に存在します。これらにもキャッシュを活用するための仕組みとして Service Worker を導入しました。

 Service Workerはブラウザとオリジンサーバーの「間に立つ」ブラウザ管理のスクリプトです。ページのJavaScriptランタイムが動き始める前から割り込みが可能であり、ハードナビゲーションの段階からキャッシュ済みデータを利用できる唯一に近いウェブAPIです。

 動作の流れは次の通りです:

  1. ブラウザがIssueページへのナビゲーションリクエストを開始する
  2. Service Workerがそのリクエストを捕捉し、ローカルキャッシュに該当Issueのデータがあれば、リクエストヘッダーに「キャッシュヒット」フラグを付与する
  3. サーバーはそのヘッダーを見て応答パスを分岐する:
    • キャッシュヒット時:HTMLシェル(レイアウト+最小限のマークアップ+JS)のみを返し、クライアントサイドのReactがローカルキャッシュのIssueペイロードからレンダリングする
    • キャッシュミス時:通常のSSR(サーバーサイドレンダリング)レスポンスを返す(フォールバック)

 このService WorkerはTurboナビゲーションにも大きな恩恵をもたらしました。Turboはサーバー応答時間に強く依存するため、サーバー側の計算量が削減されると速度が直接改善します。

 一方、ハードナビゲーションのキャッシュヒット時はSSRコストがクライアントレンダリングに置き換わるため、JavaScriptのダウンロードと実行が新たなボトルネックになります。これに対しては React.lazyと動的ルートプリロード によるコード分割を実施し、現在のルートに必要なコードだけを優先的に取得するようにしました。コンポーネントレベルでも初期表示に不要なモジュールを遅延ロードし(例:編集ボタンが押された時点で初めてエディタバンドルを取得)、ホバーを起点とした意図ベースプリフェッチで次操作の待ち時間を隠蔽しています。

The results(結果)

 IndexedDBキャッシュ・プレヒーティング・インメモリ層・Service Workerの全施策展開後、HPCの各パーセンタイルで以下の改善が確認されました:

パーセンタイル改善前改善後変化
P10約600ms70ms−530ms(instantバケット入り)
P25約800ms120ms−680ms(instantバケット入り)
P50(中央値)約1,200ms700ms−500ms(slowからfastへ)
P751,800ms1,400ms−400ms
P902,400ms2,100ms−300ms

 特筆すべきはP10・P25の圧縮幅です。キャッシュ済み・プレヒート済みナビゲーションがこの区間を支配するようになり、速い方の体験がさらに速くなりました。P50は1,000msの壁を初めて下回り、ナビゲーションの中央体験が「slow」から「fast」へ移行しました。

 P90はJavaScript起動とクライアントレンダリングがボトルネックとなるコールドスタートのハードナビゲーションを反映しており、改善はあるものの依然として課題が残ります。これが次フェーズの主要ターゲットとして明示されています。

The work ahead(今後の課題)

 GitHubによれば、今回の施策で「これまでで最速のGitHub Issues」を実現したものの、作業は完了していないとしています。

 Service Workerによってサーバー側の処理量を削減した後のハードナビゲーションでは、JavaScriptのダウンロードと実行が新たなボトルネックとなっています。コールドスタートでSSRに依存するパスはP90の数値が示す通り、まだ改善の余地が大きい状態です。

 次フェーズとして計画されているのは(1)低レイテンシ配信に特化したバックエンドスタックの部分的な再構築、(2)往復回数の削減と応答時間短縮を目指したエッジに近い場所でのUI配信レイヤーへの投資、の2点です。GitHubはパフォーマンス改善を一度限りのプロジェクトではなく、継続的なシステム投資として位置づけています。


まとめ

 GitHub Issuesの高速化は、バックエンド最適化ではなく「クライアントに既にあるデータをいかに再利用するか」という設計転換によって実現しました。IndexedDBキャッシュ・プレヒーティング・Service Workerを組み合わせることで、P50 HPCが1.2秒から700msへ大幅に短縮され、Reactナビゲーションの約70%が瞬時に応答するようになっています。記事中でも言及されている通り、同様の設計思想は他のデータ量が多いWebアプリにも転用できる考え方だと思います。GitHubのインフラ・スケール対応の全体像は、4月の可用性レポートでも詳しく紹介されていますので、あわせてご覧いただければと思います。

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!
  • URLをコピーしました!
目次