ProgramaçãoInfra e DevOps
Renderizando pull requests enormes no app GitHub Copilot
há 1 h
O GitHub reconstruiu a superfície de diff do app GitHub Copilot para manter rápida a revisão de pull requests extremos. O teste usou um pull request de código aberto com 2.200 arquivos, mais de 1 milhão de linhas alteradas e mais de 400 comentários inline.
O problema
Virtualizar apenas linhas de código é relativamente simples: a interface monta somente as linhas próximas da viewport, recicla os elementos durante a rolagem e calcula a posição de cada linha porque todas têm altura conhecida. Comentários tornam esse modelo insuficiente. A altura de um comentário depende da quebra do Markdown, de seções expansíveis, do editor de resposta e do carregamento de imagens; esses fatores só ficam conhecidos durante ou depois da renderização.
Reservar uma altura estimada para cada comentário também falhava. Estimativas maiores que o necessário criavam espaços vazios, enquanto estimativas menores cortavam conteúdo ou geravam barras de rolagem internas. Medir a altura depois da pintura e atualizar uma tabela compartilhada deslocava todo o conteúdo abaixo do comentário, causando saltos durante a rolagem.
Duas geometrias independentes
A solução separou o documento em dois domínios:
- Geometria do código: continua determinística, baseada em somas prefixadas e com alturas exatas. Redimensionar um comentário não força a reconstrução das linhas de código.
- Geometria de blocos dinâmicos: representa threads de revisão, rascunhos e editores de resposta, cuja altura é imprevisível. Cada bloco tem uma chave estável, é ancorado a arquivo, linha e lado do diff, e mantém uma impressão digital do conteúdo e do estado que podem alterar sua altura.
A altura efetiva de um bloco usa a medição atual quando válida, uma medição em cache quando a impressão digital e a largura ainda coincidem, ou uma estimativa nos demais casos. As medições são armazenadas em um índice separado do índice das linhas. A largura é registrada em faixas arredondadas para evitar invalidar todas as medições em um redimensionamento comum da janela.
O sistema evita um ResizeObserver por bloco. Esse desenho criava um ciclo de realimentação: o observador alterava o layout do próprio elemento observado, podia ser acionado novamente e aumentava o custo conforme crescia o número de blocos montados. A implementação final usa uma única passagem de medição, limitada por estado ocioso e por rolagem.
Quando uma medição diverge da estimativa, a correção é feita pela identidade do bloco, não apenas por coordenadas de pixel. As correções são pequenas, reutilizam medições existentes e permanecem ancoradas no conteúdo que o usuário está lendo.
Um problema adicional surgiu ao distinguir rolagem do usuário de rolagem causada pela própria superfície. Alterar a árvore lateral mudava a largura do diff; com quebra de linha ativada, as linhas acima refluíam e alteravam todo o espaço de coordenadas. O próprio componente emitia uma pequena rolagem ao se ajustar, mas o código a interpretava como interação recente do usuário e pulava a correção necessária. A correção passou a separar explicitamente eventos de rolagem iniciados pelo usuário daqueles provocados pela interface.
Carregamento e desempenho
O diff passou a carregar a estrutura antes do conteúdo. A árvore de arquivos e os metadados aparecem enquanto o documento ainda é baixado, e o conjunto completo de threads é resolvido antecipadamente em vez de chegar gradualmente.
O trabalho por item é adiado até ser necessário:
- O realce de sintaxe roda fora da thread principal; as linhas aparecem primeiro como texto simples e recebem cores quando o resultado chega.
- Corpos grandes de Markdown e o contexto de sugestões de alteração só são construídos quando se aproximam da viewport.
- O documento do diff é liberado ao sair da página para evitar consumo crescente de memória.
- Os últimos diffs ficam em cache; itens além desse limite são removidos, e uma atualização em segundo plano identifica conteúdo obsoleto. Assim, o cabeçalho e a árvore de arquivos reaparecem imediatamente ao retornar, sem manter todos os documentos grandes residentes.
Instrumentação e testes
Como muitos defeitos só apareciam de forma intermitente em pull requests grandes, a equipe adicionou sondas estruturadas permanentes para verificar invariantes a cada renderização. Esses sinais são tratados como orçamentos de aprovação ou falha em testes de ponta a ponta com uma fixture sintética contendo muitos comentários.
O ciclo automatizado de mudança, medição e melhoria tinha duas frentes:
- Uma sonda sem interface gráfica executava fluxos declarativos, como abrir um pull request, rolar até uma fração, alternar uma seção de detalhes e redimensionar a janela. Ela lia a instrumentação de produção, incluindo contagens de renderização do React, a linha do tempo de desempenho e amostras de
requestAnimationFramepara detectar travamentos. - Um piloto controlava o app desktop real em ciclos frios e aquecidos, alternando seções ``, abrindo e cancelando editores de resposta, recolhendo e expandindo arquivos, alternando a árvore lateral, percorrendo arquivos profundos e redimensionando a janela. Cada amostra era gravada em um log no disco e recebia um sinal de saúde.
Uma amostra aquecida só era considerada saudável quando não havia lacunas entre comentários, blocos de comentário vazios ou threads reais que deixassem de ser montadas em qualquer trecho da rolagem, inclusive durante a varredura de arquivos profundos. O fluxo era executado, medido, analisado, ordenado pelos gargalos e repetido sem intervenção manual; o agente podia descrever o fluxo em linguagem natural, sem editar o código-fonte.
Resultado
A nova visualização faz um diff de mais de 1 milhão de linhas com centenas de comentários se comportar como um pull request comum: abre e rola sem degradar de forma perceptível, renderiza comentários completos em vez de cortá-los em caixas com rolagem interna, move apenas o código abaixo quando uma seção é expandida e restaura a posição anterior ao retornar ao pull request.
Fontes
- GitHub BlogRendering huge pull requests in the GitHub Copilot app