Ihre Herausforderung: Entwicklung von APIs – aufwändig und fehleranfällig

Moderne cloud-native Anwendungsarchitekturen bestehen aus einer Vielzahl an Services, die miteinander kommunizieren müssen. Da kann schnell der Überblick verloren gehen. Dabei sollte die Cloud es Teams doch ermöglichen, Änderungen möglichst unabhängig voneinander auszurollen. Gerade bei Schnittstellen sind Abhängigkeiten aber naturgemäß nicht vermeidbar. Die Frage ist daher: Wie lassen sich in solch einem Kontext Schnittstellen effizient und sicher ändern und weiterentwickeln?

Oft ist es das Provider-Team, also das Team, das eine Schnittstelle anbietet, welches die Spezifikation pflegt und für Consumer-Teams bereitstellt. Consumer-Teams implementieren auf Basis dieser Spezifikation.

Dabei kommt es schnell zu Missverständnissen: Consumer-Teams machen falsche Annahmen, etwa weil die Spezifikation unklar ist oder man den Aufwand zusätzlicher Absprachen auf beiden Seiten scheut. Dadurch entstandene Fehler werden in der Regel erst recht spät in einer gemeinsamen Testumgebung entdeckt – ein unabhängiges Ausrollen in Produktion ist unmöglich. Schnell wird dann auch mit dem Finger aufeinander gezeigt.

Provider-Teams müssen aber aufgrund von Anforderungen Schnittstellen weiterentwickeln. Oft ist das nicht ohne aufwändige Abstimmungen mit den Consumern möglich. Dabei ist es gerade in größeren Organisationen nicht selbstverständlich, dass jedes Consumer-Team bekannt ist und Ansprechpartner verfügbar sind. Um Verzögerungen zu vermeiden und den Überblick zu behalten ist ein häufig gewählter Ausweg die Versionierung der Schnittstellen. Inkompatible Änderungen werden dann mit einer neuen Version der Schnittstelle eingeführt. Das führt dazu, dass Provider-Teams oft mehrere Versionen pflegen müssen, um die Kompatibilität mit nicht oder nur langsam weiterentwickelten Consumern zu bewahren.

So ergeben sich zwei Problembereiche:

  1. Korrektheit: Provider- und Consumer-Teams müssen die Kompatibilität mittels Integrationstests in einer gemeinsam betriebenen Testumgebung sicherstellen. Solch eine Testumgebung muss aufgesetzt, gewartet und mit abgestimmten Testdaten versehen werden. Ein erheblicher Aufwand, vor allem wenn man berücksichtigt, dass Kompatibilitätsprobleme bei dieser Vorgehensweise erst nach der Entwicklung und damit relativ spät entdeckt werden.
  2. Evolvierbarkeit: Änderungen der Schnittstellen sind mit hohem Aufwand verbunden. Alle Consumer müssen identifiziert und abgeholt werden. Oft lässt es sich nicht vermeiden, mehrere Versionen einer Schnittstelle weiter zu betreiben und zu pflegen.

Was ist ein Consumer-Driven Contract?

Ein Contract stellt eine Übereinkunft zwischen einem Provider und einem Consumer über die Definition einer Schnittstelle dar. Wichtig ist, dass der Contract eindeutig beschrieben ist und sich beide Seiten einig sind.

Contract zwischen Consumer und Provider.Quelle: eigene Darstellung

Kerngedanke der Consumer-Driven Contracts ist, dass Contracts aus Consumer-Sicht geschrieben sind. Stellt ein Provider Elemente in seiner Schnittstelle zur Verfügung, die für den Consumer nicht relevant sind, so sind diese nicht Teil des Contracts. Der Provider kann sie jederzeit ändern, ohne die Übereinkunft mit dem Consumer zu verletzen.

Da die Schnittstelle eines Providers in der Regel mehrere Consumer hat, hält ein Provider mit jedem dieser Consumer einen Contract. Ein Consumer-Driven Contract ist dann die Vereinigung aller Contracts, die ein Provider für eine Schnittstelle geschlossen hat. Im Gegensatz zu einer vom Provider getriebenen API-Spezifikation hat ein Consumer-Driven Contract zwei wichtige Eigenschaften:

  1. Alle Consumer stimmen zu, dass der Consumer-Driven Contract ihre Übereinkunft mit dem Provider korrekt abbildet.
  2. Der Consumer-Driven Contract enthält nur Elemente, die auch von mindestens einem Consumer benötigt werden.

