GitHubは、極端なケースに対応するため、GitHub Copilotアプリのプルリクエスト表示インターフェースを再構築した。そのケースとは、2,200ファイル、100万行を超える変更、そして400件を超えるインラインレビューコメントを含むプルリクエストである。目的は大規模な差分の表示を高速化することだけではなく、会話そのものがドキュメントに可変の寸法を加えた後も、スクロールとレビューを使いやすく保つことだった。
従来の大規模な差分向けインターフェースは、遅延読み込み、つまりビューポート付近に表示される要素だけを維持し、スクロール中にDOM要素を再利用する方式に依存している。このモデルは、各要素が既知の高さを持つコード行である場合には、比較的容易に機能する。しかしコメントにはこの性質がない。コメントの高さは、Markdownの折り返し、<details>セクションの展開、返信エディターの表示、画像、提案された変更、さまざまなインタラクション状態に応じて変化する。
1つの高さテーブルではなく、2つのレイアウト
GitHubはコード行について、固定されたレイアウトを維持した。これは位置と高さの事前計算を使用し、コメントが変化しても再構築しない。一方、コメント、返信エディター、動的なブロックは独立したインデックスに配置した。各ブロックには、ファイル、行、側に関連付けられた安定したキーに加え、コンテンツのフィンガープリント、開いているセクションの状態、直近の計測幅が与えられる。
計測値が有効な場合は計測された高さを使用し、フィンガープリントと幅が一致している場合はキャッシュされた値を使用する。それ以外の場合、インターフェースは一時的な推定値を使用する。これにより、1つのコメントが拡張しても、何百万行ものレイアウトを再計算する必要がなくなる。
スクロール処理の外側での計測
GitHubは、各ブロックに個別のResizeObserverを割り当て、計測値をレイアウトへ直接書き込む初期設計を取りやめた。この方式では、レイアウトの変更が監視を再実行するため、フィードバックループが発生する可能性がある。また、複合ブロックの数が増えるほどコストも増大する。
同社が提供した設計では、アイドル期間およびスクロール終了後に関連付けられた、1つの計測サイクルを使用する。通常はビューポートから約2,400ピクセル以内にあるブロックだけを計測し、遠くにあるブロックは近づくまで推定値のままにする。表示中の要素の計測値は一括で読み取り、繰り返しのリフローを避ける。
監視機能は、画像の読み込みや返信エディターへの入力などの変更を検知するために引き続き存在する。ただし、高さを直接変更するのではなく、計測サイクルで再読み取りする必要があることをブロックにマークする。例外は、セクションや返信エディターを開くなど、表示中のブロックでユーザーが起こした変更である。この場合は同じフレーム内で1回の同期的な補正を適用できるため、コメントの拡張と、その下にあるコードの移動との間に目に見える段差が生じない。
読み取り位置を失わずにレイアウトを補正する
実際の高さが推定値と異なる場合、インターフェースはピクセル数だけを基準に位置を補正しない。ユーザーが読んでいる要素が行なのかコメントブロックなのかを特定し、その内部でのオフセットを保存してから、高さの差分を適用し、同じ要素の位置を再計算する。これにより、固定された要素はほぼ同じ場所にとどまる。
通常、ユーザーがスクロールしている間やスクロールの勢いがある間は補正を行わず、画面下部のコンテンツによって生じた変更を表示位置の移動には使用しない。チームは、パネルの表示幅の変更によって発生したプログラムによるスクロールがユーザーによるスクロールと見なされ、補正が失われて読んでいたファイルがずれるバグに遭遇した。これには、ユーザーの操作とインターフェース自体が引き起こした変更を分離することで対処した。
データフローと自動テスト
応答性は遅延表示だけに依存していない。差分の構造とファイルデータを最初に送信し、残りのドキュメントを読み込んでいる間にファイルツリーとメタデータを表示する。一方、構文強調や詳細なMarkdownの構築などの処理は、要素がビューポートに近づくまで遅延させる。また、インターフェースは直近で使用した少数の差分を一時的に保持し、それを超えるものを解放してメモリ使用量を抑える。
深いスクロール時にしか現れない不具合を検出するため、GitHubは永続的な計測シグナルと自動テストを追加した。これらは、複合された行とブロックの数、フレーム時間、スクロール補正の規模、監視機能のリーク、未充填の空白の有無を監視する。チームはデスクトップアプリケーションと実際の表示エンジン上で自動フローを実行し、セクションを開く、返信エディターを起動する、ウィンドウのサイズを変更する、大規模なファイル一覧内を移動するなどのケースをテストした。
この設計が重要な理由
公表された結果では、100万行と数百件のコメントを含むプルリクエストが通常のプルリクエストのように動作する。コメントは入れ子になったスクロールバーの中で切り詰められるのではなく完全に表示され、セクションを展開するとランダムなジャンプなしにその下のコードが移動し、プルリクエストに戻った際には読み取り位置を保持できる。最も重要なエンジニアリング上の価値は、GitHubがすべてのコンテンツを同じ性質にしようとしなかったことにある。決定論的な部分を高速なまま維持し、そのサイズを表示前に把握できないコンテンツから分離した。
ただし、この記事は、すべての大規模なプルリクエストが理解やリスク管理の面でレビューしやすくなったと主張しているわけではない。対処したのは、表示インターフェースのパフォーマンスと、計測および変更中の挙動である。また、結果はGitHub Copilotとその内部設計に関連するものであり、異なるデバイスや表示エンジンにおけるメモリ消費量やパフォーマンス指標について、独立した数値を示しているわけではない。