Zum Inhalt springen
Zurück zum Blog

„Letzte Woche lief's noch": API Drift erkennen, bevor die Production bricht

13 Min.
„Letzte Woche lief's noch": API Drift erkennen, bevor die Production bricht

Einleitung

Vor kurzem saß ich in einem Meeting mit Kunden. Es ging um die Anbindung an Services und APIs von anderen Parteien in ihrem großen Konzern. Einer ihrer Entwickler, nennen wir ihn Frustrated Fred, schimpfte über den ständig wandernden Boden, der die Integrationsarbeit so kompliziert machte.

"Ich weiß, dass das vor zwei Wochen funktioniert hat. Und jetzt nicht mehr."

Und er schob jenes ewige Schreckgespenst des Cloud Computing hinterher:

"Irgendwas muss sich an den Permissions geändert haben."

Genau so sehen unsere sich rasant verändernden Arbeitsumgebungen aus. Buchstäblich hunderte Teams scrummen sich jede Woche durch ihre Kanban-Boards und releasen, ebenso buchstäblich, hunderte Änderungen und Kniffe an den Oberflächen ihrer Angebote. All diese Oberflächen sitzen auf Libraries und Komponenten, die selbst durch Versionsnummern mahlen, als wäre das ihre eigentliche Daseinsberechtigung. In solchem Fluss zählt Experimentieren wenig und Erfahrung vielleicht noch weniger.

Technische Risikobewertung fasziniert mich seit langem, und das hier ist eine spannende, herausfordernde Umgebung. Dieser Blogpost ist meine Analyse davon, wie wir hierher gekommen sind, was dieser Druck emotional mit uns macht, warum unsere aktuellen Lösungen versagt haben, und wie wir ihre Stärken zu neuem Tooling kombinieren können, das hilft, diese Erfahrungen zu stabilisieren.

Wenn meine Ahnungen halbwegs stimmen, hätte mein Kumpel Frustrated Fred innerhalb von 24 Stunden herausgefunden, dass eine gut gemeinte Aktion seine Erwartungen durchkreuzt hat, und nicht erst zwei Wochen später, als es darauf ankam. Vielleicht liest dieser frustrierte Kollege sogar diesen Blogpost?

Falls ja: Bro, der hier ist für dich!

Wie wir hierher kamen

Angesichts der schieren Zahl an Maschinen, Services und Softwarekomponenten musste sich Enterprise Cloud Computing von imperativen Denkmodellen wegbewegen. Irgendeine Disk, irgendein Lüfter, irgendein Installer würde immer abstürzen. Große Ansammlungen von Teilen erleben pausenlos Drift zwischen dem, wie die Welt sein sollte, und dem, wie sie ist.

Infrastructure as Code, umgesetzt in Terraform/OpenTofu (TF) oder Kubernetes Operators, erlaubte Cloud Engineers, darüber zu reden, was sie wollten. Der Fokus verschob sich auf die "Soll"-Welt statt darauf, wie man dorthin kommt. Absichten ersetzten Aktionen als Beschreibungseinheit. Resources statt VMs, Permissions oder Accounts wurden zur Einheit des Denkens.

Nur … irgendwann muss ein Agent etwas tun. Wir können die Aktionen den Stack hinunterschieben, weg von den Designdokumenten und dem Terraform-Code. Aber irgendwo muss, wie die Amerikaner in ihrer autoverliebten Art sagen, der Gummi der Reifen die Straße berühren.

Und was wir lernten, oft auf die harte Tour, war, dass die Realität der APIs weder so stabil noch so unkompliziert zu benutzen war. Unsere ordentlichen Stapel aus Architecture Decision Records und validierten Terraform-HCL-Dateien wollten auf den glitschigen Oberflächen dieser kniffligen Integrationspunkte nicht gerade stehen. Punkte, die wir brauchten, um unsere Cloud-Vision bereitzustellen.

Intensions vs Realities
intensions vs realities
Intensions vs Realities

Die Scham des Missverständnisses

Ich erinnere mich lebhaft an den Ärger, meinen Einsatz auf diese API gesetzt zu haben, über zwei Designentwürfe hinweg, nur um beim Schreiben des TF-Moduls festzustellen, dass mir die Default-Einstellungen um die Ohren flogen.

