Code Velocity
Eines per a Desenvolupadors

Rendiment de les línies Diff: L'ardu camí de GitHub per optimitzar

·7 min de lectura·GitHub·Font original
Compartir
Diagrama que mostra les millores de rendiment a les línies diff de GitHub, destacant la reducció de nodes DOM i del heap de JavaScript en una vista optimitzada.

title: "Rendiment de les línies Diff: L'ardu camí de GitHub per optimitzar" slug: "the-uphill-climb-of-making-diff-lines-performant" date: "2026-04-06" lang: "ca" source: "https://github.blog/engineering/architecture-optimization/the-uphill-climb-of-making-diff-lines-performant/" category: "Eines per a Desenvolupadors" keywords:

  • GitHub
  • Peticions d'extracció
  • Optimització del rendiment
  • Línies diff
  • Desenvolupament amb React
  • Rendiment web
  • Optimització del DOM
  • Heap de JavaScript
  • Interaction to Next Paint (INP)
  • Arquitectura de programari
  • Disseny de components
  • Enginyeria de front-end meta_description: "L'equip d'enginyeria de GitHub detalla el seu rigorós viatge per optimitzar el rendiment de les línies diff en les peticions d'extracció, reduint dràsticament el heap de JavaScript, els nodes DOM i millorant l'INP." image: "/images/articles/the-uphill-climb-of-making-diff-lines-performant.png" image_alt: "Diagrama que mostra les millores de rendiment a les línies diff de GitHub, destacant la reducció de nodes DOM i del heap de JavaScript en una vista optimitzada." quality_score: 94 content_score: 93 seo_score: 95 companies:
  • GitHub schema_type: "NewsArticle" reading_time: 7 faq:
  • question: "Què és la pestanya 'Fitxers canviats' a les peticions d'extracció de GitHub i per què era crític el seu rendiment?" answer: "La pestanya 'Fitxers canviats' és un component central del flux de treball de les peticions d'extracció de GitHub, que permet als enginyers revisar les modificacions del codi. El seu rendiment és crític perquè és on els desenvolupadors passen un temps considerable, i les ralentitzacions, especialment amb peticions d'extracció grans, poden dificultar greument la productivitat i l'experiència de l'usuari. GitHub va prioritzar la seva optimització per garantir la capacitat de resposta en totes les escales de canvis de codi, des de correccions menors fins a refactoritzacions extenses, que poden implicar milions de línies en milers de fitxers. Mantenir un procés de revisió fluid i eficient és fonamental per al desenvolupament col·laboratiu."
  • question: "Quins van ser els principals reptes de rendiment que GitHub va afrontar amb peticions d'extracció grans en l'arquitectura v1?" answer: "En la seva arquitectura inicial basada en React (v1), GitHub va experimentar una degradació significativa del rendiment en gestionar peticions d'extracció grans. Els problemes clau incloïen que el heap de JavaScript superava 1 GB, el nombre de nodes DOM superava els 400.000, i les interaccions de la pàgina es tornaven extremadament lentes o fins i tot inutilitzables. La mètrica Interaction to Next Paint (INP), que mesura la capacitat de resposta, mostrava valors inacceptablement alts. Aquests problemes derivaven d'una estratègia de renderització ineficient on cada línia diff era intensiva en recursos, amb massa elements DOM, components de React i gestors d'esdeveniments, particularment en casos que implicaven milers de línies de codi."
  • question: "Com va abordar GitHub la solució dels complexos problemes de rendiment, anant més enllà d'una solució 'bala de plata'?" answer: "Reconeixent que cap solució única abordaria la diversa gamma de mides i complexitats de les peticions d'extracció, GitHub va adoptar un enfocament estratègic multifacètic. Es van centrar en tres temes principals: optimitzacions dirigides als components de les línies diff per mantenir ràpides les revisions mitjanes i grans, degradació gràcil amb virtualització per mantenir la usabilitat en les peticions d'extracció més grans limitant el contingut renderitzat, i inversió en components fonamentals i millores de renderització per obtenir beneficis compostos en totes les mides de peticions d'extracció. Aquesta estratègia integral els va permetre adaptar solucions a àrees problemàtiques específiques."
  • question: "Quines van ser les limitacions clau de l'arquitectura de renderització diff 'v1' que la van fer insostenible a escala?" answer: "L'arquitectura v1, tot i ser inicialment raonable per a diffs més petits, va resultar insostenible per a peticions d'extracció a gran escala. Cada línia diff era costosa, requerint entre 10 i 15 elements DOM, entre 8 i 13 components de React, i més de 20 gestors d'esdeveniments. Això es va agreujar amb l'anidament profund de components, els hooks useEffect excessius i les consultes de dades O(n), que van provocar re-renderitzacions innecessàries i una complexitat augmentada. Les capes d'abstracció, destinades a compartir codi, van afegir inadvertidament sobrecàrrega en transportar lògica tant per a vistes dividides com unificades, fins i tot quan només una estava activa. Aquest disseny va comportar un augment significatiu del heap de JavaScript, el recompte de DOM i puntuacions INP deficients per a diffs més grans."
  • question: "Quins canvis arquitectònics específics es van implementar en 'v2' per millorar dràsticament el rendiment de les línies diff?" answer: "L'arquitectura v2 va introduir diversos canvis crítics. Va simplificar l'arbre de components, reduint els components de React per línia diff de vuit a dos, creant components dedicats per a vistes dividides i unificades, fins i tot amb certa duplicació de codi. La gestió d'esdeveniments es va centralitzar en un únic gestor de nivell superior utilitzant valors data-attribute, substituint nombrosos gestors individuals. L'estat complex de l'aplicació, com ara les funcions de comentaris, es va traslladar a components fills renderitzats condicionalment, garantint que les línies diff se centressin principalment en la renderització del codi. A més, la v2 va restringir els hooks useEffect als fitxers diff de nivell superior i va adoptar l'accés a dades de temps constant O(1) utilitzant JavaScript Map per a consultes d'estat eficients, reduint significativament les re-renderitzacions i millorant la gestió de dades."
  • question: "Com va aconseguir l'equip d'enginyeria de GitHub millores quantificables en el heap de JavaScript, els nodes DOM i les mètriques INP amb la v2?" answer: "L'efecte acumulatiu dels canvis arquitectònics de la v2 va comportar millores quantificables substancials. Per a una petició d'extracció amb 10.000 canvis de línia, la mida del heap de JavaScript es va reduir d'1GB+ a 250MB, una millora del 75%. Els nodes DOM van disminuir de 400.000+ a 80.000, una reducció del 80%. L'Interaction to Next Paint (INP) p95 (percentil 95) va experimentar una millora sorprenent del 90%, passant de 1000ms+ a només 100ms. Aquests resultats es van aconseguir mitjançant una optimització meticulosa, incloent l'eliminació d'elements DOM superflus, la simplificació de l'estructura de components de React, la centralització de la gestió d'esdeveniments i l'optimització de la gestió d'estats i dels patrons d'accés a dades, resultant en una experiència d'usuari molt més ràpida i responsiva."
  • question: "Què és Interaction to Next Paint (INP) i per què la seva millora és significativa per a l'experiència d'usuari de GitHub?" answer: "Interaction to Next Paint (INP) és una mètrica crucial de rendiment web que avalua la capacitat de resposta d'una pàgina mesurant la latència de totes les interaccions realitzades per un usuari amb la pàgina. Registra el temps des que un usuari interactua (per exemple, clic, toc, pulsació de tecla) fins que es pinta el següent fotograma a la pantalla, reflectint la retroalimentació visual d'aquesta interacció. Per a GitHub, un INP alt significava que els usuaris experimentaven un retard notable en l'entrada, fent que la plataforma semblés lenta i poc reactiva. Reduint l'INP p95 de més de 1000ms a 100ms en la v2, GitHub va millorar significativament la velocitat percebuda i la fluïdesa de la pestanya 'Fitxers canviats', garantint una experiència de desenvolupador més fluida i satisfactòria, especialment durant la revisió de codi."

