Zurück zu allen Beiträgen
·5 Min. Lesezeit

API-Verträge versionieren: Breaking Changes verwalten und Kompatibilität testen

In der ersten Version einer API können ein neues Feld, ein umbenannter Endpoint oder ein angepasstes Fehlerobjekt wie ein kleines Refactoring wirken. Sobald eine API veröffentlicht ist, wird sie jedoch zur Build- und Laufzeitabhängigkeit einer anderen Codebasis. Ein Webclient, eine Mobile-App, Browser-Erweiterung, ein Background Worker oder eine Drittintegration kann sich still auf das bestehende Verhalten verlassen.

Ein API-Vertrag ist deshalb mehr als eine OpenAPI-Datei oder eine DTO-Klasse. URL, HTTP-Methode, Request- und Response-Felder, Defaults, Sortierung, Paginierung, Fehlerformat, Autorisierungsverhalten und Timeout-Erwartungen gehören alle zum Vertrag. Solide Versionierung bedeutet nicht, jede Änderung in eine neue Version zu legen. Sie bedeutet zu wissen, welche Änderung Konsumenten bricht, welche die Oberfläche sicher erweitern kann und wie der Unterschied bewiesen wird.

Zuerst Konsumenten und Nutzungsmuster sehen

Eine Kompatibilitätsentscheidung ist ohne Wissen über die Nutzer eines Endpoints nicht möglich. Es reicht nicht, nur das Frontend im selben Repository zu betrachten. Ältere Mobile-Releases, Automatisierungsskripte und unabhängige Services können mit der Zeit zurückbleiben.

Diese Fragen helfen bei der Inventur:

  • Welche Anwendungen konsumieren diesen Endpoint oder dieses Event?
  • Wie häufig aktualisieren sich Clients, und wie alt können aktive Releases sein?
  • Wird ein Feld tatsächlich genutzt oder existiert es nur in der Dokumentation?
  • Wie sieht der Migrationsplan aus, wenn ein Konsument nicht aktualisiert werden kann?

Zugriffslogs, API-Gateway-Metriken und Codesuche können helfen. Eine geringe Nutzung eines Feldes bedeutet jedoch nicht, dass es sicher entfernt werden kann: Eine Integration mit wenig Volumen kann geschäftskritisch sein. Versionierungsentscheidungen brauchen technische Beobachtung und Produktverantwortung.

Erweiterbarkeit zum Standard machen

Die sichersten kompatiblen Änderungen fügen optionale Informationen hinzu, ohne bestehende Bedeutung zu verändern. Ein neues Response-Feld ist meist sicher, wenn Clients unbekannte Felder ignorieren. Auch ein neuer Endpoint oder eine neue Fehlercodeklasse mit unterscheidbarem Body bricht bestehende Aufrufe nicht.

Es gibt jedoch keine Regel, dass ein neues Feld immer sicher ist. Clients mit strikten JSON-Schemas können unbekannte Felder als Fehler behandeln. Besonders neue Enum-Werte brauchen Aufmerksamkeit: archived in einem status-Feld kann einen Client brechen, der nur active und inactive kennt. Dass Konsumenten unbekannte Werte sicher behandeln, sollte ein expliziter Teil des Vertrags sein.

Beim Hinzufügen eines Request-Feldes muss das Standardverhalten des Servers klar sein. Die Antwort auf „Was passiert, wenn das Feld fehlt?“ darf sich zwischen Releases nicht ändern. Ein neues Pflichtfeld ist meist brechend; ein temporärer Default, eine zweistufige Migration oder ein neuer Endpoint kann besser sein.

Breaking Changes früh erkennen

Die folgenden Änderungen sollten für die meisten Konsumenten als brechend gelten:

  • Endpoint oder HTTP-Methode ändern,
  • ein Pflichtfeld im Request ergänzen,
  • ein Response-Feld entfernen, umbenennen oder seinen Typ ändern,
  • Standardsortierung oder Paginierungsbedeutung verändern,
  • eine erfolgreiche Antwort in einen anderen HTTP-Status verschieben,
  • Autorisierungs- oder Fehlerverhalten einschränken oder anderweitig verändern.

