GitHub's pull request experience, the core of developer collaboration, faced a significant performance bottleneck with large code changes. The platform recently deployed a new React-based 'Files changed' tab, prioritizing speed and responsiveness even for pull requests spanning millions of lines across thousands of files. This effort tackled issues like excessive JavaScript heap usage, massive DOM node counts, and sluggish interaction latency, which previously rendered extreme cases unusable.
The engineering team at GitHub adopted a multi-pronged strategy rather than a single fix. They focused on optimizing individual diff lines, ensuring efficient rendering for typical reviews, and implementing graceful degradation with virtualization for the largest codebases. Foundational rendering improvements were also key, compounding benefits across all pull request sizes.
The Uphill Climb of Making Diff Lines Performant
Previously, each diff line was a performance hog. In the unified view, a single line could translate to roughly 10 DOM elements, ballooning to 15 in split view, further inflated by syntax highlighting's numerous <span> tags. At the React layer, this meant a minimum of eight components per line in unified view and 13 in split view, with additional overhead for UI states like comments or hover effects.
This architecture, while initially practical when porting from Rails, became unsustainable. Each small, reusable component often carried its own event handlers, leading to dozens per line. Across a large pull request, this compounded into thousands of handlers, severely impacting performance and developer complexity. The result was a direct correlation between pull request size and slower interaction metrics, coupled with increased JavaScript heap usage.
The v2 architecture prioritized simplicity: less state, fewer elements, and leaner JavaScript. A prime example of this granular optimization was the removal of unnecessary <code> tags from line number cells. While seemingly minor, dropping just two nodes per line across 10,000 lines resulted in 20,000 fewer DOM nodes.