L'ardu camí de GitHub: Optimitzant les línies Diff per a un rendiment màxim

Les peticions d'extracció (pull requests) són el nucli vibrant de GitHub, on innombrables enginyers dediquen una part significativa de les seves vides professionals. Donada la immensa escala de GitHub, gestionar peticions d'extracció que van des de petites correccions d'una sola línia fins a canvis colossals que abasten milers de fitxers i milions de línies, l'experiència de revisió ha de romandre excepcionalment ràpida i responsiva. El recent llançament de la nova experiència basada en React per a la pestanya Fitxers canviats, ara per defecte per a tots els usuaris, va marcar una inversió fonamental per garantir un rendiment robust, especialment per a aquestes grans i desafiadores peticions d'extracció. Aquest compromís va implicar abordar constantment problemes difícils com la renderització optimitzada, la latència d'interacció i el consum de memòria.

Abans d'aquestes optimitzacions, mentre que la majoria dels usuaris gaudien d'una experiència responsiva, les grans peticions d'extracció inevitablement conduïen a una notable disminució del rendiment. Els casos extrems veien el heap de JavaScript superar 1 GB, el recompte de nodes DOM superar els 400.000, i les interaccions de la pàgina es tornaven severament lentes o fins i tot inutilitzables. Mètriques clau de responsivitat com Interaction to Next Paint (INP) s'elevaven per sobre dels nivells acceptables, creant una sensació tangible de retard en l'entrada per als usuaris. Aquest article s'endinsa en el viatge detallat que GitHub va emprendre per millorar dràsticament aquestes mètriques de rendiment clau, transformant l'experiència de revisió de diffs.