Das ist doppelt schlecht: nicht nur peinlich, sondern auch ein Anreiz für schlechte Impulse. Warum überhaupt dutzende Versionen eines Designdokuments schreiben, Feedback von Beteiligten und Review-Meetings einarbeiten, wenn nichts davon einfängt, wie die API tatsächlich funktioniert? Klar, das sind die Gremlins, die uns in die Irre führen wollen. Und es ist eine Kritik, die wir hören müssen.

Menschen meiden Schmerz, Enttäuschungen drängen uns, schneller zur Implementierung zu springen. Klar, wir machen einen Feedback-Loop draus! "Wir finden gerade heraus, wie es wirklich läuft, dann aktualisieren wir das Designdokument mit den neuesten Erkenntnissen! Deal?"

Der wahre Kern dieser Haltung ist, dass technische Risikobewertung notwendig ist. Das Problem bleibt, dass unsere üblichen Lösungen sich als unzureichend erwiesen haben. Meine These: Ihre Fähigkeiten passen schlecht zum Problem.

Warum unsere üblichen Lösungen versagen

Dass unsere üblichen Lösungen versagt haben, zeigt sich schon daran, dass ich diesen Blogpost schreibe. Die interessantere Frage ist, wie es dazu kam. Ich fange mit meinem Werkzeugkasten für die Untersuchung technischer Risiken an. Darin finde ich: den Prototyp, den Tracer Bullet, das Beispiel und natürlich den alten Getreuen, die Erfahrung. Gehen wir sie der Reihe nach durch.

Der Prototyp

Der Prototyp implementiert das ganze Produkt der Erfahrung wegen, schreibt dann das Designdokument und fängt von vorne an. Zumindest offiziell. Kritik am Prototyp, die bis zu Fred Brooks und seinem Mythical Man Month zurückreicht, ist älter als der erste Star-Wars-Film.

Prototyping ohne Design kann ein atemberaubend teurer und zeitraubender Weg zu einem schlechteren Produkt sein. Aber das ist hier nicht unser Thema. Tatsächlich gebe ich gern zu, dass Prototyping die Risikofläche erschöpfend erkundet, wie könnte es auch anders sein? Prototyping bleibt außerdem bei jeder Verschiebung der Risikofläche auf dem Laufenden, solange es im Entwicklungsmodus ist. Das sind echte Stärken.

Das Problem für die technische Risikobewertung ist der massive Overkill. Prototyping tut so viel mehr, als eine Risikofläche abzustecken. Es ist ein bisschen so, als bringe man einen Schlachtkreuzer, um bei Spitz in der Wachau die Donau zu überqueren: einfach zu viel. Und wegen dieses "zu viel" ist es auch nicht lange durchzuhalten.

Der Tracer Bullet

Den Tracer Bullet stelle ich mir immer als Versuch vor, die Vorteile des Prototyps zu ernten, ohne in seiner Teergrube stecken zu bleiben, nämlich dem Bau einer exakten Replik des Endprodukts.

Der Fokus des Tracer Bullet liegt auf dieser beispielhaften Flugbahn: einem Datenfluss durch ein Produkt. Der Tracer Bullet wählt eine interessante Menge an Übergangspunkten und implementiert dann gerade genug, oft über Stubs, damit das funktioniert.

Auch das liefert eine realistische Einschätzung der Risikofläche für das, was im Datenfluss abgedeckt ist. Und zwar für einen Bruchteil des Preises der Replik-Variante.

Und genau da liegt der Haken. Dieser Bruchteil ist proportional zur Abdeckung des Datenflusses. Ist der Pfad zu eng gewählt und damit günstiger, wird die Risikofläche zu spärlich abgetastet. Und weil es nicht mal eine Replik des Produkts ist, bleibt es nicht lange bestehen.

Also zwar billiger als der Prototyp, aber immer noch zu viel für zu wenig Information.

Das Beispiel

Das Beispiel macht einen radikalen Schwenk, indem es den Problemraum atomisiert und sich auf eine Oberfläche konzentriert. Das Beispiel zeigt dann eine Anwendung oder höchstens eine simple CRUD-Sequenz.

Dieser Ansatz trennt sauber zwischen dem Risikobewertungsprozess und dem gesamten Designprozess. Jedes Beispiel lässt sich für sich behandeln. Beispiele können parallel verfolgt werden, ein großartiger Einsatz von Teamressourcen. Es lenkt auch nicht mehr vom Designprozess ab: Sobald es läuft, ist die Arbeit getan und die Aufmerksamkeit kehrt zur Dokumentation zurück.

