Reference Guide · APIs

API-Versionierung ohne Kunden zu brechen

Eine öffentliche API ist ein Vertrag mit fremdem Code. Wie man sie weiterentwickelt, ohne die Produktion der Kunden zu brechen. Für CTOs, API-Architekten und Backend-Teams.

Was ist das? · Reference Guide

Ein belastbarer Leitfaden zu einer Engineering-Frage — mit Trade-offs, Kosten und dem Fall, in dem wir anders entscheiden. Kein Meinungsstück, sondern ein Referenztext. Zur Übersicht

Autor
Batunet Engineering
Lesezeit
13 Min.
Niveau
Vertiefung
Status
Freigegeben
Zuletzt geprüft
21. Juli 2026
Aktualisiert
21. Juli 2026
Auf dieser Seite

Sobald ein anderes System Ihre API aufruft, gehört ein Teil Ihres Codes nicht mehr Ihnen allein. Jede Antwort, die Sie zurückgeben, jeder Feldname, jeder Fehlercode ist ein Versprechen, auf das fremder Code sich verlässt. Eine API zu ändern heißt deshalb nicht, den eigenen Code zu ändern — es heißt, in die Produktion von Menschen einzugreifen, die man nie trifft.

Das ist der Grund, warum API-Versionierung eine der unversöhnlichsten Disziplinen der Softwaretechnik ist. Intern kann man einen Fehler korrigieren, ausrollen, weitermachen. An einer öffentlichen Schnittstelle ist eine unbedachte Änderung ein stiller Ausfall in fremden Systemen — bemerkt nicht von Ihnen, sondern vom Kunden des Kunden. Dieser Text handelt davon, wie man eine API über Jahre weiterentwickelt, ohne dieses Versprechen zu brechen: kompatibel, wo es geht; sauber versioniert, wo es sein muss; und mit einem Ausstieg, der niemanden überrascht.


Warum eine API ein Versprechen ist

Der Vertrag einer API ist alles, worauf ein Aufrufer sich verlassen kann: die Struktur der Antwort, die Bedeutung der Felder, die Semantik der Fehler, das Verhalten in Grenzfällen. Vieles davon steht nirgends in der Dokumentation — es steht im Code der Kunden, die eine bestimmte Antwortform erwarten und darauf gebaut haben. Der wahre Vertrag ist größer als das, was Sie zugesagt haben; er umfasst alles, was Kunden beobachten konnten.

Daraus folgt die Grundhaltung: An einer öffentlichen API ist Rückwärtskompatibilität keine Höflichkeit, sondern die Voreinstellung. Man ändert nur, was man ändern kann, ohne dass bestehende Aufrufer es merken — und behandelt jede sichtbare Änderung als das, was sie ist: einen Eingriff in fremde Systeme.

Was ein Breaking Change ist — und was nicht

Bevor man über Versionierung nachdenkt, muss man erkennen, was überhaupt bricht. Die Grenze verläuft nicht zwischen „klein" und „groß", sondern zwischen „ein bestehender Aufrufer merkt es" und „er merkt es nicht".

Nicht brechend sind in aller Regel additive Änderungen: ein neues, optionales Feld in der Antwort; ein neuer optionaler Parameter mit sinnvollem Standard; ein neuer Endpunkt; ein neuer Wert in einer erweiterbaren Aufzählung, wenn Clients unbekannte Werte tolerieren.

Brechend sind Änderungen, die eine bestehende Erwartung verletzen: ein Feld entfernen oder umbenennen; den Typ oder das Format eines Feldes ändern; ein bislang optionales Feld zur Pflicht machen; die Validierung verschärfen; einen Standardwert ändern; die Bedeutung eines Fehlercodes verschieben; die Reihenfolge oder Semantik ändern, auf die sich jemand verlassen konnte. Auch das Verschärfen von Grenzen — ein niedrigeres Limit, ein strengeres Format — bricht, obwohl kein Feld verschwindet.

Der unbequeme Teil: Manche Kunden verlassen sich auf Verhalten, das Sie nie zugesagt haben. Deshalb ist die sicherste Annahme, dass jede beobachtbare Änderung für irgendjemanden brechend ist, bis das Gegenteil belegt ist.

Das Prinzip: Evolution vor Versionierung

