Zum Inhalt springen
Projekt besprechen

25. September 2026

Zwei grüne PRs können zusammen ein rotes System ergeben

Software ArchitectureMulti-Agent DevelopmentCI/CDGitIntegrationstests

Git beantwortet eine sehr begrenzte Frage zuverlässig: Lassen sich zwei Änderungen auf denselben Dateistand anwenden? In modernen Entwicklungsprozessen ist das nicht mehr die entscheidende Frage.

Wenn mehrere Menschen oder Agenten parallel in eigenen Worktrees arbeiten, können zwei Pull Requests unabhängig grün sein, konfliktfrei mergen und dennoch gemeinsam ein defektes System erzeugen. Der Schaden reicht von falschen Fachentscheidungen über Sicherheitslücken bis zu schleichend inkonsistenten Daten.

Das Problem ist kein Versagen von Git. Es ist ein Kategorienfehler: Wir behandeln semantische Integration wie eine Variante des Text-Mergens.

Der Unterschied zwischen Merge-Konflikt und semantischem Konflikt

Ein Git-Konflikt entsteht, wenn zwei Änderungen nicht eindeutig auf Text- oder Zeilenebene zusammengeführt werden können. Git sieht dabei weder Architektur noch Fachlichkeit noch Laufzeitverhalten.

Ein semantischer Konflikt liegt dagegen vor, wenn Änderungen jeweils für sich korrekt erscheinen, aber zusammen eine Annahme verletzen. Typische verletzte Annahmen sind:

  • ein API-Vertrag,
  • eine Zustandsinvariante,
  • eine Reihenfolge in einem Prozess,
  • ein Berechtigungsmodell,
  • eine Datenmigration,
  • ein Betriebsvertrag wie Retry-, Timeout- oder Idempotenzverhalten.

Die gefährlichsten Konflikte berühren nicht einmal dieselbe Datei.

Ein sauberer Drei-Wege-Merge ist kein Beweis für ein korrekt integriertes System.

Ein konkretes Beispiel: Zwei richtige Änderungen, eine falsche Abrechnung

Nehmen wir einen Dienst zur Rechnungsstellung. Ein Agent bearbeitet PR A, ein anderer PR B. Beide starten vom selben main-Commit und arbeiten in getrennten Worktrees.

PR A: Rabatte zentral berechnen

Der Agent verschiebt die Rabattlogik aus dem Controller in einen Domain-Service. Dabei wird eine Invariante eingeführt: Preise im Invoice-Objekt sind künftig bereits rabattiert.

const discountedTotal = calculateDiscountedTotal(lines, customer);
return Invoice.create({ lines, total: discountedTotal });

Die Unit-Tests von PR A prüfen die Rabattberechnung. Alles grün.

PR B: Steuerberechnung anpassen

Der zweite Agent implementiert eine neue Steuerregel. Er geht – entsprechend dem bisherigen Verhalten – davon aus, dass invoice.total den Betrag vor Rabatt enthält, und zieht den Rabatt in der Steuerberechnung ab.

const taxableAmount = invoice.total.minus(invoice.discount);
return taxableAmount.multiply(taxRate);

Auch PR B ist grün. Die Tests verwenden Fixtures im alten Datenmodell und prüfen die neue Steuerregel isoliert.

Git sieht keinen Konflikt: unterschiedliche Dateien, unterschiedliche Zeilen. Nach dem Merge wird der Rabatt jedoch zweimal berücksichtigt. Rechnungen enthalten zu wenig Steuer.

Das ist kein Code-Konflikt. Es ist ein Konflikt über die Bedeutung von invoice.total.

Warum parallele Agenten-Streams das Problem verstärken

Parallele Entwicklung ist nicht neu. Neu ist die Geschwindigkeit und Anzahl unabhängiger Änderungsströme. Ein einzelner Entwickler hält einen Teil des impliziten Systemmodells im Kopf. Bei mehreren Agenten existiert dieses Modell oft nur fragmentiert in Tickets, Prompts, Code und Tests.

Agenten beschleunigen die Umsetzung lokaler Aufgaben. Gleichzeitig verstärken sie drei bekannte Risiken.

1. Lokale Optimierung statt Systemverständnis

