Ce qui se passe
GitHub a reconstruit la vue des pull requests de son application Copilot pour qu'elle reste fluide sur les très gros changements. Le banc d'essai : une pull request open source de 2 200 fichiers, plus d'un million de lignes modifiées et plus de 400 commentaires en ligne. Alberto Gimeno, qui signe le billet, en tire des règles qui valent pour toute liste virtualisée à hauteur variable.
Ce qui marchait déjà pour le code
Un diff d'un million de lignes ne tient pas dans le DOM. La réponse classique est la virtualisation : une centaine de lignes seulement sont montées, et recyclées pendant le défilement. Tout le reste est de l'arithmétique sur une table de hauteurs. Comme chaque ligne de code a la même hauteur, cette table se calcule d'avance et ne change jamais. GitHub a gardé tout ce socle : un rendu impératif sans composant React par ligne, des tableaux typés pour les décalages, un diff envoyé structure d'abord, et une API « défiler jusqu'à la ligne N ».
Le problème des commentaires
Un fil de revue n'a pas de hauteur connue avant d'être rendu : elle dépend du
retour à la ligne du markdown, d'un bloc <details> déplié, d'un champ de
réponse qui grandit à la frappe, d'une image qui finit de charger. Réserver une
hauteur estimée laisse des trous ou coupe les longs fils. Corriger après coup
décale tout ce qui suit, pendant que l'utilisateur fait défiler.
Deux géométries
La solution est de ne plus forcer une seule géométrie à tout porter :
hauteur totale = hauteur du code (exacte, calculée d'avance)
+ Σ hauteurs des blocs (estimées, puis mesurées)
+ marge de défilementChaque commentaire devient un bloc identifié par ce qu'il est, pas par sa position : une clé stable, un ancrage sur fichier, ligne et côté du diff, une empreinte de tout ce qui peut changer sa taille, et la largeur de la dernière mesure, arrondie par paliers pour qu'un simple redimensionnement n'invalide pas tout. Sa hauteur est la mesure si elle est valide, sinon une valeur en cache si empreinte et largeur correspondent, sinon l'estimation. Ces hauteurs vivent dans leur propre index : un commentaire qui grandit ne reconstruit jamais la géométrie du code.
L'erreur qu'ils ont d'abord commise
Le premier réflexe, un ResizeObserver par bloc qui réécrit la hauteur
mesurée, a été rejeté : un observateur qui modifie la mise en page de l'élément
qu'il surveille peut se redéclencher lui-même, et le coût croît avec le nombre
de blocs. À la place :
- une seule passe de mesure, lancée quand la zone visible se stabilise, jamais pendant un défilement ;
- limitée aux blocs à moins de ~2 400 px de l'écran, donc proportionnelle à la fenêtre, pas au document ;
- toutes les lectures groupées, en un seul reflow sans écriture intercalée ; un bloc monté n'est jamais écarté au profit d'une estimation, règle qui a corrigé les bandes blanches sous certains commentaires ;
- l'observateur ne fait que marquer un bloc à relire ;
- une seule exception : quand l'utilisateur provoque lui-même le changement (déplier, répondre), la correction est appliquée dans la même frame, une fois par frame au plus, et jamais pendant un défilement.
Ancrer par identité, pas par pixel
Avant chaque correction, on note l'élément regardé et le décalage à l'intérieur ; on applique les nouvelles hauteurs ; on retrouve la nouvelle position de cet élément et on défile pour qu'il reste en place. Un bloc au-dessus de l'écran qui change de taille est compensé, un bloc en dessous ne l'est pas.
Le piège le plus instructif : la garde « ne pas corriger pendant que l'utilisateur défile » se basait sur le dernier défilement observé, que les défilements programmatiques rafraîchissaient aussi. Ouvrir la barre latérale élargissait le diff, provoquait un petit défilement interne, et la correction censée garder la place sautait. Leur règle : un test « l'utilisateur interagit-il ? » ne doit jamais pouvoir être satisfait par vos propres effets de bord.
Côté données et outillage
La coloration syntaxique tourne hors du thread principal, les derniers diffs visités restent en cache, et des sondes permanentes vérifient en test de bout en bout des budgets chiffrés : nombre de blocs montés, une seule écriture par frame, taille des corrections, observateurs bien déconnectés. Une boucle automatique pilotait l'application et écrivait ses mesures sur disque, où un agent les lisait sans humain au clavier.
Source : Rendering huge pull requests in the GitHub Copilot app, Alberto Gimeno, GitHub Blog, 23 septembre 2026.