Eine neue Version ist teuer — für Sie, weil Sie zwei Verträge betreiben, und für die Kunden, weil sie migrieren müssen. Deshalb ist die neue Version das letzte Mittel, nicht der erste Reflex. Die meisten Änderungen lassen sich kompatibel gestalten, wenn man die API von Anfang an auf Evolution auslegt.

Das bedeutet: additiv erweitern statt umbauen, optionale Felder statt Pflichtfelder, tolerante Leser auf beiden Seiten. Eine API, die so entworfen ist, wächst jahrelang ohne einen einzigen Versionssprung — und genau das ist das Ziel. Eine Version ist ein Eingeständnis, dass man nicht mehr kompatibel weiterkommt; man sollte es selten und bewusst machen.

Die Empfehlung: Behandeln Sie die neue Version als letztes Mittel und lösen Sie so viel wie möglich durch kompatible Evolution. Der Preis ist, dass die kompatible Lösung selten die eleganteste ist — man trägt Altlasten mit, sammelt optionale Felder an und lebt mit Namen, die man heute anders wählen würde. Anders entscheiden wir, wenn das Modell grundlegend falsch geworden ist oder eine Sicherheitsanforderung eine inkompatible Änderung erzwingt — dann ist die neue Version die ehrlichere Antwort als eine Kette von Verrenkungen, die den Vertrag verschleiern.

Rückwärtskompatibel erweitern

Das Werkzeug der kompatiblen Evolution ist dasselbe wie bei der Datenmigration: additiv wachsen, nie im Bestand ändern. Neue Felder kommen hinzu, ohne dass alte verschwinden. Neue Parameter sind optional und haben einen Standard, der das bisherige Verhalten erhält. Wo ein Feld ersetzt werden soll, existieren beide eine Zeit lang nebeneinander, bevor das alte irgendwann — in einer neuen Version — verschwindet.

Die zweite Hälfte ist der tolerante Leser: Beide Seiten ignorieren, was sie nicht kennen. Ein Client, der unbekannte Felder überliest und bei neuen Aufzählungswerten nicht abstürzt, erlaubt dem Server, additiv zu wachsen, ohne ihn zu brechen. Ein Server, der unbekannte Eingabefelder nicht sofort ablehnt, lässt Clients vorausschauend bauen. Streng in dem, was man zusichert; tolerant in dem, was man annimmt.

Antwort altes Feld + neues Feld alter Client überliest das neue Feld neuer Client nutzt das neue Feld

Schema: Ein additives Feld bricht niemanden — der alte Client ignoriert, was er nicht kennt.

Die Empfehlung: Erweitern Sie additiv und bauen Sie Toleranz auf beiden Seiten ein. Der Preis ist, dass Felder sich ansammeln, fast alles optional wird und die API mit der Zeit mehr trägt, als ein Neuentwurf enthielt — Disziplin ist nötig, damit das nicht in Beliebigkeit kippt. Anders entscheiden wir bei einer rein internen API, deren Aufrufer wir im selben Zug ausrollen — dort kostet Toleranz nur Unschärfe, und ein direkter, koordinierter Bruch ist sauberer als dauerhafte Optionalität.

Wenn ein Breaking Change unvermeidlich ist

Manchmal geht es nicht kompatibel. Das Modell hat sich grundlegend geändert, ein Fehler im Vertrag muss korrigiert werden, oder eine Zusammenlegung lässt sich nicht additiv abbilden. Dann — und erst dann — entsteht eine neue Version. Eine Version ist keine Marketing-Nummer, sondern eine Grenze: hinter ihr gilt ein anderer, klar benannter Vertrag, und die alte Version bleibt unberührt gültig, solange Kunden sie brauchen.

Die entscheidende Regel: Eine neue Version bricht die alte nicht. Sie tritt neben sie. Wer „Version 2" ausrollt und dabei „Version 1" verändert, hat nicht versioniert, sondern nur den Bruch umbenannt.

Die Empfehlung: Führen Sie eine neue Hauptversion nur für echte Breaking Changes ein und bündeln Sie mehrere davon, statt für jede kleine Inkompatibilität eine Version zu ziehen. Der Preis ist, dass gewünschte Brüche warten müssen, bis sich ein Versionssprung lohnt, und dass jede Version, die Sie eröffnen, jahrelang mitgeschleppt wird. Anders entscheiden wir bei einer API vor dem ersten stabilen Release, die ausdrücklich als instabil gekennzeichnet ist — dort darf man brechen, weil noch niemand ein Versprechen erhalten hat, auf das er bauen durfte.