En iniciar la investigació de rendiment per a la pestanya Fitxers canviats, ràpidament es va fer evident que una única solució de "bala de plata" no seria suficient. Les tècniques dissenyades per preservar totes les funcions i el comportament natiu del navegador sovint topaven amb un límit amb càrregues de dades extremes. Per contra, les mesures de mitigació dirigides únicament a prevenir els pitjors escenaris podrien introduir compromisos desfavorables per a les revisions quotidianes.

En canvi, l'equip d'enginyeria de GitHub va desenvolupar un conjunt integral d'estratègies, cadascuna dissenyada meticulosament per abordar mides i complexitats de peticions d'extracció específiques. Aquestes estratègies es van construir sobre tres temes fonamentals:

  1. Optimitzacions focalitzades per als components de línia Diff: Millora de l'eficiència de l'experiència diff principal per a la majoria de peticions d'extracció. Això va garantir que les revisions mitjanes i grans es mantinguessin ràpides sense comprometre funcionalitats esperades com la cerca nativa a la pàgina.
  2. Degradació gràcil amb virtualització: Garantia de la usabilitat per a les peticions d'extracció més grans prioritzant la capacitat de resposta i l'estabilitat, i limitant intel·ligentment el que es renderitza en cada moment.
  3. Inversió en components fonamentals i millores de renderització: Implementació de millores que proporcionen beneficis compostos en totes les mides de peticions d'extracció, independentment del mode de visualització específic de l'usuari.

Aquests pilars estratègics van guiar els esforços de l'equip, permetent-los abordar sistemàticament les causes arrel dels problemes de rendiment i preparar l'escenari per a posteriors refinaments arquitectònics.

Desconstruint la V1: El cost d'una línia Diff costosa

