API-Vertragsbrüche automatisch in CI erkennen mit OpenAPI-Diff-Tools
AI generated
{ }
GET
REST · OpenAPI · CI/CD
API-Vertragsbrüche automatisch in CI erkennen
Wie OpenAPI-Diff-Tools Vertragsbrüche erkennen, bevor sie Konsumenten erreichen

Ein Breaking Change in einer öffentlichen API entsteht meist nicht absichtlich, sondern schleicht sich in einem unauffälligen Pull Request ein, der eigentlich nur einen internen Bugfix beheben sollte. Diagnose-Tools wie oasdiff und openapi-diff vergleichen zwei Versionen einer OpenAPI-Spezifikation automatisiert und erkennen genau solche Vertragsbrüche zuverlässig, bevor sie gemergt werden. Dieser Artikel zeigt den Unterschied zu reinem Spectral-Linting, wie ein CI-Gate konkret aufgebaut wird und wie bewusst gewollte Breaking Changes über einen Whitelist-Mechanismus dokumentiert freigegeben werden.

16 Min. Lesezeit OpenAPI · oasdiff CI/CD-Gates

1. Warum stille Breaking Changes ein wiederkehrendes Problem sind

Ein Breaking Change in einer öffentlichen API entsteht selten absichtlich böswillig, sondern meist aus Unachtsamkeit: Ein Pflichtfeld wird umbenannt, ein Enum-Wert wird entfernt, ein Response-Feld ändert seinen Typ von String zu Integer, und all das passiert in einem Pull Request, der eigentlich nur einen internen Bugfix beheben sollte. Ohne automatisierte Prüfung bemerkt das Team diese Änderung oft erst, wenn ein externer API-Konsument sich meldet, weil sein Client plötzlich mit einem Parsing-Fehler abstürzt.

Manuelles Code-Review allein reicht hier nicht aus, weil ein API-Vertragsbruch häufig in einem einzigen, unauffälligen Diff-Zeilenpaar versteckt ist, das ein menschlicher Reviewer unter Dutzenden anderer Änderungen leicht übersieht. Genau hier setzen OpenAPI-Diff-Tools an: Sie vergleichen die Spezifikation zweier API-Versionen automatisiert, maschinell und zuverlässig, und markieren jede Änderung, die für bestehende Konsumenten inkompatibel sein könnte, bevor sie überhaupt gemergt wird.

2. Linting mit Spectral versus Vertragsvergleich mit Diff-Tools

Spectral und Diff-Tools wie oasdiff werden in der Praxis oft verwechselt, obwohl sie fundamental unterschiedliche Aufgaben lösen. Spectral ist ein Linter: Es prüft eine einzelne OpenAPI-Spezifikation gegen einen Satz von Stilregeln, etwa ob jeder Endpunkt eine Beschreibung hat, ob Property-Namen konsistent in camelCase geschrieben sind oder ob jeder Response-Code dokumentiert ist. Spectral kennt dabei keine vorherige Version der API, es bewertet ausschließlich den aktuellen Zustand isoliert.

Ein Diff-Tool wie oasdiff oder openapi-diff verfolgt ein anderes Ziel: Es vergleicht zwei konkrete Versionen derselben Spezifikation, typischerweise den Stand auf dem Ziel-Branch gegen den Stand im Feature-Branch, und identifiziert semantische Unterschiede zwischen ihnen. Ein perfekt lint-konformes Schema kann trotzdem einen gravierenden Breaking Change enthalten, wenn beispielsweise ein zuvor optionales Feld plötzlich als required markiert wird, denn das ist stilistisch einwandfrei, aber vertraglich inkompatibel. Linting und Diffing ergänzen sich deshalb, ersetzen sich aber nicht gegenseitig.

3. oasdiff im Detail: Funktionsweise und Ausgabe

Bevor ein Diff-Tool überhaupt etwas vergleichen kann, braucht es zwei konkrete OpenAPI-Dateien: eine für den aktuellen Stand des Ziel-Branches und eine für den Stand im Pull Request. In einer Symfony-Anwendung, die ihre API-Dokumentation über NelmioApiDocBundle aus Attributen generiert, lässt sich dieser Export über einen eigenen Konsolenbefehl automatisieren, der in der CI-Pipeline vor dem eigentlichen Diff-Schritt läuft.