Wo die Version steht: URL, Header oder Media Type

Wenn eine Version sein muss, stellt sich die Frage, wo sie sichtbar wird. Es gibt drei etablierte Orte, und keiner ist ohne Preis.

PlatzierungBeispielVorteilPreisWann
URL-Pfad/v2/orderssichtbar, cachebar, einfach zu testenVersion klebt an der Ressourcen-Identitätöffentliche APIs, grobe Versionen
HeaderApi-Version: 2URL bleibt über Versionen stabilunsichtbar, leicht zu vergessen, Cache-Tückenkontrollierte, interne Clients
Media TypeAccept: …vnd.batunet.v2+jsonfeingranular, nah am HTTP-Modellkomplex, hoher Tooling-AufwandHypermedia, feine Evolution

Für die meisten öffentlichen APIs ist die Version im URL-Pfad die richtige Wahl: Sie ist sichtbar, in Logs und Caches eindeutig, in jedem Werkzeug sofort testbar und für Kunden ohne Erklärung verständlich. Der Preis ist konzeptioneller Natur — die Version wird Teil der Ressourcen-URL, obwohl dieselbe Ressource gemeint ist, und Versionen neigen dazu, sich im Pfad zu vermehren.

Die Empfehlung: Versionieren Sie öffentliche APIs grob über den URL-Pfad. Der Preis ist die konzeptionelle Unschärfe und die Neigung zur Versionsvermehrung, wenn man zu leicht eine neue Nummer zieht. Anders entscheiden wir bei feingranularer Evolution oder Hypermedia-APIs, wo die Media-Type-Versionierung dem HTTP-Modell treuer bleibt, und bei rein internen Clients, wo ein Header genügt, weil man Server und Aufrufer gemeinsam kontrolliert.

Zwei Versionen parallel betreiben

Sobald eine zweite Version existiert, laufen zwei Verträge gleichzeitig — und die teure Falle ist, dafür zwei Systeme zu bauen. Der richtige Weg ist eine Implementierung im Kern und eine dünne Übersetzung an der Kante: Ein Version-Router nimmt die Anfrage entgegen, übersetzt sie in das interne Modell, und formt die Antwort in den jeweils zugesagten Vertrag zurück. Der Kern kennt keine Versionen; nur der Rand tut es.

Client v1 Client v2 Version-Router Übersetzung Kern (versionslos)

Schema: Der Kern kennt keine Versionen; nur die Kante übersetzt in den jeweiligen Vertrag.

Die Empfehlung: Betreiben Sie mehrere Versionen als Übersetzungen über einer gemeinsamen Implementierung, nicht als getrennte Stacks. Der Preis ist eine Übersetzungsschicht je Version, die man pflegt, und die Pflicht, jeden Vertrag getrennt zu testen. Anders entscheiden wir, wenn zwei Versionen sich fachlich so weit auseinanderentwickelt haben, dass die Übersetzung komplizierter würde als zwei getrennte Pfade — ein seltener Fall, der meist bedeutet, dass es zwei verschiedene Produkte sind, keine zwei Versionen eines Produkts.

Deprecation ist ein Prozess, kein Ereignis

Eine alte Version abzuschalten ist kein Stichtag, sondern ein Ablauf mit Vorlauf. Er beginnt mit einer Ankündigung, lange bevor etwas passiert. Er macht die Deprecation im Protokoll sichtbar — über Deprecation- und Sunset-Header, die jedem Aufruf mitteilen, dass diese Version endet und wann. Und er stützt sich auf Telemetrie: Man muss wissen, wer die alte Version noch nutzt, bevor man sie abschaltet, sonst schaltet man blind.

v1 v2 Ankündigung Abschaltung v1 beide aktiv · Sunset-Header Zeit

Schema: Zwischen Ankündigung und Abschaltung laufen beide Versionen — der Sunset-Header nennt den Termin.