La implementació inicial de GitHub basada en React, referida com a v1, va establir les bases per a la vista diff moderna. Aquesta versió va ser un esforç sincer per portar la vista clàssica de Rails a React, prioritzant la creació de components de React petits i reutilitzables i mantenint una estructura clara de l'arbre DOM. No obstant això, aquest enfocament, tot i ser lògic en el seu inici, va resultar ser un coll d'ampolla significatiu a escala.

En la v1, renderitzar cada línia diff era una operació costosa. Una sola línia en una vista unificada es traduïa típicament en uns 10 elements DOM, mentre que una vista dividida requeria prop de 15. Aquest recompte augmentaria encara més amb el ressaltat de sintaxi, introduint moltes més etiquetes <span>. A la capa de React, els diffs unificats contenien almenys vuit components per línia, i les vistes dividides un mínim de 13. Aquests eren recomptes base, amb estats d'interfície d'usuari addicionals com comentaris, "hover" i "focus" afegint encara més components.

L'arquitectura v1 també patia d'una proliferació de gestors d'esdeveniments de React. Tot i semblar inofensiu a petita escala, una sola línia diff podia contenir 20 o més gestors d'esdeveniments. Quan es multiplicava per milers de línies en una petició d'extracció gran, això s'agreujava ràpidament, provocant una sobrecàrrega excessiva i un augment de l'ús del heap de JavaScript. Aquesta complexitat no només afectava el rendiment, sinó que també feia el desenvolupament i el manteniment més desafiadors. El disseny inicial, efectiu per a dades delimitades, va patir significativament quan es va enfrontar a la naturalesa il·limitada de les diverses mides de peticions d'extracció de GitHub.

Per resumir, per a cada línia diff de la v1, el sistema tenia:

  • Mínim de 10-15 elements de l'arbre DOM
  • Mínim de 8-13 components de React
  • Mínim de 20 gestors d'esdeveniments de React
  • Nombrosos components de React petits i reutilitzables

Aquesta arquitectura va correlacionar directament les mides més grans de les peticions d'extracció amb un INP més lent i un augment de l'ús del heap de JavaScript, fent necessària una reavaluació i un redisseny fonamentals.

Revolucionant la renderització: l'impacte de les optimitzacions de la V2

La transició a la v2 va marcar una revisió arquitectònica significativa, centrant-se en canvis granulars i d'impacte. L'equip va adoptar la filosofia que "cap canvi és massa petit quan es tracta de rendiment, especialment a escala". Un exemple clar va ser l'eliminació d'etiquetes <code> innecessàries de les cel·les dels números de línia. Tot i que eliminar dos nodes DOM per línia diff pugui semblar menor, en 10.000 línies, això instantàniament va equivaldre a 20.000 nodes menys al DOM, demostrant com les optimitzacions incrementals i dirigides produeixen millores substancials.

La comparació visual a continuació destaca la complexitat reduïda de la v1 a la v2 a nivell de component:

Components i HTML de la Diff V1. Teníem 8 components de React per a una sola línia diff. Components i HTML de la Diff V2. Teníem 3 components de React per a una sola línia diff.

Arquitectura de components simplificada

Una innovació clau en la v2 va ser la simplificació de l'arbre de components. L'equip va passar de vuit components de React per línia diff a dos. Això es va aconseguir eliminant els arbres de components profundament anidats i creant components dedicats per a cada línia diff dividida i unificada. Tot i que això va introduir certa duplicació de codi, va simplificar dràsticament l'accés a dades i va reduir la complexitat general. La gestió d'esdeveniments també es va centralitzar, ara gestionada per un únic gestor de nivell superior utilitzant valors data-attribute, reemplaçant els nombrosos gestors d'esdeveniments individuals de la v1. Aquest enfocament va simplificar dràsticament tant el codi com el rendiment.

Gestió intel·ligent de l'estat i accés a dades O(1)