oasdiff selbst ist ein Kommandozeilenwerkzeug, das zwei OpenAPI-Dokumente (YAML oder JSON) als Argumente entgegennimmt und eine strukturierte Liste an Änderungen ausgibt, kategorisiert nach Schweregrad: info, warning und breaking. Die Kategorisierung folgt der OpenAPI-Semantik selbst, oasdiff weiß beispielsweise, dass das Hinzufügen eines neuen optionalen Response-Felds harmlos ist, während das Entfernen eines bestehenden Felds oder das Verschärfen einer Validierungsregel als breaking eingestuft wird.


<?php
declare(strict_types=1);

namespace App\Command;

use Nelmio\ApiDocBundle\ApiDocGenerator;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;

/**
 * Exportiert die aktuelle OpenAPI Spezifikation als YAML Datei,
 * damit sie in der CI Pipeline gegen den Stand des Ziel Branches verglichen werden kann.
 */
#[AsCommand(name: 'api:openapi:export')]
final class ExportOpenApiSpecCommand extends Command
{
    public function __construct(
        private readonly ApiDocGenerator $apiDocGenerator,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $spec = $this->apiDocGenerator->generate();

        file_put_contents(
            'var/openapi/current.yaml',
            Yaml::dump($spec->toArray(), 6),
        );

        $output->writeln('OpenAPI Spezifikation nach var/openapi/current.yaml exportiert.');

        return Command::SUCCESS;
    }
}

4. openapi-diff als Alternative: Unterschiede zu oasdiff

openapi-diff (ursprünglich von OpenAPITools als Java-basiertes Tool veröffentlicht) verfolgt denselben Grundgedanken wie oasdiff, unterscheidet sich aber in Implementierung und Ökosystem. Während oasdiff als Go-Binary sehr schnell startet und sich einfach in Docker-Images oder GitLab-CI-Jobs einbinden lässt, läuft openapi-diff auf der JVM, was in reinen PHP-Symfony-Umgebungen eine zusätzliche Laufzeitabhängigkeit bedeutet, die separat gepflegt werden muss.

Inhaltlich liefern beide Tools vergleichbare Ergebnisse bei der Erkennung klassischer Breaking Changes wie entfernten Endpunkten, geänderten Pflichtfeldern oder verschärften Enum-Einschränkungen. Der praktische Unterschied liegt eher in der Ausgabeformatierung und der Integrationsreife: oasdiff bietet von Haus aus ein maschinenlesbares JSON-Format, das sich direkt in einem CI-Skript auswerten lässt, während openapi-diff primär für die HTML-Berichtsausgabe optimiert ist und für eine automatisierte Gate-Prüfung mehr Anpassung erfordert.

5. Ein CI-Gate, das PRs bei unangekündigten Breaking Changes blockiert

Ein CI-Gate für API-Vertragsbrüche funktioniert nach einem einfachen Prinzip: Der Pipeline-Job lädt die OpenAPI-Spezifikation des Ziel-Branches (meist main oder develop), generiert die Spezifikation des aktuellen Feature-Branches, führt oasdiff gegen beide aus und bricht den Job mit einem Fehlercode ab, sobald mindestens eine als breaking eingestufte Änderung gefunden wird. Der Pull Request kann dadurch technisch nicht gemerged werden, solange dieser Job als Required-Check konfiguriert ist.

Entscheidend für die Akzeptanz im Team ist, dass die Fehlermeldung im CI-Log konkret und verständlich ist, also nicht nur 'breaking change detected', sondern die genaue Property, den betroffenen Endpunkt und die Art der Änderung nennt. Nur so kann ein Entwickler in wenigen Sekunden einschätzen, ob die Änderung tatsächlich unbeabsichtigt war oder ob sie bewusst gewollt ist und über den weiter unten beschriebenen Whitelist-Mechanismus freigegeben werden muss.


openapi-breaking-check:
  stage: test
  image: tufin/oasdiff:latest
  script:
    - git show origin/main:openapi/spec.yaml > /tmp/base.yaml
    - oasdiff breaking /tmp/base.yaml openapi/spec.yaml
      --fail-on ERR
      --exclude-ops-with-extension "x-breaking-allowed"
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

6. Was als Breaking Change gilt und was nicht

Nicht jede Schemaänderung ist automatisch ein Breaking Change, und ein zu aggressiv konfiguriertes Diff-Tool erzeugt schnell Alarm-Müdigkeit im Team. Als eindeutig breaking gelten unter anderem: das Entfernen eines Endpunkts oder einer HTTP-Methode, das Hinzufügen eines neuen Pflichtfelds zu einem Request-Body, das Entfernen eines Response-Felds, auf das Konsumenten sich verlassen könnten, sowie das Verschärfen von Validierungsregeln wie Minimal- oder Maximallängen.