PhaseWas geschiehtWas der Aufrufer sieht
Ankündigungdas Ende wird früh mitgeteiltDeprecation-Header, Changelog
Parallelbetriebalt und neu laufen zugleichSunset-Header nennt den Termin
BeobachtungTelemetrie prüft die Restnutzungnichts — es läuft weiter
Abschaltungalt wird entfernt, wenn kaum genutztder angekündigte Termin greift

Die Empfehlung: Kündigen Sie Deprecations früh an, machen Sie sie über Deprecation- und Sunset-Header maschinenlesbar und schalten Sie erst ab, wenn die Telemetrie zeigt, dass die alte Version nicht mehr ernsthaft genutzt wird. Der Preis ist, dass Sie die alte Version über das gesamte Fenster weiter betreiben, absichern und testen — eine Version, die Sie längst loswerden wollten, bleibt Monate erhalten. Anders entscheiden wir bei einer API ohne externe Nutzer oder mit einer kleinen, bekannten Menge von Aufrufern, mit denen man direkt spricht — dort darf das Fenster kurz sein, weil die Migration koordiniert statt erraten wird.

Verträge testen, bevor der Kunde es tut

Der gefährlichste Breaking Change ist der unbeabsichtigte — die Umbenennung, die niemand als brechend erkannt hat. Dagegen hilft nur, den Vertrag selbst zu testen. Vertragstests prüfen die zugesagte Form der Antwort gegen jede Änderung; consumer-getriebene Verträge erlauben Kunden, ihre Erwartungen zu hinterlegen, sodass ein Bruch im eigenen Build auffällt, nicht in fremder Produktion.

Die Empfehlung: Sichern Sie den öffentlichen Vertrag mit Tests ab, die brechen, sobald sich die zugesagte Form ändert. Der Preis ist eine Testsuite, die man pflegt, und bei consumer-getriebenen Verträgen ein Stück Koordination mit den Aufrufern. Anders entscheiden wir bei einer sehr kleinen, stabilen API mit wenigen bekannten Nutzern — dort kann ein leichtgewichtiger Satz von Beispielantworten als Regressionsnetz genügen, statt eines vollen Vertragstest-Aufbaus.

Kommunikation: Changelog, Fristen, Migrationspfad

Technik allein bricht keine Kunden — schlechte Kommunikation tut es. Zu jeder sichtbaren Änderung gehört ein Changelog, das brechende von nicht brechenden Änderungen klar trennt. Zu jeder neuen Version gehört ein Migrationspfad, der Schritt für Schritt zeigt, wie man von alt nach neu kommt. Und zu jeder Deprecation gehört eine Frist, die früh genannt und eingehalten wird. Bekannte Aufrufer werden zusätzlich direkt informiert, nicht nur über einen Header, den vielleicht niemand liest.

Die Empfehlung: Behandeln Sie Kommunikation als Teil des Vertrags — Changelog, Migrationsanleitung und verlässliche Fristen. Der Preis ist laufender Aufwand: Jede Änderung will beschrieben, jede Frist gepflegt werden. Anders entscheiden wir bei internen APIs innerhalb eines Teams, wo ein kurzer Hinweis genügt, weil die Aufrufer bekannt und erreichbar sind.

Häufige Fehler

Die immer gleichen Muster brechen Kunden:

  • Ein Feld umbenennen oder entfernen, ohne es als Breaking Change zu erkennen — der häufigste stille Bruch.
  • Die Validierung nachträglich verschärfen und annehmen, das sei „nur eine Korrektur" — für den Aufrufer ist es ein Bruch.
  • „Version 2" ausrollen und dabei „Version 1" verändern — ein umbenannter Bruch, keine Versionierung.
  • Für jede kleine Inkompatibilität eine neue Version ziehen, bis niemand mehr weiß, welche gilt.
  • Zwei Versionen als getrennte Systeme bauen, statt an der Kante zu übersetzen — doppelte Pflege, doppelte Fehler.
  • Ohne Telemetrie abschalten und hoffen, dass niemand die alte Version mehr nutzt.
  • Die Deprecation nur im Header ankündigen und sich wundern, dass Kunden überrascht sind.
  • Die alte Version „vorübergehend" behalten, bis sie zur dauerhaften Zweitschnittstelle wird.

Checkliste