Ein Agent löst in der Regel den explizit formulierten Auftrag. Wenn der Auftrag lautet „Rabattlogik zentralisieren“, untersucht er möglicherweise die betroffenen Aufrufer, aber nicht jede implizite Bedeutung des resultierenden Datenfelds.

Das ist keine besondere Schwäche eines Modells. Auch Menschen machen genau diesen Fehler unter Zeitdruck. Bei Agenten fällt er häufiger an, weil mehrere lokale Optimierungen gleichzeitig stattfinden.

2. Veraltete Annahmen durch gemeinsame Ausgangspunkte

Zwei Worktrees basieren häufig auf demselben Commit. PR B kennt die neue Invariante aus PR A nicht, weil sie beim Start noch nicht existierte. Selbst ein späteres Rebase löst das Problem nicht automatisch: Rebase aktualisiert Dateien, nicht Annahmen.

3. Tests bestätigen oft nur den eigenen Ausschnitt

Unit-Tests sind absichtlich lokal. Das macht sie schnell und präzise, aber auch blind für Wechselwirkungen außerhalb ihrer Grenze. Wenn jeder Stream nur seine eigene Änderung testet, bleibt die Schnittstelle zwischen den Änderungen ungetestet.

Die eigentliche Integrationsfläche liegt in Verträgen

Teams unterschätzen semantische Konflikte, wenn sie Integrationsfläche als Menge gemeinsam geänderter Dateien definieren. Die relevante Fläche ist größer:

  • gemeinsame Domänenobjekte und ihre Bedeutungen,
  • Events und deren Reihenfolge,
  • Datenbankschemata und Migrationszustände,
  • API- und Message-Schemas,
  • Feature Flags und ihre Kombinationen,
  • Caches, Nebenläufigkeit und Retry-Verhalten,
  • Observability-Signale, auf die Betrieb oder Downstream-Systeme reagieren.

Je stärker ein System über implizite Verträge gekoppelt ist, desto mehr grüne PRs können gemeinsam rot werden.

Ein guter Indikator ist diese Frage: Welche Annahme müsste ein anderer Entwickler kennen, damit seine Änderung weiterhin korrekt bleibt? Wenn die Antwort nicht maschinenprüfbar oder zumindest explizit dokumentiert ist, existiert ein semantisches Konfliktrisiko.

Was eine CI-Pipeline leisten kann – und was nicht

CI ist notwendig, aber ihre Aussagekraft hängt von der geprüften Hypothese ab.

Eine Pipeline für jeden einzelnen PR beantwortet: „Ist dieser Branch gegen seinen definierten Basisstand konsistent?“ Sie beantwortet nicht: „Bleibt das System unter der Menge aller gerade mergebaren Änderungen konsistent?“

Auch ein Merge-Queue-System ist kein vollständiges Gegenmittel. Es reduziert das Risiko, indem es PRs gegen einen aktuellen, vorgeschalteten Stand testet. Es erkennt aber nur Eigenschaften, die Tests, Checks oder statische Regeln ausdrücken.

Die richtige Konsequenz ist nicht, CI durch manuelle Reviews zu ersetzen. Die Konsequenz ist, CI um semantisch relevante Prüfungen zu erweitern.

Ein praktisches Schutzmodell in fünf Schichten

Kein einzelner Mechanismus löst das Problem. Robuste Teams kombinieren Architektur, Arbeitsprozess und automatisierte Nachweise.

1. Invarianten explizit machen

Die wichtigste Information gehört nicht nur in ein Ticket oder einen Prompt, sondern in eine dauerhaft auffindbare Form.

Für kritische Domänenbegriffe helfen kurze Architekturentscheidungen oder Vertragsdokumente. Nicht als allgemeine Wiki-Prosa, sondern konkret:

## Invoice.total
 
- Enthält die Summe nach positionsbezogenen Rabatten.
- Enthält keine Steuer.
- `Invoice.discount` ist nur ein Ausweisungswert und darf nicht erneut abgezogen werden.
- Steuerberechnung verwendet `Invoice.total` direkt.

Diese Dokumentation allein verhindert keinen Fehler. Sie schafft aber einen referenzierbaren Vertrag für Implementierung, Review und Tests.

Bei Agenten-gestützter Entwicklung sollte die relevante Invariante Teil des Arbeitsauftrags sein. Ein Prompt ohne Architekturkontext produziert häufig plausiblen, lokal korrekten Code mit falscher Systembedeutung.