Potser el canvi més impactant va ser la reubicació de l'estat complex de l'aplicació, com ara els comentaris i els menús contextuals, en components fills renderitzats condicionalment. En un entorn com GitHub, on les peticions d'extracció poden superar milers de línies, és ineficient que cada línia porti un estat de comentaris complex quan només una petita fracció tindrà comentaris. En traslladar aquest estat a components anidats, la responsabilitat principal del component de línia diff va passar a ser purament la renderització del codi, alineant-se amb el Principi de Responsabilitat Única.

A més, la v2 va abordar el problema de les consultes O(n) i els hooks useEffect excessius que afectaven la v1. L'equip va adoptar una estratègia de dues parts: restringir estrictament l'ús de useEffect al nivell superior dels fitxers diff i establir regles de linting per evitar la seva reintroducció en components d'embolcall de línies. Això va garantir una memoïtzació precisa i un comportament previsible. Simultàniament, les màquines d'estat globals i diff es van redissenyar per aprofitar les consultes de temps constant O(1) utilitzant objectes JavaScript Map. Això va permetre selectors ràpids i consistents per a operacions comunes com la selecció de línies i la gestió de comentaris, millorant significativament la qualitat del codi, el rendiment i reduint la complexitat mitjançant el manteniment d'estructures de dades aplanades i mapejades. Aquest enfocament meticulós per a optimitzar els fluxos de treball dels desenvolupadors i l'arquitectura subjacent garanteix un sistema robust i escalable.

L'Impacte Mesurable: la V2 Ofereix Guanyes Quantificables

Les meticuloses optimitzacions arquitectòniques i a nivell de codi implementades en la v2 van produir millores profundes i quantificables en les mètriques clau de rendiment. El nou sistema funciona significativament més ràpid, amb una reducció massiva en l'ús del heap de JavaScript i les puntuacions INP. La taula següent mostra les millores dramàtiques observades en una petició d'extracció representativa amb 10.000 canvis de línia en una configuració de diff dividit:

Mètricav1v2Millora
Heap de JavaScript1GB+250MB75%
Nodes DOM400.000+80.00080%
INP p951000ms+100ms90%

Aquestes xifres subratllen l'èxit de l'estratègia multifacètica de GitHub. Una reducció del 75% en la mida del heap de JavaScript i una disminució del 80% en els nodes DOM no només es tradueix en una petjada més lleugera al navegador, sinó que també contribueix directament a una interfície més estable i responsiva. La millora més sorprenent, una reducció del 90% en l'INP p95 (el percentil 95 de la latència d'interacció), significa que el 95% de les interaccions dels usuaris es completen ara en només 100 mil·lisegons, eliminant pràcticament el retard d'entrada que afectava les grans peticions d'extracció en la v1. Això millora significativament l'experiència de l'usuari, fent que les revisions de codi grans se sentin tan fluides i responsives com les més petites.

El compromís de GitHub amb la millora contínua, evidenciat per aquesta anàlisi profunda de l'optimització de línies diff, és un testimoni de la seva dedicació a proporcionar una plataforma de desenvolupadors de classe mundial. En analitzar rigorosament els colls d'ampolla de rendiment i implementar solucions arquitectòniques dirigides, no només han resolt problemes crítics d'escalabilitat, sinó que també han establert un nou estàndard de capacitat de resposta en el seu producte principal. Aquest enfocament en el rendiment garanteix que els enginyers puguin participar de manera eficient en tasques crucials com les revisions de codi, el que finalment condueix a una major qualitat i seguretat del codi i a un entorn de desenvolupament més productiu.

Preguntes freqüents

