Fundierte Artikel über Technologien, die das Kommende formen.

Wenn Git bricht: Monorepo-Lektionen aus 30.000 Merges am Tag

Git-Monorepo-Skalierung scheitert bei extremen Merge-Raten auf vorhersehbare Weise. Lektionen zu Clients und serverseitigen Commits.

Illustration von Roboter-Kurieren, die an einem Kassentisch in einem riesigen Archiv voller Dateikisten drängeln.
Git geht davon aus, dass jeder Client das gesamte Archiv mit sich trägt. Das ist in Ordnung, bis Tausende Clients pro Sekunde anstehen.

Wenn du ein Git-Monorepo betreibst, gibt es irgendwo in deiner Zukunft eine Zahl, ab der die Sache aus dem Ruder läuft. Bei Anthropic lag diese Zahl bei etwa 30.000 Merges am Tag, einer 100 GB großen Historie des Hauptzweigs und grob 25 Millionen CI-Checkouts täglich. Naiv abgerufen ergibt das über 600 Petabyte Git-Traffic. Und zwar jeden einzelnen Tag. Ihre Antwort war, in fünf Monaten ein eigenes Versionskontrollsystem zu schreiben. Deine Antwort sollte das nicht sein. Aber die Fehler, die sie unterwegs gemacht haben, sehe ich bei Teams auch schon in einem Zehntel dieser Größenordnung, also gehen wir sie durch.

Fehler eins: einen zentralisierten Workflow behandeln wie einen verteilten

Git setzt im Kern auf Dezentralisierung. Jeder Clone ist ein vollständiges Repository mit kompletter Historie, vollständigem Objektspeicher und voller Funktionalität offline. Diese Wette hat Git die Welt erobern lassen, und für die meisten Repos stimmt sie auch heute noch. Aber hier kommt der Punkt, den fast niemand zugibt: Fast niemand nutzt Git tatsächlich so. Deine Organisation hat einen maßgeblichen Remote bei GitHub oder GitLab. Deine Entwickler pushen und pullen ständig. Deine CI-Runner sind per Definition online. Du betreibst ein zentralisiertes System im verteilten Kostüm und zahlst die Kosten eines verteilten Systems: Jeder CI-Job zieht eine Historie herunter, die er nie lesen wird, und jede Agenten-Sandbox baut Packfiles auf, die sie nicht braucht.

Das ist die erste echte Lektion aus Anthropics Neuentwurf. Als sie einen Schritt zurücktraten, merkten sie, dass ihre Clients „Agenten und Jobs sind, die per Definition online sind“. Also haben sie um einen Server herum entworfen, der immer erreichbar ist. Der Client wurde bewusst dumm gehalten: ein begrenzter lokaler Cache, eine Arbeitskopie auf der Festplatte, ein Dateisystem-Watcher. Der Server beantwortet Blame-Anfragen, berechnet Diffs und führt Merges aus. Zu diesem Schluss ist auch Google mit Piper gekommen und Meta mit Sapling. Das ist keine Absage an Git, sondern die Erkenntnis, dass sich die Architektur an der tatsächlichen Arbeitslast orientieren sollte und nicht an der, für die das Werkzeug vor zwanzig Jahren entworfen wurde. Ich sehe dieses Muster auch bei Security-Tooling: Teams setzen eine offlinefähige Agenten-Architektur ein und betreiben sie dann in einer Umgebung, in der Offline-Betrieb schlicht nie vorkommt. Dann wundern sie sich, warum ihre Sync-Schicht ein Albtraum ist.

Die Konsequenz für dein Team lautet nicht „Bau ein eigenes VCS“. Sie lautet: Prüf deine Annahmen. Schreib auf, welche Git-Eigenschaften du tatsächlich brauchst. Offline-Arbeit? Echte Forks? Kryptografisch signierte Commits? Wenn die ehrliche Antwort lautet „ein Remote, immer online, CI erledigt alles“, dann hör auf, für einen verteilten Workflow zu optimieren, den du gar nicht hast, und optimiere stattdessen die Kosten für Checkouts und Abfragen.

Fehler zwei: Skalierungsprobleme mit Mirrors und Seeds übertünchen