Fragen, die ein Team vor jeder API-Änderung stellen kann. Es sind Diagnosefragen, keine Urteile.

  • Kann ein bestehender Aufrufer diese Änderung bemerken? Wenn ja, ist sie brechend — unabhängig davon, wie klein sie wirkt.
  • Lässt sich das Ziel additiv erreichen, statt im Bestand zu ändern? Kompatible Evolution ist fast immer der günstigere Weg.
  • Wenn eine neue Version nötig ist — bleibt die alte unberührt gültig? Eine Version, die die alte verändert, ist nur ein umbenannter Bruch.
  • Läuft der Kern versionslos, mit Übersetzung nur an der Kante? Sonst pflegt man bald zwei Systeme statt zweier Verträge.
  • Wissen wir aus Telemetrie, wer die alte Version noch nutzt? Ohne diese Zahl schaltet man blind ab.
  • Sind Deprecation- und Sunset-Header gesetzt und ist die Frist kommuniziert? Überraschung ist der eigentliche Bruch.
  • Testen wir den öffentlichen Vertrag automatisch? Sonst lernt der Kunde den Bruch vor uns.
  • Gibt es einen Migrationspfad, den ein fremdes Team allein gehen kann? Wenn nicht, hängt die Migration an Nachfragen.

FAQ

Brauchen wir überhaupt Versionen, wenn wir kompatibel bleiben? Im Idealfall selten. Eine gut entworfene API wächst jahrelang additiv ohne Versionssprung. Versionen sind für den Moment reserviert, in dem Kompatibilität nicht mehr reicht — nicht für gewöhnliche Weiterentwicklung.

URL-Pfad oder Header — was ist richtig? Für öffentliche APIs meist der URL-Pfad, weil er sichtbar, cachebar und ohne Erklärung testbar ist. Header passen für kontrollierte interne Clients, Media-Type-Versionierung für feingranulare oder Hypermedia-APIs. Der Preis jedes Weges steht in der Tabelle oben; es gibt keinen Sieger ohne Kontext.

Wie lange müssen wir eine alte Version betreiben? So lange, bis die Telemetrie zeigt, dass sie nicht mehr ernsthaft genutzt wird, und mindestens so lange, wie Sie es angekündigt haben. Die Zahl hängt von Ihren Kunden ab, nicht von einer Regel — aber eine Frist, die früh genannt und dann eingehalten wird, ist wichtiger als ihre genaue Länge.

Ist ein neuer Pflichtparameter ein Breaking Change? Ja. Alles, was einen bestehenden, bisher gültigen Aufruf plötzlich scheitern lässt, bricht — ein neuer Pflichtparameter, eine strengere Validierung, ein kleineres Limit. Neu und optional mit sinnvollem Standard bricht nicht; neu und verpflichtend bricht.

Was, wenn ein Kunde sich auf undokumentiertes Verhalten verlässt? Dann verlässt er sich trotzdem darauf, und sein System bricht, wenn Sie es ändern. Deshalb ist die sichere Annahme, dass jedes beobachtbare Verhalten Teil des Vertrags ist. Man kann solche Abhängigkeiten über eine Deprecation-Frist abbauen — aber nicht durch den Hinweis, es sei ja nie zugesagt gewesen.

Wie führt man zwei Versionen, ohne den Aufwand zu verdoppeln? Nicht als zwei getrennte Systeme, sondern als einen versionslosen Kern mit einer dünnen Übersetzungsschicht an der Kante: Der Kern kennt keine Versionen, jede Version übersetzt an ihrer Grenze in die gemeinsame Form. So pflegt man die Fachlichkeit einmal und nur die Übersetzung doppelt — genau das ist der Unterschied zwischen tragbarem und untragbarem Parallelbetrieb.

Weiterführend

Grundlage ist die Batunet Engineering Method: Verträge bewusst entscheiden, in kleinen umkehrbaren Schritten ändern, den Fehlerfall vorher proben.


Eine gute API-Änderung merkt der Kunde nicht. Neue Felder erscheinen, alte bleiben, Versionen enden mit Ansage — und die Systeme, die man nie sieht, laufen weiter, als wäre nichts geschehen.

Referenzierte Entitäten

Ein konkretes Vorhaben in diesem Feld?

Reference Guides zeigen, wie wir denken. Für Ihr System sprechen Sie mit der Geschäftsführung — technisch, ohne Vertrieb.