GraphQL Schema-Versionierung ohne Breaking Changes langfristig planen
AI generated
{ }
type
GraphQL · Schema-Design · API-Governance
GraphQL Schema-Versionierung ohne Breaking Changes langfristig planen
wenn ein einziges Schema über Jahre wachsen muss

GraphQL kennt keine Versionsnummern in der URL, und das ist Absicht, nicht Zufall. Wer eine GraphQL-API über Jahre stabil betreiben will, braucht deshalb eine andere Disziplin: additive Schema-Evolution, klar definierte Deprecation-Zyklen und automatisierte Checks, die Breaking Changes erkennen, bevor sie überhaupt deployt werden.

18 Min. Lesezeit Schema-Evolution · Deprecation · Schema-Registry GraphQL · CI/CD

1. Warum GraphQL keine Versionsnummern in der URL braucht

Bei REST-APIs ist /v1/products gefolgt von /v2/products ein etabliertes Muster, wenn sich die Datenstruktur grundlegend ändert. GraphQL Schema-Versionierung funktioniert bewusst anders: Es gibt genau ein Schema, das sich über Zeit weiterentwickelt, ohne dass Clients auf eine neue Version wechseln müssen. Der Grund liegt im Anfrage-Modell selbst, ein GraphQL-Client fragt exakt die Felder an, die er braucht, neue Felder im Schema haben keinerlei Auswirkung auf bestehende Clients, die diese Felder nicht anfragen.

Diese Eigenschaft macht GraphQL Schema-Versionierung grundlegend zu einer additiven Disziplin statt zu einer Versionsnummern-Verwaltung. Statt parallele API-Versionen zu pflegen, was Wartungsaufwand vervielfacht, wächst ein einziges Schema kontinuierlich, alte Felder bleiben so lange erhalten, bis sich Clients aktiv auf neue Alternativen umgestellt haben. Der Preis dafür ist Disziplin: Jede Schema-Änderung muss bewusst danach bewertet werden, ob sie additiv ist oder bestehende Clients bricht.

Der häufigste Fehler bei GraphQL Schema-Versionierung ist die Annahme, GraphQL mache Breaking Changes automatisch unmöglich. Das stimmt nicht, ein Feld zu entfernen, seinen Typ zu ändern oder ein optionales Argument verpflichtend zu machen, bricht bestehende Clients genauso wie bei REST. Der Unterschied ist nur, dass GraphQL die Werkzeuge bereitstellt, um solche Änderungen zu vermeiden oder kontrolliert über einen Deprecation-Zyklus abzuwickeln, statt sie erzwungenermaßen sofort durchzusetzen.

Diese Werkzeuge entfalten ihren Wert aber nur, wenn ein Team sie tatsächlich konsequent einsetzt. Ein Schema, das formal additiv wachsen könnte, aber in der Praxis regelmäßig unangekündigte Breaking Changes erfährt, weil Deadlines Druck erzeugen, verliert denselben Vertrauensvorschuss bei Client-Teams wie eine schlecht gepflegte REST-API. GraphQL Schema-Versionierung ist deshalb am Ende weniger eine technische als eine organisatorische Aufgabe, die folgenden Abschnitte behandeln beide Seiten gleichermaßen.

2. Was überhaupt als Breaking Change zählt

Für saubere GraphQL Schema-Versionierung muss ein Team eine klare, gemeinsame Definition haben, was eine Breaking Change ist. Grundsätzlich gilt: Jede Änderung, die eine bestehende, gültige Query ungültig macht oder ihr Ergebnis in einer für den Client unerwarteten Weise verändert, ist eine Breaking Change. Das umfasst das Entfernen eines Feldes oder Typs, das Ändern eines Feldtyps, etwa von String zu Int, das Hinzufügen eines verpflichtenden Arguments zu einem bestehenden Feld, und das Ändern eines nullable Feldes zu non-nullable, wenn der Server tatsächlich gelegentlich null zurückgeben kann.