2. Verträge als Code testen

Wichtige Invarianten brauchen Tests an der Grenze, an der sie verletzt werden können.

Im Rechnungsbeispiel genügt kein Test für Rabatt und kein Test für Steuer allein. Es braucht einen Test für die Kombination:

it('berechnet Steuer auf den bereits rabattierten Nettobetrag', () => {
  const invoice = createInvoice({
    lines: [line({ net: money('100.00') })],
    customerDiscount: percent(10),
  });
 
  expect(invoice.total).toEqual(money('90.00'));
  expect(calculateTax(invoice, percent(19))).toEqual(money('17.10'));
});

Je nach Grenze eignen sich unterschiedliche Prüfungen:

  • Consumer-Driven Contract Tests für APIs,
  • Schema-Kompatibilitätsprüfungen für Events,
  • Property-based Tests für Domäneninvarianten,
  • Integrationstests gegen echte Persistenz und Queues,
  • End-to-End-Tests für wenige geschäftskritische Abläufe.

Der Maßstab ist nicht möglichst viel Testcode. Der Maßstab ist: Werden die Annahmen getestet, an denen unabhängige Änderungen vorbeilaufen können?

3. Änderungen nach Kopplung schneiden, nicht nach Dateigrenzen

„Frontend“, „Backend“ und „Datenbank“ sind oft schlechte Zuschnitte für parallele Arbeit. Sie erzeugen PRs, die technisch getrennt aussehen, aber fachlich dieselbe Invariante verändern.

Besser sind vertikale, aber koordiniert integrierbare Schnitte. Wenn zwei Änderungen denselben Vertrag anfassen, gibt es meist drei Optionen:

  1. Sequenzieren: Erst den Vertrag einführen, dann seine Konsumenten umstellen.
  2. Kompatibel erweitern: Neue Felder oder Event-Versionen hinzufügen, alte Bedeutung zunächst erhalten.
  3. Gemeinsam integrieren: Änderungen bewusst in einem Branch oder einer temporären Integrationsumgebung zusammenführen.

Die richtige Wahl hängt von Risiko und Änderungsvolumen ab. Entscheidend ist, die gemeinsame semantische Fläche vor dem Start zu erkennen.

4. Integration als eigenen Arbeitsschritt behandeln

In vielen Teams ist Merge gleich Integration. Das funktioniert nur bei geringer Kopplung.

Für größere Vorhaben lohnt ein expliziter Integrations-Branch oder eine Merge Queue mit einem gestapelten Kandidatenstand. Dort laufen nicht nur Standardtests, sondern gezielte Szenarien für die betroffene Fähigkeit.

Ein schlanker Ablauf kann so aussehen:

  1. Änderungen werden in unabhängigen Worktrees umgesetzt.
  2. Jeder PR benennt geänderte Verträge, Invarianten und Migrationsannahmen.
  3. Die Merge Queue testet den PR auf dem aktuellen Kandidatenstand.
  4. Bei Änderungen an kritischen Verträgen läuft ein zusätzlicher Integrations-Testplan.
  5. Nach dem Rollout prüfen Metriken und fachliche Kontrollwerte die reale Wirkung.

Der vierte Punkt muss nicht bürokratisch sein. Ein kurzer Abschnitt in der PR-Vorlage reicht oft:

## Semantische Integrationsfläche
 
- Geänderte Verträge/Invarianten:
- Betroffene Producer oder Consumer:
- Kombinationen mit offenen PRs geprüft:
- Zusätzliche Integrationschecks:

Leere Antworten sind ebenfalls wertvoll: Sie machen sichtbar, dass niemand eine Integrationsfläche erkannt hat.

5. Deployment entkoppeln und beobachtbar machen

Nicht jeder semantische Konflikt lässt sich vor Produktion beweisen. Deshalb gehört sichere Auslieferung zur Integrationsstrategie.

Nützlich sind insbesondere:

  • rückwärtskompatible Datenbankmigrationen nach dem Expand-Contract-Muster,
  • Feature Flags mit klar definierten Flag-Kombinationen,
  • Canary- oder gestaffelte Deployments,
  • fachliche Metriken neben technischen Fehlerquoten,
  • schnelle, getestete Rollback- oder Forward-Fix-Wege.