Also, wo sind die Nachteile? Am wichtigsten: Beispiele, die eine API lehren sollen, haben eine pädagogische Absicht. Sie treffen vereinfachende Annahmen, um Ablenkungen zu minimieren. Sie nutzen jeden halbwegs sinnvollen Default, der das Beispiel gelingen lässt. Kurz: Das Beispiel verzerrt, was das Produkt in Wahrheit brauchen wird.

Aus demselben Grund nutzen Beispiele Spielzeugdaten, nichts dergleichen, womit das fertige Produkt umgehen muss, wenn die User Stories ein Hinweis sind.

Ich weiß das, weil genau das Frustrated Fred und mir passiert ist. Fred war überbucht und brauchte Hilfe, um ein Terraform-Modul abzuhaken. Ich hatte eine freie Stelle im Kalender und bot an, ihm eine minimale Implementierung zu schreiben, die den fraglichen API-Call vorführt.

Es genügt zu sagen: Als Fred weiter im Stress steckte und die noch offene Aufgabe zu mir zurückwanderte, merkte ich schnell, wie wenig hilfreich mein triviales Beispiel gewesen war. Außer irgendeinem Tutorial hatte ich keine Entschuldigung dafür, anzunehmen, ich wüsste, was Fred brauchte. Meine "minimale Lösung", auf Produktionsniveau aufgebohrt, brachte am Ende immer wieder die CI/CD-Jobs zum Absturz, wegen eines toxischen Defaults, den mein Code isoliert nie berührt hatte. Es brauchte das Durchprobieren von zwei Major-Versionen (es crashte in allen) und Debugging auf Go-Ebene beim API-Anbieter, um herauszufinden, wie der realistische Fall hätte aussehen sollen!

Erfahrung

Wir alle verlassen uns auf Erfahrung, um Lösungen zu entwerfen und Designs zu bewerten. Aber in dem Kontext, auf den sich dieser Blogpost konzentriert, dem Nachverfolgen der technischen Risikofläche eines Produkts, wirkt Erfahrung wie die Legende eines Beispiels, das irgendwo und irgendwann mal lief. Erfahrung dient als Existenzbeweis, dass es einmal funktioniert hat. Aber sie beantwortet die Frage nach dem Version Drift nicht. Und es wäre reines Glück, wenn die Daten der aktuellen User Stories dieselbe Form und dieselben Anforderungen hätten wie die Daten damals.

Und weil es oft nur eine Geschichte ist, nicht die Software selbst, kann man sie nicht einfach nochmal laufen lassen.

Diskussion

Klar haben Prototypen, Tracer Bullets, Beispiele oder Erfahrung ihren Platz. Ich argumentiere, dass diese Werkzeuge, selbst gut eingesetzt, das falsche Problem lösen. Sie taugen nicht dazu, die Oberflächenrisiken zu entschärfen, denen Produkte in der modernen, driftenden Softwarelandschaft ausgesetzt sind.

Der Grund, warum diese Werkzeuge genutzt wurden, um die Risikoflächen zu erkunden, auf denen Softwareprodukte laufen, ist, dass sie alle etwas richtig machen. Da keines davon die volle Lösung liefert, nimm ihre Stärken und kombiniere sie zu etwas, mit dem wir technisches Risiko anders angehen können.