What is the 'Files changed' tab in GitHub pull requests and why was its performance critical?
The 'Files changed' tab is a core component of GitHub's pull request workflow, allowing engineers to review code modifications. Its performance is critical because it's where developers spend significant time, and slowdowns, especially with large pull requests, can severely impede productivity and user experience. GitHub prioritized its optimization to ensure responsiveness across all scales of code changes, from minor fixes to extensive refactorings, which can involve millions of lines across thousands of files. Maintaining a smooth and efficient review process is paramount for collaborative development.
What were the primary performance challenges GitHub faced with large pull requests in the v1 architecture?
In its initial React-based architecture (v1), GitHub encountered significant performance degradation when handling large pull requests. Key issues included the JavaScript heap exceeding 1 GB, DOM node counts soaring past 400,000, and page interactions becoming extremely sluggish or even unusable. The Interaction to Next Paint (INP) metric, which measures responsiveness, showed unacceptably high values. These problems stemmed from an inefficient rendering strategy where each diff line was resource-intensive, with too many DOM elements, React components, and event handlers, particularly in cases involving thousands of lines of code.
How did GitHub approach solving the complex performance issues, moving beyond a 'silver bullet' solution?
Recognizing that no single solution would address the diverse range of pull request sizes and complexities, GitHub adopted a multi-faceted strategic approach. They focused on three core themes: targeted optimizations for diff-line components to keep medium and large reviews fast, graceful degradation with virtualization to maintain usability for the largest pull requests by limiting rendered content, and investing in foundational components and rendering improvements to yield compounding benefits across all pull request sizes. This comprehensive strategy allowed them to tailor solutions to specific problem areas.
What were the key limitations of the 'v1' diff rendering architecture that made it unsustainable for scale?
The v1 architecture, while initially sensible for smaller diffs, proved unsustainable for large-scale pull requests. Each diff line was costly, requiring 10-15 DOM elements, 8-13 React components, and over 20 event handlers. This was compounded by deep component nesting, excessive `useEffect` hooks, and O(n) data lookups, leading to unnecessary re-renders and increased complexity. The abstraction layers, meant to share code, inadvertently added overhead by carrying logic for both split and unified views, even when only one was active. This design led to a significant increase in JavaScript heap, DOM count, and poor INP scores for larger diffs.
What specific architectural changes were implemented in 'v2' to drastically improve diff line performance?
The v2 architecture introduced several critical changes. It streamlined the component tree, reducing React components per diff line from eight to two by creating dedicated components for split and unified views, even with some code duplication. Event handling was centralized to a single top-level handler using `data-attribute` values, replacing numerous individual handlers. Complex app state, such as commenting features, was moved into conditionally rendered child components, ensuring that diff lines primarily focused on rendering code. Furthermore, v2 restricted `useEffect` hooks to top-level diff files and adopted O(1) constant-time data access using `JavaScript Map` for efficient state lookups, significantly reducing re-renders and improving data management.
How did the GitHub engineering team achieve quantifiable improvements in JavaScript heap, DOM nodes, and INP metrics with v2?
The cumulative effect of v2's architectural changes led to substantial quantifiable improvements. For a pull request with 10,000 line changes, the JavaScript heap size was reduced from 1GB+ to 250MB, a 75% improvement. DOM nodes decreased from 400,000+ to 80,000, an 80% reduction. The Interaction to Next Paint (INP) p95 (95th percentile) saw an astounding 90% improvement, dropping from 1000ms+ to just 100ms. These results were achieved through meticulous optimization, including removing extraneous DOM elements, simplifying the React component structure, centralizing event handling, and optimizing state management and data access patterns, leading to a much faster and more responsive user experience.
What is Interaction to Next Paint (INP) and why is its improvement significant for GitHub's user experience?
Interaction to Next Paint (INP) is a crucial web performance metric that assesses a page's responsiveness by measuring the latency of all interactions made by a user with the page. It records the time from when a user interacts (e.g., click, tap, keypress) until the next frame is painted to the screen, reflecting the visual feedback of that interaction. For GitHub, a high INP meant users experienced noticeable input lag, making the platform feel slow and unresponsive. By reducing INP p95 from over 1000ms to 100ms in v2, GitHub significantly enhanced the perceived speed and fluidity of the 'Files changed' tab, ensuring a smoother and more satisfying developer experience, especially during code review.

Manteniu-vos al dia

Rebeu les últimes notícies d'IA al correu.

Compartir