Eine 100-prozentige HTTP-200-Rate beweist nicht, dass Rechnungen korrekt sind. Für kritische Prozesse braucht es Domänensignale: ungewöhnliche Rabattquoten, negative Salden, fehlende Events, Abweichungen zwischen Buchungs- und Rechnungssumme.

Speziell für Multi-Agent Development: Orchestrierung vor Parallelität

Mehr Agenten bedeuten nicht automatisch mehr Durchsatz. Ohne Koordination steigt zunächst die Zahl der Integrationsaufgaben.

Ein wirksames Muster ist die Trennung von drei Rollen:

  • Architektur- oder Planungsinstanz: identifiziert Verträge, Abhängigkeiten und die sinnvolle Reihenfolge.
  • Ausführende Agenten: bearbeiten möglichst klar abgegrenzte Änderungen in Worktrees.
  • Integrationsinstanz: prüft den kombinierten Stand, aktualisiert Annahmen und entscheidet über Merge-Reihenfolge.

Das muss keine aufwendige Agentenplattform sein. Es kann auch ein menschlicher Tech Lead mit einer guten PR-Vorlage und einer Merge Queue sein. Wichtig ist die Verantwortlichkeit: Jemand muss nicht nur fragen, ob jeder PR korrekt ist, sondern ob die Änderungen zusammen korrekt sind.

Für Agenten-Aufträge sind diese Angaben besonders nützlich:

  • aktuelle und gewünschte Invarianten,
  • erlaubte und verbotene Änderungen an öffentlichen Verträgen,
  • bekannte parallele Arbeiten,
  • relevante Testbefehle und Integrationsszenarien,
  • Kriterien, ab wann der Agent Arbeit stoppen und einen Konflikt melden soll.

Ein guter Stop-Grund lautet beispielsweise: „Die Aufgabe verändert die Bedeutung eines Feldes, das von mehreren Modulen gelesen wird. Kein kompatibler Übergang ist spezifiziert.“ Das ist kein Fehlschlag, sondern ein wertvolles Architektur-Signal.

Was im Code Review anders geprüft werden sollte

Reviews konzentrieren sich verständlicherweise auf Lesbarkeit, Fehlerbehandlung und Stil. Bei paralleler Entwicklung braucht das Review zusätzlich Fragen zur Bedeutung:

  • Welche bestehende Annahme ändert dieser PR?
  • Ist die Annahme dokumentiert oder nur im Code verteilt?
  • Wer konsumiert dieses Feld, Event oder Verhalten noch?
  • Welche offenen PRs oder geplanten Änderungen berühren denselben Vertrag?
  • Testet die Suite die neue Regel isoliert oder ihre Wirkung im relevanten Ablauf?
  • Ist die Änderung rückwärtskompatibel, und wenn nicht: Wie wird der Übergang koordiniert?

Diese Fragen sind besonders wichtig bei scheinbar harmlosen Refactorings. Eine Umbenennung oder Verlagerung von Logik kann eine Bedeutung verschieben, ohne dass sich ein API-Typ ändert.

Ein realistisches Zielbild

Semantische Konflikte lassen sich nicht vollständig eliminieren. Ein Systemmodell bleibt immer unvollständig, und jede Prüfung hat Kosten. Das Ziel ist daher nicht perfekte Vorhersage.

Das Ziel ist, gefährliche Annahmen früh sichtbar zu machen, kritische Verträge maschinenprüfbar zu machen und Integration nicht dem Zufall der Merge-Reihenfolge zu überlassen.

Für die Praxis reichen oft diese ersten Schritte:

  1. Eine PR-Vorlage um „geänderte Invarianten und Verträge“ ergänzen.
  2. Für die drei kritischsten Geschäftsabläufe echte Integrations- oder Contract-Tests ergänzen.
  3. Eine Merge Queue oder zumindest einen Test des aktuellen Merge-Kandidaten einsetzen.
  4. Agenten-Aufträge mit Architekturkontext und expliziten Stop-Kriterien versehen.
  5. Fachliche Metriken für Fehler definieren, die technisch erfolgreich aussehen können.

Git bleibt dabei unverzichtbar. Es löst aber nur die syntaktische Hälfte der Integrationsaufgabe. Sobald mehrere Worktrees, Teams oder Agenten gleichzeitig am System arbeiten, muss die andere Hälfte bewusst gestaltet werden: die gemeinsame Bedeutung des Codes.