Nicht jede Änderung, die auf den ersten Blick riskant wirkt, ist tatsächlich eine Breaking Change. Ein Feld als @deprecated zu markieren, bricht keine bestehenden Queries, es liefert weiterhin dieselben Daten. Ein neues optionales Argument zu einem Feld hinzuzufügen ist additiv, solange ein sinnvoller Standardwert existiert. Diese Unterscheidung ist der Kern jeder soliden Strategie für GraphQL Schema-Versionierung, additive Änderungen können jederzeit deployt werden, entfernende oder verändernde Änderungen brauchen einen Deprecation-Prozess.

Eine Grauzone bilden Änderungen an Beschreibungstexten und Interface-Implementierungen. Eine reine Textänderung an description bricht keine Query, kann aber Tooling verwirren, das Beschreibungen automatisiert weiterverarbeitet. Ein Typ, der plötzlich ein zusätzliches Interface implementiert, ist meist additiv, ein Typ, der ein bestehendes Interface nicht mehr implementiert, ist dagegen fast immer eine Breaking Change für jeden Client, der über Fragment-Spreads auf dieses Interface zugreift.

3. Additive Evolution: neue Felder statt neue Endpunkte

Der zentrale Baustein von GraphQL Schema-Versionierung ist additive Evolution: Statt ein Feld zu ändern, wird ein neues Feld mit dem gewünschten Verhalten hinzugefügt, während das alte Feld bestehen bleibt. Muss sich beispielsweise das Format eines Preisfeldes von einem einfachen Float zu einem strukturierten Objekt mit Währung und Betrag ändern, entsteht kein Konflikt, wenn beide Varianten parallel existieren, bis alle Clients auf die neue Struktur umgestellt sind.

Diese Strategie kostet zunächst mehr Felder im Schema als eine harte Migration, zahlt sich aber durch Stabilität aus. Bestehende mobile Apps, die möglicherweise nicht sofort aktualisiert werden können, funktionieren unverändert weiter, während neue Clients bereits die verbesserte Struktur nutzen. Erst nachdem alle bekannten Clients migriert sind, wird das alte Feld tatsächlich entfernt, nie vorher.


# BEFORE: a plain float price field, ambiguous currency assumption
type Product {
  id: ID!
  price: Float!
}

# ADDITIVE EVOLUTION: new structured field added, old field kept working
type Money {
  amount: Float!
  currency: String!
}

type Product {
  id: ID!
  price: Float! @deprecated(reason: "Use priceDetails instead, ambiguous currency. Removal planned for 2027-Q1.")
  priceDetails: Money!
}

# Existing clients querying `price` keep working unchanged,
# new clients migrate to `priceDetails` at their own pace

4. @deprecated richtig einsetzen und kommunizieren

Die @deprecated-Direktive ist das zentrale Werkzeug für kontrollierte GraphQL Schema-Versionierung. Sie markiert ein Feld als veraltet, ohne es zu entfernen, und liefert über das reason-Argument eine für Entwickler lesbare Begründung, die in GraphiQL, Apollo Studio und jedem Introspection-basierten Tool sichtbar ist. Eine gute Deprecation-Begründung nennt drei Dinge: warum das Feld veraltet ist, welche Alternative genutzt werden soll, und bis wann das Feld voraussichtlich entfernt wird.

Wichtig für GraphQL Schema-Versionierung ist, dass @deprecated allein nicht ausreicht, um Clients tatsächlich zur Migration zu bewegen. Ohne aktive Kommunikation, etwa über Changelog-Einträge, Team-Benachrichtigungen oder automatisierte Warnungen in CI-Pipelines, die deprecateter Felder verwenden, bleiben veraltete Felder oft jahrelang in Benutzung, weil niemand die Migration priorisiert. Ein fester Prozess, deprecated Felder nach einer definierten Frist tatsächlich zu entfernen, verhindert, dass ein Schema unbegrenzt wächst.


type Query {
  # Old, ambiguous filter argument, deprecated with a clear migration path
  products(status: String @deprecated(reason: "Use statusFilter (enum) instead. Removed in v2027.1.")): [Product!]!

  # New, type-safe replacement using an enum instead of a free-form string
  productsV2(statusFilter: ProductStatus): [Product!]!
}

