
GitHub reconstrói visualização de PRs gigantes no app Copilot
O problema: quando comentários quebram a virtualização
O time do GitHub reconstruiu a visualização de pull requests no app Copilot para abrir um PR de código aberto com 2.200 arquivos, mais de um milhão de linhas alteradas e mais de 400 comentários de revisão embutidos. A renderização rápida de diffs grandes é um problema conhecido: virtualizar as linhas, montar só as linhas visíveis com uma pequena margem e reciclar elementos do DOM. Como cada linha de código tem altura fixa e conhecida, dá para calcular a tabela de geometria inteira antes da pintura - o contrato de "todas as alturas conhecidas antes da pintura".
Comentários quebram esse contrato. A altura de um comentário depende de como o markdown quebra linha, de blocos expansíveis, de caixas de resposta e do carregamento de imagens - tudo descoberto apenas na renderização. A abordagem óbvia de reservar slots de altura fixa por estimativa falha nos extremos: superestima a maioria e corta ou gera barras de rolagem aninhadas nos piores casos; e corrigir a tabela de offsets depois da pintura causa saltos de rolagem enquanto o usuário lê.
Duas geometrias em vez de uma
A solução foi dividir a altura do documento em dois domínios independentes: a geometria do código, determinística e exata, mantida como antes; e a geometria de blocos dinâmicos, para threads de revisão, rascunhos e compositores de resposta. Cada bloco tem uma chave estável, é ancorado a arquivo, linha e lado (não a coordenadas de pixel), carrega uma impressão digital de tudo que pode alterar sua altura e registra a largura em que foi medido pela última vez. A altura efetiva usa a medida real quando válida, cache quando a impressão digital e a largura batem, e estimativa caso contrário. O número de blocos é limitado pelos comentários, não pelas linhas.
No agendador de medição, o primeiro design - um ResizeObserver por bloco escrevendo alturas de volta no layout - foi rejeitado por criar um loop de feedback que se reativa. O que entrou em produção é uma única passada de medição ociosa e limitada à rolagem: nunca roda durante o scroll, cobre só blocos a cerca de 2.400 px do viewport, lê todos os blocos montados em um único lote (regra que corrigiu o pior bug, comentários com faixas de espaço em branco) e faz no máximo uma renderização fora da tela como fallback. Observadores por bloco apenas sinalizam o bloco para releitura; nunca escrevem alturas. A exceção deliberada: quando o próprio usuário expande um bloco ou abre um compositor, a correção é aplicada sincronamente no mesmo quadro, com no máximo um commit por frame e nunca durante rolagem ativa.
Quando uma altura medida difere da estimativa, a correção é feita por identidade, não por pixel: captura-se a que o usuário está ancorado, aplicam-se os deltas, resolve-se a nova posição da âncora e rola-se para mantê-la fixa. Correções acima do viewport são aplicadas; hidratação abaixo não é; interações do usuário suprimem a correção. Um detalhe subtle: o guard de "usuário rolando" confundia rolagens programáticas da própria superfície (como ao alternar a barra lateral) com rolagens do usuário, fazendo o conteúdo driftar - a correção foi distinguir rolagens do usuário das causadas pela superfície.
O pipeline e o loop de medição
No lado dos dados, três hábitos sustentaram a interface: transmitir estrutura antes do conteúdo (a árvore de arquivos pinta enquanto o diff carrega, e todas as threads de revisão são resolvidas de uma vez); adiar trabalho por item (realce de sintaxe roda fora da thread principal, markdown grande só é construído perto do viewport); e manter um cache dos últimos diffs visitados, evitando o efeito de um shell instantâneo em volta de um diff vazio ao voltar.
Como os bugs só apareciam sob carga, em motor específico e posição específica de rolagem, o time instrumentou a superfície com sondas permanentes e estruturadas de seus próprios invariants (limites de linhas montadas, coalescência de medição por frame, tamanho das correções de scroll, vazamentos de observers) e as fixou como orçamentos em testes end-to-end. O centro do processo foi um loop autônomo de mudar → medir → melhorar: uma via headless que executava fluxos declarativos em JSON contra um servidor mock, lendo a instrumentação de produção, e um autopilot que dirigia o app desktop sem supervisão em cenários frios e quentes, com um sinal de saúde objetivo por amostra - sem lacunas entre comentários, sem blocos em branco, em toda a faixa de rolagem.
O resultado é uma visualização em que um diff de um milhão de linhas com centenas de comentários em thread abre, rola e se comporta como um pull request de tamanho normal: comentários renderizam por completo, expandir uma seção move apenas o código abaixo dela e voltar a um PR recoloca o usuário onde estava.