Manche Änderungen sind technisch unsichtbar, verändern aber die geschäftliche Bedeutung. Einen Währungswert von Ganzzahl auf Dezimalwert umzustellen, ein Datum von Lokalzeit nach UTC zu verschieben oder aus einem leeren Array null zu machen, kann Konsumenten beeinflussen, auch wenn das Schema ähnlich aussieht. Bei einer Vertragsprüfung sollten neben Feldtypen auch Einheit, Zeitzone, Bedeutung leerer Werte und Sortiergarantien betrachtet werden.

Deprecation ist ein Prozess

Statt ein altes Feld oder einen Endpoint zu löschen, sollte es zuerst als veraltet markiert werden. Die Dokumentation muss Ersatz, Migrationsbeispiel und Zieldatum zeigen. Wo sinnvoll, können Deprecation- oder Warninformationen zurückgegeben werden, die Integrationsverantwortliche zum Handeln bewegen.

Der Prozess ist mehr als eine Ankündigung. Nutzungsmetriken sollten zeigen, ob der alte Weg tatsächlich verlassen wurde. Eine Entscheidung auf Basis von Konsumentenmigration und Risikobewertung ist sicherer als nur ein Kalenderdatum. Bei risikoreichen Clients kann es günstiger sein, zwei Verträge parallel zu unterstützen als einen plötzlichen Ausfall zu verursachen.

Wenn eine neue Hauptversion nötig ist, kommen /v2 in der URL, Header-basierte Versionierung oder Media-Type-Versionierung infrage. Es gibt keinen universell richtigen Mechanismus; entscheidend ist, die Wahl konsistent anzuwenden und feststellen zu können, welche Version ein Client verwendet.

Den Vertrag als testbares Artefakt behandeln

Wenn Dokumentation und laufende Anwendung auseinanderlaufen, verliert die Dokumentation schnell Vertrauen. Den Vertrag als Artefakt im Source Control zu erzeugen und bei Änderungen einen Diff zu prüfen, ist eine erste Verteidigung. Ein explizites Schema macht entfernte oder neu verpflichtende Felder sichtbar.

Provider-seitige Contract Tests prüfen, ob echte Endpoints Schema und Beispielverhalten einhalten. Auf der Konsumentenseite können kritische Clients ihre Erwartungen als Tests veröffentlichen. Der Provider sieht dann vor einer Feldänderung in CI, welcher Konsument betroffen sein könnte.

Diese Tests sollten mehr als erfolgreiche Antworten abdecken. Nicht autorisierter Zugriff, Validierungsfehler, Paginierungsgrenzen, leere Ergebnisse, Rate Limits und Retry-Verhalten sind ebenfalls Teil der Vereinbarung. Die Zuverlässigkeit einer API entsteht genauso aus konsistentem Verhalten im Fehlerpfad wie im Happy Path.

Nachrichtenschnittstellen nicht vergessen

Über eine Queue veröffentlichte Events sind ebenfalls APIs. Ein Feld in einem OrderCreated-Event zu verändern, trägt dieselbe Kompatibilitätsverantwortung wie eine Änderung an einem HTTP-Endpoint. Schema-Version, Producer, Erstellungszeit und Idempotency-Information des Events sollten explizit sein.

Konsumenten sollten unbekannte Felder ignorieren und Events als möglicherweise ungeordnet oder dupliziert behandeln. Einen neuen Eventtyp hinzuzufügen, ist oft sicherer, als einen bestehenden zu überladen. Wenn vergangene Events erneut abgespielt werden müssen, sollten Schemaentwicklung und Migrationsstrategie von Anfang an bedacht werden.

Gute Versionierung verlangsamt Änderungen nicht

Vertragsdisziplin kann wie Bürokratie wirken, macht aber die Kosten von Änderungen sichtbar. Teams, die wissen, was Konsumenten erwarten, was eine Änderung brechen kann und wie eine Migration beobachtet wird, können schneller und ruhiger handeln.

Ein praktischer Einstieg ist einfach: versionskontrollierte Schemas für kritische Endpoints, Kompatibilitäts-Diffs, einige Contract Tests und klare Deprecation-Notizen. Sobald diese etabliert sind, ist eine API kein Implementierungsdetail eines Services mehr, sondern eine langlebige Produktoberfläche, auf der verschiedene Teams sicher aufbauen können.