enum ProductStatus {
  DRAFT
  ACTIVE
  ARCHIVED
}

5. Nullable-Felder als sicheres Erweiterungswerkzeug

Ein oft übersehenes Werkzeug bei GraphQL Schema-Versionierung ist der bewusste Einsatz von Nullable-Feldern. Ein neues Feld sollte in den allermeisten Fällen als nullable eingeführt werden, selbst wenn es zum Startzeitpunkt für alle bestehenden Datensätze befüllbar wäre. Der Grund ist Zukunftssicherheit, ein Feld, das später aus fachlichen Gründen für manche Datensätze nicht mehr befüllt werden kann, muss sonst nachträglich von non-nullable auf nullable geändert werden, was selbst eine Breaking Change ist.

Umgekehrt sollte ein bestehendes nullable Feld nur dann zu non-nullable geändert werden, wenn absolut sichergestellt ist, dass der Server für dieses Feld niemals mehr null zurückgibt, für keinen einzigen bestehenden oder zukünftigen Datensatz. Diese Garantie ist in der Praxis überraschend schwer zu geben, weshalb erfahrene GraphQL-Teams bei GraphQL Schema-Versionierung im Zweifel eher konservativ bei Nullability bleiben, auch wenn das bedeutet, dass Clients zusätzliche Null-Checks schreiben müssen.

Dasselbe Prinzip gilt für Argumente von Feldern und für Enum-Werte. Ein neues optionales Argument mit sinnvollem Standardwert ist additiv, ein neuer Enum-Wert dagegen kann bestehende Clients überraschen, die eine switch-Anweisung über alle bekannten Werte geschrieben haben und den neuen Wert stillschweigend falsch behandeln. Für GraphQL Schema-Versionierung lohnt es sich deshalb, Enums von Anfang an mit einem expliziten Hinweis in der Beschreibung zu versehen, dass Clients auf unbekannte Werte defensiv reagieren sollten, statt sich auf eine feste, geschlossene Liste zu verlassen.

6. Schema-Registry: Breaking Changes vor dem Deploy erkennen

Manuelle Reviews reichen bei wachsenden Teams nicht aus, um jede Breaking Change zuverlässig zu erkennen. Eine Schema-Registry wie GraphQL Hive oder Apollo Studio speichert historische Schema-Versionen und vergleicht jedes neue Schema automatisch gegen die zuletzt produktiv genutzte Version. Dieser Diff-Check erkennt entfernte Felder, geänderte Typen und andere inkompatible Änderungen automatisch, bevor sie überhaupt in die Produktion gelangen.

Für GraphQL Schema-Versionierung ist die Integration eines solchen Checks in die CI-Pipeline der wichtigste einzelne Schutzmechanismus. Ein Pull Request mit einer Breaking Change wird automatisch blockiert oder zumindest deutlich markiert, bevor ein Reviewer die Änderung manuell durchgehen muss. Fortgeschrittene Registries gehen noch weiter und verknüpfen den Schema-Diff mit tatsächlicher Nutzungstelemetrie, eine Breaking Change auf einem Feld, das kein einziger Client in den letzten neunzig Tagen genutzt hat, ist ein geringeres Risiko als dieselbe Änderung an einem stark frequentierten Feld.


# GraphQL Inspector: check schema changes against the current baseline
# in a CI pipeline, before merging
npx graphql-inspector diff \
  schema-baseline.graphql \
  schema-candidate.graphql \
  --fail-on-breaking

# Output flags every removed field, changed type, or newly required argument
# Exit code 1 blocks the pipeline on any breaking change

7. Nutzung deprecateter Felder tatsächlich messen

Ein deprecated Feld sicher zu entfernen setzt voraus, dass tatsächlich bekannt ist, ob und wie oft es noch genutzt wird. Ohne Nutzungstelemetrie bleibt jede Entscheidung, ein Feld zu entfernen, eine Vermutung, die im schlimmsten Fall einen produktiven Client bricht, von dessen Existenz das API-Team gar nichts wusste. Für GraphQL Schema-Versionierung ist Field-Level-Usage-Tracking deshalb kein Nice-to-have, sondern Voraussetzung für sicheres Entfernen.