Nicht breaking sind dagegen das Hinzufügen eines neuen optionalen Felds, das Hinzufügen eines neuen Endpunkts, das Lockern einer Validierungsregel (etwa eine Maximallänge erhöhen statt senken) oder das Hinzufügen eines neuen, zusätzlichen Enum-Werts in einer Response, sofern der Client für unbekannte Enum-Werte tolerant implementiert ist. Diese Unterscheidung folgt dem Grundsatz, dass eine Änderung dann breaking ist, wenn ein bestehender, korrekt implementierter Client durch sie kaputtgeht, unabhängig davon, ob die Änderung inhaltlich sinnvoll oder gewollt war.

7. Whitelist-Mechanismus für bewusst akzeptierte Breaking Changes

In der Praxis gibt es legitime Fälle, in denen ein Breaking Change bewusst gewollt ist, etwa beim geplanten Entfernen eines seit Langem als deprecated markierten Felds im Rahmen einer angekündigten Major-Version. Ein starres CI-Gate, das jeden Breaking Change ausnahmslos blockiert, würde solche geplanten Änderungen ebenfalls verhindern und das Team dazu verleiten, die Prüfung im Zweifel zu deaktivieren, was den eigentlichen Schutz zunichtemacht.

Der praktikable Ansatz ist ein Whitelist-Mechanismus: Ein Entwickler, der einen Breaking Change bewusst einführen will, trägt die betroffene Änderung explizit in eine Ausnahmeliste ein (etwa über eine Extension wie x-breaking-allowed im Schema oder eine separate Konfigurationsdatei) und muss im Pull Request eine kurze Begründung liefern, warum die Änderung notwendig und angekündigt ist. Das CI-Gate erkennt die Ausnahme, lässt die Pipeline grün werden, aber die Änderung bleibt im Review-Verlauf sichtbar und nachvollziehbar dokumentiert, statt stillschweigend zu passieren.

8. Einbindung in den Team-Workflow: PR-Begründung und Review

Damit der Whitelist-Mechanismus nicht zur bequemen Umgehung des Gates verkommt, sollte jede Ausnahme im Pull-Request-Workflow an eine verpflichtende Begründung und im Idealfall an eine zweite Reviewer-Freigabe gekoppelt sein. Ein einfaches Muster ist ein Pull-Request-Template mit einem eigenen Abschnitt 'Breaking Changes', der ausgefüllt werden muss, sobald das CI-Gate eine Ausnahme meldet, und der vom API-Owner oder einem definierten Reviewer-Team explizit gegengezeichnet wird.

Dieser Prozess macht Breaking Changes nicht unmöglich, sondern sichtbar und bewusst getroffen, statt sie versehentlich durchrutschen zu lassen. Für externe API-Konsumenten lohnt es sich zusätzlich, akzeptierte Breaking Changes in einem öffentlichen Changelog zu dokumentieren und mit einer angemessenen Übergangsfrist anzukündigen, bevor die neue Major-Version tatsächlich produktiv geschaltet wird.

9. Grenzen der automatisierten Erkennung im Überblick

Automatisierte Diff-Prüfung ersetzt kein vollständiges Vertragsverständnis, sie erkennt zuverlässig strukturelle Änderungen im Schema, aber keine semantischen Verhaltensänderungen, die sich nicht in der OpenAPI-Spezifikation niederschlagen, etwa eine geänderte Sortierreihenfolge in einer Response-Liste oder eine veränderte Fehlerbehandlung bei Rand-Timeouts. Die folgende Tabelle ordnet die besprochenen Werkzeuge und Mechanismen im direkten Vergleich ein.

Aspekt Spectral oasdiff/openapi-diff CI-Gate mit Whitelist
Prüft Stil einer einzelnen Spec Semantischen Unterschied zwischen zwei Specs Kombination aus Diff-Ergebnis und Ausnahmeliste
Erkennt Breaking Changes Nein Ja, kategorisiert nach Schweregrad Ja, mit bewusster Ausnahmemöglichkeit
Typische Nutzung Vor jedem Commit/PR Gegen Ziel-Branch in der CI-Pipeline Als Required Check vor dem Merge
Reaktion auf Fund Warnung/Fehler im Linting-Report Fehlercode bei breaking Änderung Pipeline-Abbruch außer bei dokumentierter Ausnahme

Mironsoft

OpenAPI-Design, Symfony-APIs und API-Sicherheit

APIs, die externe Teams ohne Rückfragen integrieren können?