Jedes Team mit großen Repositories durchläuft dieselben Phasen des Frusts. Zuerst flache Clones. Dann Sparse Checkouts und Promisor-Remotes. Danach interne Git-Mirrors, anschließend Snapshot-„Seeds“ im Blob-Storage, damit CI-Jobs von etwas starten können, das dem Tip-of-Main nahekommt. Anthropic hat all das ausprobiert, auf dem Höhepunkt mit mehreren hundert internen Mirrors. Und der Fehlermodus ist immer derselbe: Die Workarounds scheitern nicht laut, sie verrotten. Seeds driften zwischen den Refreshes immer weiter vom Tip ab. Partielle Clones lassen git blame und git log --follow auf verwirrende Weise scheitern. Irgendwer bekommt bei einem Bisect stillschweigend falsche Ergebnisse, weil die Historie abgeschnitten ist. Das merkt niemand, bis es im Incident-Review auffällt.

Genau dieser letzte Punkt hält mich nachts wach. Fehler bei der Korrektheit partieller Historien sind genau die Art subtiler Falschheit, die in der Lücke zwischen dem lebt, was du glaubst, dass das System garantiert, und dem, was es tatsächlich tut. Ein flacher Clone meldet keinen Fehler, wenn du Blame jenseits seiner Tiefe anforderst. Er liefert einfach eine unvollständige Antwort. Wenn deine automatisierte Revert-Pipeline dieser Antwort vertraut, hast du eine Maschine gebaut, die selbstbewusst die falsche Änderung rückgängig macht.

  • Flache Clones brechen historienabhängige Werkzeuge (Blame, Bisect, Merge-Base) auf eine Weise, die still scheitert statt laut.
  • Snapshot-Seeds driften vom Tip-of-Main ab; je höher deine Merge-Rate, desto schneller der Drift, sodass der Workaround genau dann schlechter wird, wenn die Last steigt.
  • Mirror-Flotten vervielfachen die betriebliche Angriffsfläche: Hunderte zustandsbehaftete Kopien, die unabhängig voneinander hinterherhinken, korrupt werden oder den Speicherplatz ausgehen lassen können.
  • Jeder Workaround, der die Historien-Semantik berührt, braucht einen Regressionstest, sonst beißt er dich während eines Incidents.

Die Lösung auf Anthropics Skala war strukturell: Irgendwo existiert keine vollständig materialisierte Kopie des Repos mehr, auch nicht auf dem Server, und alles ist inhaltsadressiert und horizontal cachebar. In deinem Maßstab ist die Lösung bescheidener: Miss, wie viele Maschinen vollständige oder nahezu vollständige Kopien deines Repos halten, und sei ehrlich über den Drift und die Fehlermodi jeder einzelnen.

Fehler drei: jeder CI-Job pflegt seine eigene Kopie des Repos

Eine Frage, die sich lohnt, bei deiner CI-Flotte zu stellen: Wie viele dieser Jobs brauchen überhaupt eine Arbeitskopie? Anthropics Antwort nach dem Aufbau ihres Systems lautete: „deutlich weniger, als wir angenommen hatten“. Ein Job, der den Diff zwischen einem Commit und seiner Merge-Base braucht, das Blame für eine Datei oder die Liste der Commits, die einen Pfad berühren, kann den Server direkt über eine API fragen. Kein Clone, kein Fetch, keine Platte. Ihre automatisierten Bisect- und Revert-Pipelines wären „auf dieser Skala nicht praktikabel“ ohne diesen günstigen programmatischen Zugriff. Ich glaube ihnen das, denn ich habe gesehen, wie Teams versucht haben, dieselben Pipelines auf lokalen Clones laufen zu lassen, und dabei in Fetch-Zeiten ertrunken sind.

Dieses Prinzip gilt weit über Source Control hinaus. Behandle den Checkout als die teure Ausnahme, nicht als Standard. Wenn ein Job tatsächlich Dateien braucht, senken Sparse- und virtualisierte Checkouts die Kosten um Größenordnungen, weil nur die angeforderten Verzeichnisse oder sogar nur die tatsächlich gelesenen Dateien materialisiert werden. Anthropics Fetch-Protokoll hat die Git-Aushandlung komplett gestrichen: Der Client fordert gezielt die unveränderlichen Trees und Blobs an, die er braucht, jeder Cache kann antworten, und das Ganze verhält sich wie ein gestuftes CDN statt wie ein intelligenter Server, der für jeden Client Packfiles berechnet. Wenn du jemals erlebt hast, wie ein Git-Server unter CI-Last schmilzt, weißt du, warum das wichtig ist. Die Erzeugung von Packfiles ist CPU-intensiv, eben weil das Protokoll den Server bittet, für jeden Client ein Delta gegenüber dem zu berechnen, was dieser bereits hat.