Tools wie Apollo Studio oder eigene Resolver-Middleware können pro Feld protokollieren, welcher Client-Name, welche Client-Version und wie oft ein bestimmtes Feld angefragt wird. Erst wenn diese Metrik über einen definierten Zeitraum, etwa neunzig Tage, konstant bei null liegt, ist die tatsächliche Entfernung eines deprecateten Feldes ein kalkulierbares statt ein riskantes Vorgehen.

Ein zusätzlicher Sicherheitsmechanismus ist eine explizite Vorwarnung im Response selbst, etwa ein extensions-Feld in der GraphQL-Antwort, das auflistet, welche angefragten Felder deprecated sind. Manche Client-Bibliotheken können solche Warnungen automatisch in der Entwicklerkonsole ausgeben, was die Sichtbarkeit von Deprecations deutlich erhöht, ohne dass Entwickler aktiv im Schema nachschauen müssen.

8. Governance: wer darf das Schema wie ändern

Mit wachsender Team-Größe wird GraphQL Schema-Versionierung auch zu einer organisatorischen Frage. Ohne klare Regeln entstehen leicht Schema-Wildwuchs, inkonsistente Namenskonventionen und unkoordinierte Breaking Changes von verschiedenen Teams, die an unterschiedlichen Teilen desselben Schemas arbeiten. Ein Schema-Governance-Prozess definiert, wer Änderungsvorschläge reviewen darf, welche Namenskonventionen gelten, und wie mit widersprüchlichen Anforderungen zwischen Teams umgegangen wird.

In föderierten Architekturen mit mehreren Teams, die jeweils Teile eines übergreifenden Schemas beisteuern, ist ein zentrales Schema-Registry-Team oft sinnvoll, das Namenskonventionen durchsetzt und Breaking-Change-Checks über Team-Grenzen hinweg koordiniert. Für kleinere Teams reicht häufig ein einfacher, dokumentierter Review-Prozess mit verpflichtendem Schema-Diff-Check vor jedem Merge, der wichtigste Punkt ist, dass die Regeln klar und für alle Beteiligten sichtbar dokumentiert sind.

Ein praktikables Governance-Dokument für GraphQL Schema-Versionierung beantwortet mindestens vier Fragen: Wer genehmigt eine neue Breaking Change nach abgeschlossenem Deprecation-Zyklus, welches Mindestintervall gilt zwischen Ankündigung und tatsächlicher Entfernung, wie werden externe Partnerteams oder Drittanbieter-Clients informiert, die möglicherweise nicht im selben Versionskontrollsystem sichtbar sind, und wo wird der aktuelle Deprecation-Status zentral einsehbar dokumentiert. Ohne schriftliche Antworten auf diese vier Fragen bleibt Governance meist Ansichtssache einzelner Teammitglieder, was bei Personalwechsel schnell zu inkonsistenter Praxis führt.

9. Schema-Evolution im Vergleich zu REST-Versionierung

Der grundlegende Unterschied zwischen GraphQL Schema-Versionierung und klassischer REST-API-Versionierung zeigt sich am deutlichsten in der Wartungsstruktur.

Aspekt REST-Versionierung GraphQL Schema-Evolution
Parallele Versionen Mehrere komplette API-Versionen Ein Schema, additiv erweitert
Alte Clients Bleiben auf alter Version Nutzen weiterhin alte Felder
Wartungsaufwand Steigt mit jeder Version Konstant, ein Codepfad
Breaking-Change-Erkennung Manuell, oft implizit Automatisiert via Schema-Diff
Entfernung alter Felder Version-Sunset mit hartem Datum Nutzungstelemetrie-gesteuert

In der Praxis führt GraphQL Schema-Versionierung zu weniger parallelem Wartungsaufwand, verlangt dafür aber mehr Disziplin bei jeder einzelnen Schema-Änderung, weil es keine harte Versionsgrenze gibt, die Fehler technisch erzwungen sichtbar macht.