Wir prüfen bestehende REST-APIs auf inkonsistente Fehlerformate, fehlende OpenAPI-Dokumentation und Sicherheitslücken und bauen daraus eine API, die klar dokumentiert, versioniert und gegen Missbrauch abgesichert ist.

API-Review

OpenAPI-Spezifikation, Fehlerformate und Statuscodes auf Konsistenz prüfen.

Symfony-Umsetzung

DTOs, Serializer und Validator für saubere, typsichere Request/Response-Modelle einsetzen.

Security-Audit

Rate-Limiting, Auth-Schemes und Input-Validierung gegen echte Angriffsflächen absichern.

10. Zusammenfassung

OpenAPI-Diff-Tools: Das Wichtigste auf einen Blick

Kernunterschied

Spectral prüft den Stil einer einzelnen Spezifikation, Diff-Tools wie oasdiff vergleichen zwei Versionen und erkennen echte Vertragsbrüche.

Praktisches Werkzeug

oasdiff als schnelles Go-Binary lässt sich direkt in CI-Pipelines einbinden und liefert maschinenlesbares JSON.

CI-Gate-Prinzip

Der Pipeline-Job blockiert den Merge, sobald eine als breaking eingestufte Änderung ohne Freigabe gefunden wird.

Whitelist-Regel

Bewusst gewollte Breaking Changes werden explizit eingetragen und im Pull Request begründet, statt das Gate zu umgehen.

11. FAQ: OpenAPI-Diff-Tools: Das Wichtigste auf einen Blick

1Was ist der Unterschied zwischen Spectral und oasdiff?
Spectral prüft eine einzelne OpenAPI-Spezifikation gegen Stilregeln, oasdiff vergleicht zwei Versionen derselben Spezifikation und erkennt semantische, vertragsbrechende Unterschiede. Beide Werkzeuge ergänzen sich, ersetzen sich aber nicht.
2Erkennt oasdiff auch das Entfernen eines Pflichtfelds?
Ja, das Entfernen eines Response-Felds oder das Hinzufügen eines neuen Pflichtfelds im Request werden von oasdiff standardmäßig als breaking eingestuft.
3Muss ich meine OpenAPI-Spec manuell pflegen, um sie zu diffen?
Nein, in einer Symfony-Anwendung mit NelmioApiDocBundle lässt sich die Spezifikation automatisiert aus Code-Attributen generieren und in der CI-Pipeline exportieren, bevor der Diff-Schritt läuft.
4Was passiert, wenn ein Breaking Change bewusst gewollt ist?
Über einen Whitelist-Mechanismus wird die Änderung explizit als Ausnahme eingetragen, mit einer Begründung im Pull Request, damit das CI-Gate die Pipeline trotzdem grün werden lässt.
5Ist openapi-diff eine gute Alternative zu oasdiff?
Funktional ja, allerdings läuft openapi-diff auf der JVM, was in reinen PHP-Umgebungen eine zusätzliche Laufzeitabhängigkeit bedeutet. oasdiff als Go-Binary ist meist einfacher in bestehende CI-Pipelines zu integrieren.
6Blockiert das CI-Gate auch harmlose Änderungen wie neue optionale Felder?
Nein, das Hinzufügen eines neuen optionalen Felds oder eines neuen Endpunkts gilt nicht als breaking und wird vom Gate durchgelassen.
7Wie verhindere ich, dass die Whitelist zur bequemen Umgehung wird?
Indem jede Ausnahme eine verpflichtende schriftliche Begründung im Pull Request erfordert und idealerweise von einem zweiten Reviewer oder dem API-Owner gegengezeichnet werden muss.
8Kann ich das CI-Gate auch für interne APIs einsetzen, die nur ein Team konsumiert?
Ja, sinnvoll ist es vor allem, sobald mehrere Teams oder externe Partner von der API abhängen. Bei einer rein internen, von einem einzigen Team kontrollierten API ist der Nutzen geringer.
9Was ist der Unterschied zwischen einem breaking und einem non-breaking Change bei Enums?
Das Entfernen eines bestehenden Enum-Werts gilt als breaking, das Hinzufügen eines neuen Enum-Werts in einer Response gilt meist als non-breaking, sofern der Client tolerant gegenüber unbekannten Werten implementiert ist.
10Erkennt ein OpenAPI-Diff auch Verhaltensänderungen, die nicht im Schema stehen?
Nein, Diff-Tools prüfen ausschließlich die strukturelle Spezifikation. Semantische Verhaltensänderungen wie eine geänderte Sortierreihenfolge werden nicht erkannt, dafür braucht es zusätzliche Contract- oder Integrationstests.