Wenn deine Clients immer online sind und es genau eine maßgebliche Kopie gibt, ist jeder lokale Clone, den du pflegst, ein Cache, für den du den vollen Preis zahlst und trotzdem Cache-Semantik bekommst.

Fehler vier: zulassen, dass die Kosten eines Commits mit der Repo-Größe wachsen

Die spannendste Ingenieursentscheidung in ihrem Design ist das Changeset. Ein Git-Commit zeigt auf einen Tree, der den gesamten Repository-Zustand repräsentiert. Ihr Changeset dagegen speichert nur die Liste der Dateien, die sich gegenüber dem ersten Parent geändert haben, dazu inhaltsadressierte Zeiger in den Blob-Storage, angehängt an ein Write-Ahead-Log. Keine Packfiles zum Annehmen, keine Locks, keine serverübergreifende Koordination. Refs, also Branches und Tags, werden bewusst in den Hintergrund gedrängt, weil „globaler Zustand, auf den sich jeder Server einigen muss, ein drohendes Skalierungsproblem ist“. Einen Ref zu verschieben ist ein Compare-and-Swap auf einer einzelnen Spanner-Zeile, entkoppelt von der Erstellung des Commits.

Stilisierte Illustration von Paketen auf einem Förderband, die in ein leuchtendes Log und abgeleitete Datenstrukturen fließen.
Nur die Änderungen werden in einem Write-Ahead-Log festgehalten; alles Übrige wie Blame, Pfadhistorie und Graph-Indizes wird daraus lazy abgeleitet.

Die Begleitidee sind „abgeleitete Daten“: Blame-Indizes, Pfadhistorie, Skip-Listen des Commit-Graphen, alle rekursiv aus den Parents jedes Changesets berechnet, und alles über einen einzigen generischen Mechanismus. Man berechnet eager oder lazy, je nach Typ. Neue Indextypen lassen sich hinzufügen, ohne den Client anzufassen. Das ist ein richtig gutes Muster für jedes event-sourcing-basierte System. Ich habe dieselbe Form auch für Audit-Log-Pipelines empfohlen, wo das append-only Log die Wahrheit ist und jede Abfragestruktur eine neu aufbaubare Projektion. Sobald jemand eine Projektion außerhalb dieses Mechanismus von Hand pflegt, hast du Drift.

Ein Vorbehalt aus der Security-Perspektive: Die Kommentatoren, die sich vor Zentralisierung sorgen, haben einen Punkt, den man ernst nehmen sollte. Ein einzelnes serverseitiges System, in dem Commits, Merges und Historienabfragen allesamt liegen, ist ein hochattraktives Ziel. Und KI-Agenten mit Schreibrechten auf „das eine Repo, das alle beherrscht“ bündeln das Risiko auf ungute Weise. Anthropic hat parallel ein „umfassendes Source-Control-IAM-System“ gebaut, und die Details sind wichtiger als die Datenstrukturen. Wenn du zentralisierst, ändert sich deine Rechnung zum Blast Radius. Plane von Tag eins an Isolation, agentenspezifische Credentials und Audit ein, nicht erst im dritten Quartal. Teams, die zustandsbehaftete Agenten gegen gemeinsame Infrastruktur betreiben, kennen diesen Schmerz bereits.

Fehler fünf: annehmen, 30.000 Merges am Tag bedeuten 30.000 gute Entscheidungen am Tag