Teams, die von einer versionierten REST-API zu GraphQL wechseln, unterschätzen diesen Disziplin-Unterschied häufig zu Beginn. Der scheinbare Vorteil, keine Versionsnummern mehr pflegen zu müssen, kippt schnell ins Gegenteil, wenn Schema-Diff-Checks und Deprecation-Prozesse fehlen, weil dann jede Änderung potenziell unbemerkt zur Breaking Change wird, statt kontrolliert hinter einer neuen Versionsnummer zu verschwinden.

Mironsoft

GraphQL-Architektur, API-Governance und Schema-Design

Schema soll über Jahre stabil bleiben, ohne Breaking Changes?

Wir richten Schema-Diff-Checks in eurer CI-Pipeline ein, definieren Deprecation-Workflows und bauen Nutzungstelemetrie auf, damit Feldentfernungen fundiert statt riskant sind.

Schema-Diff-CI

Automatisierte Breaking-Change-Erkennung vor jedem Merge

Deprecation-Workflow

Klare Prozesse für Ankündigung, Frist und Entfernung veralteter Felder

Governance

Namenskonventionen und Review-Prozesse für wachsende Teams

10. Zusammenfassung

GraphQL Schema-Versionierung funktioniert grundlegend anders als REST-Versionierung mit Versionsnummern in der URL. Statt paralleler API-Versionen wächst ein einziges Schema additiv, neue Felder ergänzen bestehende, ohne sie zu ersetzen. Die @deprecated-Direktive markiert veraltete Felder mit klarer Begründung und Zeitplan, während Nullable-Felder als sicheres Standardwerkzeug für neue Erweiterungen dienen.

Automatisierte Schema-Diff-Checks in der CI-Pipeline erkennen Breaking Changes, bevor sie deployt werden, und Nutzungstelemetrie macht die Entfernung veralteter Felder zu einer fundierten statt riskanten Entscheidung. Mit wachsender Team-Größe braucht GraphQL Schema-Versionierung zusätzlich klare Governance-Regeln, damit additive Evolution nicht in unkoordinierten Schema-Wildwuchs kippt.

GraphQL Schema-Versionierung — Das Wichtigste auf einen Blick

Additive Evolution

Neue Felder statt geänderter Felder, altes Feld bleibt bis zur vollständigen Migration erhalten.

@deprecated

Klare Begründung, Alternative und Entfernungsdatum, kombiniert mit aktiver Kommunikation.

Schema-Registry

Automatisierter Diff-Check in der CI-Pipeline blockiert Breaking Changes vor dem Deploy.

Nutzungstelemetrie

Field-Level-Usage-Tracking macht Entfernungen kalkulierbar statt riskant.

11. FAQ: GraphQL Schema-Versionierung

1Warum keine Versionsnummern in der URL?
Weil Clients exakt die benötigten Felder anfragen, neue Felder wirken sich nicht auf bestehende Clients aus.
2Was zählt als Breaking Change?
Entfernte Felder, geänderte Typen, neue Pflichtargumente und Nullable-zu-non-Nullable-Änderungen.
3Was ist additive Evolution?
Neues Feld statt Änderung, altes Feld bleibt bis zur vollständigen Client-Migration bestehen.
4Gute @deprecated-Begründung?
Warum veraltet, welche Alternative, und bis wann die Entfernung geplant ist.
5Warum neue Felder meist nullable?
Vermeidet eine spätere, selbst breaking Änderung von non-nullable auf nullable.
6Was macht eine Schema-Registry?
Vergleicht neue Schemas gegen die Produktivversion und erkennt Breaking Changes automatisch.
7Warum Nutzungstelemetrie wichtig?
Macht die Entscheidung zur Feldentfernung faktenbasiert statt riskant.
8Wie lange deprecated lassen?
Mehrere Monate bis ein Jahr, kombiniert mit Nachweis über Telemetrie, dass niemand mehr zugreift.
9Braucht jedes Team eine Registry?
Kleine Teams: Schema-Diff-Tool in CI reicht. Große föderierte Teams profitieren von zentraler Governance.
10Verhindert GraphQL Breaking Changes automatisch?
Nein, es stellt Werkzeuge bereit, Disziplin und automatisierte Checks bleiben trotzdem notwendig.