Meine Lösung? Technical Risk Probes {#meine-lösung?-technical-risk-probes}

Falls du dich jetzt fragst, was eine Technical Risk Probe ist, brauchst du es nicht zu googeln. Claude Opus und ich haben den Begriff geprägt, weil uns auch nichts Besseres eingefallen ist. Ich verrate dir: Die Zweitplatzierten waren API conceptualization test case und API call prototype. Hat jemand einen besseren Namen dafür? Schreibt mir.

Terminologie mal beiseite, hier ist, was eine Technical Risk Probe erreichen will. Eine Technical Risk Probe will die Oberfläche zum Quietschen bringen. Dein Design sagt X, Y und Z, dein Terraform formuliert diese Behauptung in HCL um, aber die Probe lässt das nicht gelten und will es in Aktion sehen. Schauen wir mal, ob all diese Annahmen auch nur halb wahr sind.

Und wie bauen wir das Doubting Thomas? Denk daran, dass wir uns von den Stärken all der üblichen Lösungen inspirieren lassen wollen, die wir oben besprochen haben. Wir wollen diese Stärken auf neue Weise verpacken, um (viele) ihrer Nachteile zu vermeiden.

Das bessere Beispiel

Im Verlauf der Darstellung war das Beispiel ein klarer Favorit. Erfahrung habe ich als schwächeres Beispiel eingeordnet, während Prototyp und Tracer Bullet für unseren Zweck zu komplex und unhandlich waren.

Das Beispiel verdiente sich unser Lob dadurch, dass es klein, fokussiert und unabhängig ist. Das macht es leicht zu implementieren, anzupassen und zu warten, und zwar wieder parallel.

Ein Kritikpunkt war die pädagogische Vereinfachung, was Implementierung, Default-Annahmen und verwendete Daten angeht. Ein vereinfachtes Beispiel würde nicht testen, was ein reales Beispiel erfordert. Die erste Verbesserung am Beispiel als solchem ist also, es näher an die Bedürfnisse des Produkts zu rücken:

  • Identische Implementierungssprache wie die Deployment-Sprache
  • Identische Library-Version wie fürs Deployment geplant
  • Ein ausreichend komplexer Use Case, besonders was Optionen und Daten angeht, aus den User Stories entnommen

Beiträge von Prototyp und Tracer Bullet

Aber auch Prototyp und Tracer Bullet hatten Stärken. Diese Lösungen erreichen gute Abdeckung, weil sie mehrere verschiedene Dinge testen. Diesen Vorteil können wir nachbilden, indem wir viele Technical Risk Probes schreiben, bei Bedarf parallel.

Außerdem testen Prototyp und Tracer Bullet wegen ihrer langen Entwicklung die bereits integrierten Oberflächen laufend erneut. Diese Eigenschaft fehlt uns. Also legen wir fest, dass die Technical Surface Probes in festen Intervallen erneut laufen. Vielleicht ist nächtlich ein guter Kompromiss zwischen der Geschwindigkeit der Alarmierung und dem Ressourcenaufwand.

Mit dem Wunsch, Probes erneut laufen zu lassen, haben wir uns allerdings eine zusätzliche Anforderung eingehandelt. Wie bei einem Software Test Fixture müssen wir jetzt etwas Setup und Teardown handhaben können. Das macht unsere Probes etwas weniger fokussiert, erhöht aber den Realismus ihres Experimentierens.

Technical Risk Probes bauen

Zusammengefasst ist eine Technical Risk Probe

  • eine unabhängige Softwarekomponente, die
    • … eine fokussierte Oberflächeninteraktion umsetzt (API-Call, Library-Call usw.)
    • … inklusive Setup und Teardown
    • … mit dem angepeilten API-Versionsbereich
    • … in der angepeilten Sprache implementiert
    • … mit aufgabengerechten Defaults
    • … mit echten Daten aus den User Stories

Das reicht. Und wie kommen wir an sie? Deine Designdokumente sagen es dir.

Identifiziere jede notwendige Interaktion mit einer Risikofläche, ob API-Call oder Service-Interaktion.

Für jede Interaktion:

  1. Finde ein Beispiel aus einem Tutorial, Buch, YouTube-Video, Stackoverflow …, oder lass dir von einem LLM eines generieren.
  2. Schreib das Beispiel so um, dass es zu deiner Zielsprache und deinem Ziel-Library-Versionsbereich passt.
  3. Schreib das Beispiel so um, dass es Daten aus deinen User Stories nutzt.
  4. Schreib das Beispiel so um, dass es festhält, wo die Defaults nicht funktionieren.
  5. Füge Setup und Teardown hinzu, damit es immer und immer wieder nutzbar ist.
  6. Bring es zum Laufen.
  7. Verweise im Designdokument auf die Probe (z. B. die URL des Git-Repos).

Höchstwahrscheinlich fangen die Erkenntnisse sofort an zu sprudeln. All die kleinen Details, die die Designs nie erwähnen, Reihenfolge-Constraints, nötiges Scoping, eine Einstellung, die den Unterschied macht zwischen "läuft manchmal" und "läuft zuverlässig", die Probe trommelt sie aus ihren Löchern hervor. Weil sie dein Design bewachen, werden sie dein Design prägen. Verlass dich drauf.

Was jetzt noch fehlt, ist ein nächtlicher Runner, ein Cronjob, eine geplante Pipeline, irgendein Cloud Runner oder eine Function im Framework eines Hyperscalers, deine Wahl.

Und ein kleines Dashboard. Für deine Observability-Leute. Damit sie sagen können: "Alles grün!" Das sagen sie liebend gern.

Noch mehr Vorteile?!?!

Wenn sie vom Observability-Dashboard hören, das täglich ihre technische Risikofläche überwacht, sind die meisten schon überzeugt. Und obwohl das der Kern des Pitches ist, hier die Nebenvorteile.

  1. Dokumentation: Nichts gibt deinem Design so viel Substanz wie ein bisschen lauffähiger Code, der genau das illustriert.
  2. Kommunikationsmittel: Deine Mitstreiter verstehen nicht ganz, was du vorhast? Ein Stück Code, mit dem sie herumspielen können, bringt das Gespräch voran. Im schlimmsten Fall fixen sie deine Probe und schicken sie zurück! Win-win!
  3. Flexibel zu warten: Angenommen, sie fügen eine Library hinzu. Du schreibst noch eine Probe. Sie tauschen dir die Ziel-Library aus, pandas gegen numpy, sagen wir mal? Du schreibst nur diese Probes um. Sie nehmen eine Library raus? Lass die Probes einfach drin. Das sind ein paar Cent auf deiner Cloud-Rechnung und deinen Schlaf wert.
  4. Onboarding und Training: Eine Probe zu schreiben ist ein guter Einstieg ins Projekt. Ein Produktmodul auf Basis einer Probe zu implementieren, ist so naheliegend, dass du vielleicht sogar späte Hilfe annehmen kannst, ohne das Projekt weiter zu verzögern.

Was Probes nicht sind

Du hast es geahnt, ein paar Einschränkungen gibt es! Gibt es die nicht immer?

Aber im Ernst, es gibt einen Grund, warum wir Prototypen und Tracer Bullets haben. Und Probes sind das nicht. Du kannst eine ganze Kette aus wahnsinnig informativen Technical Risk Surface Probes aneinanderreihen. Das sagt dir trotzdem nicht, wie die Daten fließen. Oder ob das ganze Konstrukt überhaupt Sinn ergibt.

Das ist nicht der Punkt. Das ist nicht ihr Job.

Probes sind auch kein Ersatz fürs Testen. Component, System, Integration, End-to-End, Fuzzy, Edge Cases oder welches Testing auch immer. Nochmal, nicht ihr Punkt. Nicht ihr Job.

Sie sagen dir, wenn ein Eisberg auf dich zusteuert. Dafür werden sie bezahlt.

Kurz gesagt

In der sich rasant verändernden Welt des Produktbaus in Cloud-Umgebungen sind Technical Risk Surface Probes Freunde, weil

  • sie eigenständige Softwarekomponenten sind, die
    • … eine einzelne wiederholbare relevante Systeminteraktion umsetzen
    • … auf eine Weise, die implementierungskompatibel mit dem Deployment ist

Sie überwachen laufend den Drift im Oberflächenrisiko des Produkts.

Und dokumentieren, validieren und kommunizieren dabei ganz nebenbei sein Design.

Mehr gibt's nicht zu sagen.

Dieser Blog wurde vom Autor konzipiert und geschrieben. Er kann Vorschläge eines LLM enthalten.

Do it NOW.

Interesse an einer Zusammenarbeit?

Kontakt aufnehmen
Robert C. Kahlert

Robert C. Kahlert ist Senior Software Researcher bei Posedio. Er stammt aus der symbolischen KI-Forschung und hat an Projekten und Produkten in den Bereichen Pharma, Rüstung, Medizin, Natural Language Processing und Rohstofferkundung mitgewirkt. Seine Leidenschaften gelten dem Software Engineering, Compilerbau, Cloud Computing, Wissensrepräsentation und Datenbanken. In seiner Freizeit genießt er die vielfältige Wiener Museumsszene.

Alle Beiträge dieser Person

Ähnliche Beiträge

Werde Teil der Community!

Zum Newsletter anmelden und keine Events, Talks oder Neuigkeiten verpassen.