Wenn Entwickler durch ein Backlog navigieren, ist Latenz mehr als nur eine Kennzahl – sie ist ein Flow-Killer. Genau hier setzte das GitHub-Team an und hat die Architektur von GitHub Issues grundlegend umgekrempelt. Anstatt weiterhin nach marginalen Verbesserungen auf dem Backend zu suchen, wurde das Paradigma gewechselt: Die Daten liegen nun lokal beim Nutzer, bevor der Server überhaupt gefragt wird.
Das Problem: Architektur als Flaschenhals
GitHub Issues war nicht langsam im klassischen Sinne. Das Problem war der Request-Lifecycle. Wer durch Issues navigiert, öffnet oft denselben Ticket mehrfach – beim Triagieren, bei der Verfolgung eines Links und beim Zurückkehren zur Liste. Jede dieser Navigationen zahlte oft den vollen Preis für Server-Rendering, Netzwerk-Latenz und Client-Boot, selbst wenn die Daten bereits vorher gesehen wurden.
Die Messgröße, die das Team verwendete, ist ebenso spannend wie logisch: HPC (Highest Priority Content), ein interner Metrik-Analogon zum Web Vital LCP. Das Team klassifizierte Navigationen in drei Bucket: Instant (< 200 ms), Fast (< 1000 ms) und Slow (>= 1000 ms). Bisher lag der Fokus oft auf dem p90- oder p99-Wert – also den extremen Ausreißern nach oben. Das GitHub-Team erkannte jedoch: P99-Optimierung macht die Seite nicht zwingend für die Mehrheit der Nutzer schneller. Der neue Fokus lag auf der Verteilungsqualität: Wie viel Prozent der Navigationen landen im Instant-Bucket?
Die Analyse zeigte einen dominanten, aber langsamen Pfad: Harte Navigationen (Full Reloads), oft verursacht durch den Übergang zwischen dem alten Rails-Backend und der neuen React-Frontend-Architektur. Ein reiner Fokus auf React Soft Navigations hätte das Kernproblem nicht gelöst.
Schritt 1: IndexedDB und Stale-While-Revalidate
Der erste architektonische Eingriff galt den React Soft Navigations. Wenn der React-Runtime bereits aktiv ist, ist die Netzwerklatenz die dominierende Bremse. Die Lösung: Ein clientseitiger Cache auf Basis von IndexedDB, der Tabs und Browser-Neustarts überlebt.
Das System nutzt Stale-While-Revalidate (SWR): Bei einer Navigation wird sofort aus dem lokalen Cache gerendert. Parallel wird im Hintergrund der Server nach aktuellen Daten gefragt. Gibt es Abweichungen, wird die UI asynchron aktualisiert. Ist das Netzwerk offline, greift der Cache als Graceful-Degradation-Modell.
Das Ergebnis: Der Anteil an instanten Navigationen innerhalb der React-Paths sprang von 4% auf 22%. Die Cache-Hit-Rate lag bei ca. 33%. Der Preis für diesen Geschwindigkeitsschub ist kontrollierte Datenveraltung. Das Team maß eine Abweichung zwischen Server und Cache von 4,7% – ein akzeptabler Kompromiss für spürbare Geschwindigkeit.
Schritt 2: Preheating statt blindem Prefetching
Ein Cache ist nur so gut wie seine Hit-Rate. Da die Hit-Rate noch bei einem Drittel lag, war der nächste Schritt logisch: Daten vorausladen. Doch naives Prefetching auf Übersichtsseiten würde das System durch N+1-Requests überlasten.
Das Team führte daher Preheating ein. Statt Daten immer frisch zu laden, wird nur geprüft: Sind bereits nutzbare Daten lokal? Wenn ja, passiert nichts. Wenn nein, werden diese über Low-Priority-Worker im Hintergrund abgerufen. Um den kritischen Pfad weiter zu beschleunigen, wurde ein In-Memory-Layer vor die asynchrone IndexedDB geschaltet.
Das Ergebnis war massiv: Die Cache-Hit-Rate kletterte auf 96%. Bei React-Navigationen wurden 70% instant.
Schritt 3: Service Worker für harte Navigationen
Soft Navigations sind die eine Sache, aber was ist mit harten Reloads? Hier kommen Service Worker ins Spiel. Sie fungieren als programmierbarer Proxy zwischen Browser und Server und können Requests abfangen, bevor das JavaScript der Seite überhaupt bootet.
Trifft der Service Worker auf gecachte Daten, fügt er dem Request einen Header hinzu. Dieser signalisiert dem Server: „Ich habe die Daten schon, schick mir nur das HTML-Gerüst.“ Der Server überspringt das aufwendige SSR (Server-Side Rendering) und spart massiv Rechenzeit. Auf dem Client übernimmt React das Rendering aus dem lokalen Cache. Bei einem Cache-Miss greift der klassische, server-gerenderte Pfad. Besonders Turbo-Navigationen profitierten von diesem Ansatz, da der Server-Overhead drastisch reduziert wurde.
Eigenständige Einordnung: Der Paradigmenwechsel zum Local-First
Was GitHub hier beschreibt, ist weit mehr als ein Performance-Tuning. Es ist ein architektonischer Paradigmenwechsel, von dem jedes datenintensive Web-App lernen kann. Die alte Denkweise „Der Server ist die Quelle der Wahrheit, der Client muss sie jedes Mal fragen“ wird ersetzt durch „Der Client rendert sofort aus lokaler Wahrheit und gleicht asynchron ab“.
Diese Local-First-Architektur erfordert ein Umdenken. Entwickler müssen akzeptieren, dass Nutzer für einen Bruchteil einer Sekunde veraltete Daten sehen könnten. In einem Issue Tracker wie GitHub, wo es um Triaging und Text geht, ist eine Abweichung von unter 5% ein absolut fairer Deal für eine 70-prozentige Instant-Rate. In anderen Domänen (z.B. Börsenhandel oder Krankenhaus-Systemen) müsste man diese Tradeoffs strenger bewerten.
Interessant ist auch die ehrliche Einordnung der Rails-zu-React-Migration. GitHub beißt hier den Apfel des hybriden Übergangs: Solange Rails und React koexistieren, wird es harte Navigationen geben. Der Service-Worker-Ansatz ist ein brillanter Klebstoff, um die Latenz dieses Übergangs zu überbrücken, bis die Migration abgeschlossen ist.
Für Entwickler von datenlastigen Web-Apps lautet die Lektion: Wartet nicht auf einen kompletten Rewrite, um Performance zu bringen. Verschiebt die Datenhoheit in den Client, baut intelligente Caching-Layer und nutzt Service Worker, um die Server-Last zu reduzieren. GitHub beweist: „Instant“ ist keine Frage des Backends, sondern der Architektur.
Quelle: GitHub Blog