X hat am 4. November einen Engineering-Beitrag veröffentlicht, der den Umbau von x.com beschreibt — und die erste Zahl darin ist die beste Zusammenfassung. Auf einer gedrosselten Verbindung brauchte die alte Web-App mehr als 22 Sekunden bis zum ersten Post. Die neue schafft es in 2,7.

Der Beitrag von Yagiz Nizipli, Principal Engineer, erklärt, warum. x.com hatte drei Server: rweb, das die App im Browser rendert, rweb SSR für Suchmaschinen-Crawler und Jetfuel SSR für indexierte, ausgeloggte Seiten wie Events, Spaces, Communities, Trends und Artikel. Jeder machte seine Arbeit gut; das Problem war, dass eine einzelne URL auf drei verschiedene Arten gebaut werden konnte. Jede SEO-Korrektur musste in drei Codebasen landen, und ausgeloggte Besucher — also Menschen — bekamen den langsamsten Pfad: erst die App-Hülle, dann JavaScript, dann einen GraphQL-Aufruf, dann das Rendering. Crawler bekamen fertiges HTML, Menschen nicht.

Die neue App bedient alle drei Zielgruppen aus einem einzigen Server. Auf Vite mit Rolldown, React mit Streaming-Server-Rendering und Suspense, Fastify als Request-Pipeline, Node.js 26 in mehreren Worker-Threads über Platformatic Watt, Relay mit GraphQL und einer eigenen Komponentenbibliothek namens XDS. TypeScript wird von `tsrs` geprüft, einer Rust-Portierung des TypeScript-Compilers.

Wie der Server entscheidet

Menschen bekommen gestreamtes HTML: die Hülle sofort, jeder Seitenabschnitt, sobald seine Daten da sind, und jede GraphQL-Antwort genau einmal im Dokument, damit der Browser sie ohne erneuten Aufruf wiederverwenden kann. Verifizierte Suchmaschinen-Crawler bekommen ein vollständiges Dokument, das nie hydratisiert — ohne Client-Bundle, ohne Router-State, ohne Platzhalter. Die Verifikation läuft über die IP-Adresse am Edge, nicht über den User-Agent: Ein Header-Ansatz hätte laut X auch Lighthouse-Läufe in den teuren Pfad geschickt und jedem erlaubt, sich per Header den teureren Weg zu nehmen. Link-Preview-Bots von Slack, iMessage, Discord, WhatsApp und anderen bekommen nur Head-Tags und Lade-Daten, nicht den Seitenkörper. Alles, was sonst als Crawler auftritt, bekommt exakt das, was ein ausgeloggter Besucher bekommt.

Die Zahlen

Ein Laborvergleich vom 3. November, eingeloggt in dasselbe Konto, Slow-4G-Profil (150 ms RTT, 1,6 Mbit/s Downstream, leerer Cache, keine Service Worker): Die neue App zeigt die Seite nach 2,4 Sekunden und den ersten Post nach 2,7; rweb zeigt den Splash nach 1,0 Sekunden und den ersten Post nach 23,9. Über mehrere Seiten gemessen sind Profile und Posts auf langsamen Verbindungen 8- bis 13-mal früher da. Die Home-Timeline gewinnt am wenigsten — dort rendert die neue App ebenfalls erst im Browser —, mit 14,1 statt 24,6 Sekunden. An JavaScript vor dem ersten Post lädt rweb 3,0 bis 3,6 MB komprimiert; die neue App null bei Profilen und Posts, weil der Post im HTML steht, und 1,9 MB auf Home. Über eine ganze Seite sind es 1,3 bis 2,3 MB gegen 4,1 bis 4,8 MB.

Der Infrastruktur-Effekt ist der eigentliche Gewinn. Die ausgeloggte rweb-Flotte schrumpfte pro Rechenzentrum von 1.400 auf 700 Instanzen, rweb SSR von 2.400 auf 1.200 und wurde dann abgeschaltet. Die Crawler-Migration lief zuerst — Jetfuel SSR ging am 1. August außer Betrieb, Anfang September bediente die neue App den gesamten Crawler-Traffic und rweb SSR wurde abgeschaltet; allein dessen Deploy- und Build-Pfad löschte fast 36.000 Zeilen aus rweb. Ab Mitte September verschwanden auch Jetfuels Rendering- und Bilddienste.

Weil Suchmaschinen alles bemerken, behandelte das Team die alten Server als Spezifikation und schrieb ein Parity-Skript, das denselben Pfad als verifizierter Googlebot von beiden Servern abrief und Statuscode, Redirect, Titel, Description, Canonical, Robots-Tags, Open-Graph- und X-Card-Tags, die Zahl der Sprach-Alternates und die Typen strukturierter Daten verglich. Googlebot liest nur die ersten 2 MB einer Antwort — lange Threads lagen bei 2,2 bis 3,5 MB, die Antworten dahinter gingen verloren. Drei Änderungen behoben das: ein Icon-Sprite statt sieben eingebetteter Aktions-Icons pro Post (165 KB gespart auf @elonmusks Profil, 340 KB in einem langen Thread), deduplizierte Foto-Strukturdaten (450 KB) und das Entfernen von Ausgaben, die nur nach JavaScript-Ausführung relevant sind (54 KB).

Fünf Menschen, viel Agentenarbeit

Der erste Entwurf entstand in weniger als sechs Monaten mit fünf Ingenieuren, die laut Beitrag mit KI-Coding-Agenten arbeiteten; seit Ende September haben mehr als 20 Personen Commits beigetragen. Interessanter als die Geschwindigkeit ist, wie sie abgesichert wurde: eine `AGENTS.md` im Repo-Wurzelverzeichnis mit Regeln zu benannten Exporten, verbotenen Barrel-Dateien, Validierung externer Daten an Vertrauensgrenzen und der Vorgabe, dass nichts committet wird, bevor Lint, Typecheck und die relevanten Tests grün sind. Über 25 wiederkehrende Workflows liegen als Skills neben dem Code. Und eigene oxlint-Plugins prüfen Relay-Nutzung, Tailwind-Klassen, Tests und Übersetzungen — inklusive eines „Anti-Slop"-Plugins, das Muster zurückweist, die Agenten gern produzieren: verkettete Typ-Assertions, als `unknown` typisierte Parameter, konditionale Spreads leerer Objekte.

Die Migration passte zu Agenten, weil die alten Server funktionierende Spezifikationen waren: Ein Agent konnte lesen, wie rweb SSR eine URL behandelte, das Verhalten portieren und einen Test schreiben, der es festnagelt. Nizipli beschreibt die Verschiebung offen: weniger Code schreiben, mehr entscheiden, was das System tun soll, und dann im echten Browser prüfen, ob es das tut.

Der Rollout läuft über Edge-Routing und einen Feature-Switch, Land für Land, und jede Gruppe lässt sich zurückstellen. rweb bleibt aktiv, bis alle umgezogen sind — was die alte Kritik an Rewrites adressiert, die Nizipli selbst zitiert: große Neuschreibungen scheitern, weil das alte Produkt weiterläuft. Hier lief es weiter, und die Agenten verkürzten das Rennen. Im ersten Land werden eingeloggte Nutzer zudem in einem Experiment aufgeteilt, damit sich beide Apps auf echtem Traffic vergleichen lassen.