Dieser Formalismus hat einen guten Grund: ein Contract lässt sich in einem maschinenlesbaren Format, beispielsweise in JSON, ausdrücken. Dadurch werden Prozesse, die mit Contracts arbeiten, zugänglich für Automatisierung. Das verhindert Missverständnisse und ermöglicht es, die Eigenschaften eines Contracts mit automatischen Tests zu verifizieren.

Die Vorteile, die sich dadurch ergeben, sind:

  • Wir können Contracts automatisch generieren. Ein Beispiel dazu kann folgendem Blog entnommen werden
  • Wir können automatisch verifizieren, ob ein Provider einen Consumer-Driven Contract erfüllt.
  • Wir können automatisch überprüfen, ob ein Schnittstellenelement tatsächlich von einem Consumer benötigt wird oder entfernt werden kann.

Was ist Consumer-Driven Contract Testing?

Consumer-Driven Contract Testing verwendet die Contracts für die automatisierte Validierung der Kompatibilität zwischen dem Provider und dessen Consumern.

In einem ersten Schritt werden zuerst die Contracts aus Consumer Sicht erzeugt. Im Falle von PACT, einem Open-Source Framework für Consumer-Driven Contract Testing, werden dazu Tests für den Consumer gegen einen simulierten Provider erstellt. Beim Ausführen der Tests wird zusätzlich ein Contract generiert, der explizit beschreibt, welche Erwartungen der Consumer an die Antworten des Providers in definierten Szenarien hat. Dieser Contract wird an das Entwicklungsteam des Providers übergeben.

Das Provider-Team kann nun alle vereinbarten Contracts in Tests verwenden. Diese Tests stellen sicher, dass das aktuelle Verhalten des Providers den Erwartungen der Consumer entspricht. So werden nicht nur die Annahmen der Consumer über den Provider auf Korrektheit geprüft. Es wird auch sichergestellt, dass der Provider bei Änderungen an den Schnittstellen weiter kompatibel zu jedem Consumer bleibt.

Consumer Driven Contract Testing.Quelle: eigene Darstellung

Wie der folgenden Abbildung zu entnehmen ist, laufen die Contract Tests in Continuous Integration (CI) Umgebungen beider Parteien: Provider und Consumern. Sie erfolgen unabhängig von der Gegenseite und erfordern deshalb keine gemeinsame Integrationsumgebung. Die vereinbarten Contracts werden dabei über ein gemeinsames Repository ausgetauscht.

Mehr dazu erfahren Sie in unserem Blog „Service Mocking oder Consumer-Driven Contract Testing?”

Unabhängige automatisierte Tests von Consumer und Provider.Quelle: eigene Darstellung

Hat ein Consumer Bedarf einen bestehenden Contract zu erweitern, beispielsweise Aufgrund neuer Anforderungen, so kann er dies erstmal autark bei sich machen. Nachdem der aktualisierte Contract dann vom Provider – idealerweise automatisiert – bestätigt wurde, kann sich der Consumer sicher sein, dass Kompatibilitätsprobleme praktisch ausgeschlossen sind.

Erfahren Sie mehr über Consumer-Driven Contract Testing

Einordnung von Contract-Tests in die Testpyramide

In klassischen Integrationstests benötigt der Consumer einen realen Provider. Immer einen echten Service aufzurufen, macht die Tests allerdings fragil. Der übliche Lösungsansatz für dieses Problem ist der Einsatz von Servicesimulation: Consumer testen gegen simulierte Provider. Doch laufen Entwickler schnell in die Situation, dass das simulierte Verhalten veraltet und der Test fälschlicherweise erfolgreich ist. Daher benötigt der Consumer ein Werkzeug, welches die Korrektheit der Simulation gegen den echten Provider validiert.

Contract-Tests adressieren diese Problemstellung. Die Tests laufen gegen das im Contract spezifizierte Verhalten des Providers. Parallel dazu wird der Contract auf der Seite des echten Providers validiert. Anhand des Contracts entsteht so der besondere Vorteil, dass beide Seiten isoliert voneinander ihre Integrationsfähigkeit prüfen können. Das macht Contract-Tests im Prinzip zu Integrationstests, welche aber von den Vorteilen der Servicesimulation profitieren.

