Как GitHub Copilot app справился с миллионной PR: разбор рендеринга гигантских диффов

#github copilot#frontend performance#large diffs#agentic workflows

В работе с крупными PR часто сталкиваюсь с проблемой: браузер зависает, интерфейс тормозит, а код ревью превращается в пытку. Когда в проекте появляются миллионные строки изменений и сотни инлайн-комментариев — рендерить такой дифф становится серьёзной задачей. Недавно GitHub поделился, как они решили эту проблему в своем Copilot app. Мне показалось полезным разобрать их подход и понять, что можно взять в свои проекты.

Проблема: масштаб и интерактивность одновременно

Обычные GitHub PR — это в среднем сотни, максимум несколько тысяч строк. С ними справляются штатные средства. Но что если у тебя миллион строк кода в одном PR? И при этом сотни inline-комментариев, которые сохраняют контекст ревью.

В web-интерфейсе это — потеря интерактивности, огромная нагрузка на DOM, и, как следствие, троттлинг браузера. Особенно когда нужно быстро скроллить, искать, отвечать на ревью — UX начинает страдать.

GitHub Copilot app столкнулся с задачей открыть такой PR и сделать его удобным для пользователя, который хочет одновременно читать, анализировать и править код с помощью AI-помощников.

Новый подход к рендерингу диффа

Вместо классического подхода с рендерингом всего диффа целиком, команда GitHub Copilot app построила потоковую прорисовку и ленивую загрузку частей диффа.

Виртуализация и подгрузка чанков

Идея не нова — виртуализация списка давно применяется в таблицах и списках с большим количеством элементов. Но здесь сложнее:

Решение — разбить весь миллион строк на небольшие чанки и рендерить только видимые и близкие к видимым чанки. При скролле подгружаются новые части, старые отрисованные удаляются из DOM.

Оптимизация рендеринга комментариев

Комментарии — отдельная головная боль. Их нельзя просто “выкинуть” на время, так как они критичны для ревью.

Copilot app оптимизировал управление комментариями:

Сложности с foldable блоками и синхронизацией диффа

В больших PR часто встречаются foldable блоки (свернутые участки кода). Управление состоянием этих блоков усложняется на миллионной длине.

В Copilot app сделали:

Пример: простая виртуализация диффа с комментариями

Вот упрощённый пример, как можно реализовать виртуализацию списка строк с комментариями на React:

import React, { useState, useRef, useEffect } from 'react';

interface Line {
  number: number;
  content: string;
  comment?: string;
}

interface Props {
  lines: Line[];
  chunkSize?: number;
  height: number;
  lineHeight: number;
}

export function VirtualDiff({ lines, chunkSize = 100, height, lineHeight }: Props) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);

  const totalHeight = lines.length * lineHeight;
  const visibleCount = Math.ceil(height / lineHeight);

  const startIndex = Math.max(0, Math.floor(scrollTop / lineHeight) - chunkSize);
  const endIndex = Math.min(lines.length, startIndex + visibleCount + 2 * chunkSize);

  const visibleLines = lines.slice(startIndex, endIndex);

  const offsetY = startIndex * lineHeight;

  function onScroll(e: React.UIEvent<HTMLDivElement>) {
    setScrollTop(e.currentTarget.scrollTop);
  }

  return (
    <div ref={containerRef} onScroll={onScroll} style={{ height, overflowY: 'auto', fontFamily: 'monospace' }}>
      <div style={{ height: totalHeight, position: 'relative' }}>
        <div style={{ transform: `translateY(${offsetY}px)`, position: 'absolute', top: 0, left: 0, right: 0 }}>
          {visibleLines.map(line => (
            <div key={line.number} style={{ height: lineHeight, borderBottom: '1px solid #ddd', display: 'flex' }}>
              <div style={{ width: 50, backgroundColor: '#f7f7f7', textAlign: 'right', paddingRight: 8 }}>
                {line.number}
              </div>
              <pre style={{ margin: 0, flex: 1 }}>{line.content}</pre>
              {line.comment && (
                <div style={{ marginLeft: 10, backgroundColor: '#ffe', padding: '0 5px', borderRadius: 3 }}>
                  {line.comment}
                </div>
              )}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Этот компонент не решит все проблемы GitHub Copilot app, но наглядно показывает, как можно подгружать и рендерить только видимую часть диффа с комментариями.

Что важнее — UX или скорость?

Вся эта история с Copilot app — пример tradeoff между производительностью и удобством работы.

Можно отдать предпочтение скорости — показать только часть кода и прятать всё остальное. Но тогда теряется контекст и ревью становится хуже.

Можно грузить всё — но тогда интерфейс будет виснуть и тормозить.

GitHub попытался сбалансировать: виртуализация + ленивый рендер + оптимизация комментариев + сохранение интерактивности. Это похоже на vibe coding в большом масштабе — подстройка под состояние пользователя и контекст, чтобы UI не мешал, а помогал.

Где это пригодится тебе?

Если ты работаешь с большими кодовыми базами и сталкиваешься с громоздкими диффами, задумайся о подобных подходах:

Если в твоём стеке есть AI ассистенты (например, Copilot, Claude Code) — важно, чтобы UI не тормозил и быстро реагировал на запросы и подсказки.

В агентных workflow (agentic development) с несколькими AI агентами и большим количеством контента оптимальный рендеринг — одна из базовых задач.

Что попробовать дальше?

GitHub Copilot app показал, что даже с миллионом строк в одном PR можно сделать удобный и отзывчивый интерфейс. Главное — не пытаться рендерить всё сразу, а работать с данными потоково и адаптивно.

В итоге, если хочешь держать vibe в ревью и не перегружать браузер — начинай с виртуализации и оптимизации комментариев. А дальше уже можно пробовать agentic workflow и AI-интеграции, без страха потерять UX.


Попробуй сам: Cursor — AI-редактор для разработчиков.


Источник: https://github.blog/engineering/user-experience/rendering-huge-pull-requests-in-the-github-copilot-app/