Die Ingenieursleistung ist beeindruckend. Die offene Frage ist, was durch sie hindurchfließt. Ein Praktiker hat es unverblümt formuliert: Jedes Mal, wenn er aufhört, KI-generierten Code zu reviewen, „finde ich später janky Mist. Nicht jedes Review findet janky Mist, aber oft genug, dass ich nicht aufhören kann, alles zu reviewen“. Multipliziere diese Trefferquote mit 30.000 Merges am Tag, und du verstehst, warum Anthropic auch „neuartige Wege zur Verwaltung und Übernahme großer Mengen reviewten Codes“ bauen musste: die Review-Oberfläche, die Landing-Queue, die automatisierten Revert-Pipelines. Das VCS war nicht das Produkt. Die Landing-Pipeline war es. Source Control wurde erst zum Engpass, nachdem die Review-Kapazität davor zum Engpass geworden war.

In ihren Kriegsgeschichten steckt außerdem ein offenes Detail: Ingenieure im Projekt beschrieben einen großen Teil ihrer Rolle als das Sagen von „Nein“ gegenüber dem Coding-Agenten. Die handgeschriebene Prototyp-Ingestion-Pipeline war in Ordnung, bis ein neueres Modell sie mit 20-fach geringerem Speicherbedarf und 4-fach höherer Geschwindigkeit umschrieb. Dafür brauchte es aber Menschen mit Geschmack, die entschieden, was akzeptiert wird. Das deckt sich mit allem, was ich gesehen habe: KI verschiebt die Arbeit der Ingenieure vom Schreiben zum Reviewen, und der Review-Durchsatz ist inzwischen die knappe Ressource. Wir haben bereits geschrieben, wie jede Review-Ebene ein Team langsamer macht. Das Schwierige ist: Bei agentengeneriertem Code ist es auch nicht sicher, Review-Ebenen einfach zu streichen. Du brauchst bessere Review-Werkzeuge, nicht weniger Review.

Was du tatsächlich tun solltest

Du bist nicht Anthropic. Du hast nicht deren Token-Budget, das sie selbst zugeben, und du hast keine 25 Millionen täglichen Checkouts. Aber KI-Coding-Tools treiben die Merge-Raten bei allen nach oben, und die Druckpunkte treten in einer vorhersehbaren Reihenfolge auf. Hier ist die Reihenfolge der Schritte, die ich befolgen würde, nach Priyas Gesetz des geringsten Bedauerns:

  1. Zuerst messen. Zähle die täglichen Checkouts, die pro CI-Job abgerufenen Bytes und wie viele Maschinen vollständige Repo-Kopien halten. Du hast wahrscheinlich ein Verschwendungsproblem, bevor du ein Skalierungsproblem hast.
  2. Unnötige Checkouts abschaffen. Verlagere Metadatenfragen (Diffs, Blame, Abstammung) in APIs, also GitHub- oder GitLab-APIs oder einen schlanken internen Service, bevor du in Infrastruktur investierst.
  3. Sparse- und partielle Clones sicher machen. Teste deine Bisect-, Blame- und Revert-Werkzeuge ausdrücklich gegen flache Clones, oder verbiete sie in der Automatisierung.
  4. CI standardmäßig auf serverseitige Abrufe unveränderlicher Objekte umstellen, wo immer es geht, und Mirror-Flotten als technische Schulden mit einem zinseszinsartig wachsenden Zinssatz behandeln.
  5. In die Landing-Pipeline investieren, nicht ins Repo-Format. Review-Kapazität, Merge-Queues und automatisiertes Revert sind die Stellen, an denen Entwicklung im Agenten-Maßstab zuerst bricht.
  6. Erst dann alternative VCS-Architekturen in Betracht ziehen. Sapling ist Open Source und bietet dir den Großteil der Client-Server-Vorteile, ohne dass du selbst ein System schreiben musst.

Die tiefste Lektion hier hat eigentlich gar nicht mit Git zu tun. Jedes Werkzeug kodiert Annahmen über seine Arbeitslast: offline arbeitende Menschen, ein Commit nach dem anderen, Code, der langsamer geschrieben wird, als er reviewt werden kann. Und genau diese Annahmen werden gerade von Agenten verletzt, in der Infrastruktur, im Review und an den Sicherheitsgrenzen. Die Teams, die am Ende vorne liegen, werden nicht diejenigen sein, die alles von Grund auf neu bauen. Es werden diejenigen sein, die bemerkt haben, welche Annahmen zuerst gebrochen sind, und genau diese repariert haben.