Eine ausführliche Beschreibung zu diesem Thema finden Sie im Blog „Service Mocking oder Consumer-Driven Contract Testing”.

Ihre Vorteile durch den Einsatz von Consumer-Driven Contracts

Ein Umdenken von auf Seite des Providers erstellten API-Spezifikationen zu Consumer-Driven Contracts bringt eine Reihe von Vorteilen. Aus der Vogelperspektive betrachtet entsteht dadurch ein schlanker, verteilter Prozess zur Weiterentwicklung und Sicherstellung der Kompatibilität von Schnittstellen. Das funktioniert auch dann effizient und ohne Verschwendung, wenn eine wachsende Anzahl an Schnittstellen gepflegt werden muss.

Die Vorteile im Überblick

  • Unabhängiger Rollout von Services: Die Kompatibilität von Schnittstellen wird automatisiert sichergestellt, ohne dass eine gemeinsame Integrationsumgebung benötigt wird.
  • Frühe Erkennung von Fehlern: Kompatibilitätsprobleme werden noch während der Entwicklung erkannt und können direkt behoben werden.
  • Keine unnötigen Absprachen: Tritt ein Fehler auf, weiß das Provider-Team unmittelbar, welchen Contract sie brechen würden und mit welchen Consumer-Teams sie sprechen müssen.
  • Evolutionäre API-Entwicklung: Durch Identifikation nicht verwendeter Schnittstellenelemente entfällt die Notwendigkeit einer expliziten Schnittstellenversionierung.
  • Leichtgewichtige Integrationstests: Dank der kontinuierlichen und automatischen Validierung der Consumer Contracts werden aufwändige und manuelle Integrationstests und sogar E2E-Tests überflüssig.

Unser Beratungsangebot zu Consumer-Driven Contracts

Eine Beratung zur Einführung von Consumer-Driven Contracts führen wir in drei aufeinander aufbauenden Stufen durch:

  1. Initialer Workshop: Wir erarbeiten gemeinsam eine Fakten-Grundlage für die Entscheidung, ob Consumer-Driven Contracts aktuell einen Mehrwert für Sie bieten. Dafür verschaffen wir uns ein Bild Ihrer bestehenden Teststrategie sowie der Build- und Delivery-Infrastruktur. Idealerweise nehmen ein Architekt, Test- und Infrastruktur-Verantwortliche sowie Mitglieder eines Provider- und Consumer-Teams an dem Workshop teil.
    Ihr Mehrwert: Sie erhalten ein Bild der bestehenden Koordinationsprobleme in Ihrer Delivery-Pipeline, welches Ihnen Ansatzpunkte und Verbesserungspotentiale aufzeigt.
    Sind wir der Meinung, dass Consumer-Driven Contracts eine Lösung für Sie darstellen, bieten wir einen Proof-of-Concept an.
  2. Proof of Concept: Wir validieren die aus dem initialen Workshop gewonnenen Erkenntnisse und entwickeln eine Strategie für die Einführung von Consumer-Driven Contracts in Ihrem Kontext. Dafür stimmen wir Technologieentscheidungen und Erweiterungen der Build- und Delivery-Infrastruktur mit den verantwortlichen Stakeholdern ab. Wir nehmen einen technologischen Durchstich an einer ausreichend komplexen und geschäftskritischen Schnittstelle vor.
    Ihr Mehrwert: Sie erhalten die Sicherheit, dass unser Ansatz in Ihrem Kontext funktioniert. Eingebundene Entwicklungsteams erhalten technologischen und methodischen Einblick, um den demonstrierten Testansatz weiterverfolgen zu können. Wir geben Ihnen Empfehlungen für die weitere Einführung in Ihrem Unternehmen.
    Sollten Sie Bedarf an weiterer Unterstützung sehen, können wir Ihnen Begleitung bei der Umsetzung anbieten.
  3. Umsetzungsbegleitung: Wir vermitteln weiteren Teams das notwendige methodische und technologische Wissen für eine erfolgreiche Einführung von Consumer-Driven Contracts. Dafür bieten wir Schulungen und Coaching bei der konkreten Umsetzung an.
    Ihr Mehrwert: Consumer-Driven Contracts und evolutionäre API-Entwicklung werden nachhaltig in Ihrem Kontext verankert.

Ihre Ansprechpartner

Axel Schüssler

Managing Consultant
Inhaltsverzeichnis
Ihr Ansprechpartner Axel Schüssler Managing Consultant