Beliebte Suchanfragen

Cloud Native

DevOps

IT-Security

Agile Methoden

Java

//

Warum schlechte APIs teuer sind

24.2.2023 | 5 Minuten Lesezeit

Schlechte APIs? Gute APIs? Ist diese Unterteilung überhaupt sinnvoll? Ich glaube, wir müssen mal reden.

Es war einmal ... eine „schlechte“ API

Eine API ist bekanntlich eine Art von Schnittstelle, ausgeschrieben ein sog. „Application Programmable Interface“. Mithilfe solcher Schnittstellen bin ich in der Lage, Daten zu empfangen und diese somit zu „konsumieren“.
Sprichwörtlich stehe ich auf der anderen Seite, sofern ich eine API zur Verfügung stelle, um Informationen auf Anfrage zu „produzieren“. In beiden Fällen muss ich mich dabei mit dieser Information beschäftigen, um diese zu verarbeiten. Zustand und Aufbau dieser Information entscheiden dann darüber, ob ich von einer schlechten oder guten API spreche, wie ich hier erläutern möchte.

Weitere allgemeine Konzepte zum Design und Verstehen von APIs hat mein Kollege Daniel Kocot bereits in seinem Blogpost zusammengetragen.

Ein fiktives Beispiel: Online-Blumenhandel

Wir betreiben eine kleine Gärtnerei und möchten Online-Bestellungen abwickeln können. Dazu benötigen wir unter anderem einen Service, der eine Preisauskunft für eine Bestellung erteilt. Die sonstige Verarbeitung, Versand etc. sind hier nicht weiter wichtig.
Dennoch benötigen wir ein paar Dinge, damit wir einen Preis für unsere Bestellung nennen können.
Im Lager stehen eine kleine Auswahl an Töpfen, Pflanzen und dazu passende Erden und Dünger.
Dabei können wir davon ausgehen, dass die Bestelloberfläche uns die richtigen IDs nennen wird, die wir für den Request benötigen.
Optional kann noch eine entsprechende Topfdekoration gewählt werden, passend zu einem Geburtstag oder einer Danksagung. Diese Sonderwünsche werden von einem anderen Dienstleister bearbeitet.

Bestell-API

  - POST: order?{topftyp}/{pflanze}/{erdtyp}/{duenger}/{deko}
    - topftyp: uuid
    - pflanze: uuid
    - erdtyp: uuid
    - duenger: uuid
    - deko: Enumeration: (keine, dankeschoen, geburtstag)
    Je nach deko-Typ werden bestimmte Folgeobjekte angehangen außer für "keine":
        - "dankeschoen": 
            - vierblaettriges_kleeblatt: boolean, optional
        - "geburtstag":
            - alter: int
        - "aufdruck":
            - text: string, optional
            - grafik: Bild, optional

Eine teure API?

Auffällig ist dabei, dass einige Unterobjekte für die Deko variabel sind. Die Objekte unterscheiden sich nicht nur in den Werten an sich, sondern auch die darin verschachtelten Felder je nach Typ. Der Wert „dankeschoen“ erfordert zwingend das Vorhandensein von „vierblaettriges_kleeblatt“, wohingegen „geburtstag“ das Feld „Alter“ immer erwartet. „aufdruck“ beinhaltet dabei den größten Komplexitätsgrad mit einem optionalen Text-Parameter und einer optionalen Grafik, wobei nur ein Feld von beiden zwingend vorhanden sein muss.

Seitens unserer Architektur müssen wir an jeder Stelle sicherstellen, dass der Code, der mit der Weiterverarbeitung einer Bestellung beschäftigt ist, fehlerfrei funktioniert. Mit steigender Länge und Komplexität möglicher Felder steigt auch der Aufwand für die Tests möglicher Anwendungsfälle. Darüber hinaus wird es schwieriger, alle verschachtelten Anwendungsfälle vollständig abzutesten, wodurch die Qualität sinkt und sich die potentielle Fehlerquote erhöht.

Natürlich wird ein (Fehler-)Risiko erst teuer, wenn dies eintritt. Sobald dies passiert, können aber gerade im Bereich eines Produkts wie einer API hohe Folgekosten auftreten, wie in der sog. Rule of Ten herausgestellt. Dort wurde im Rahmen einer Studie festgestellt, dass sich die Kosten für einen Bugfix praktisch um Faktor 10 erhöhen, je später dieser Fehler gefunden wird. Später bezieht sich hierbei auf den Entwicklungszyklus. Konkret bedeutet es, dass ein Bug, der bei der Entwicklung eines neuen Features gefixt wird, vielleicht 100 EUR kostet, wohingegen die Behebung des Fehlers in Produktion 10.000 EUR kostet.

Außerdem ist die Erweiterbarkeit und Wartung dieser Schnittstelle mit Mehraufwänden verbunden, besonders, wenn Fachlichkeiten mit einer hohen kognitiven Komplexität verbunden sind. Darüber hinaus sind Fehler für Kunden oder Konsumenten einer API meistens auch mit entsprechend nachgelagerten Kosten verbunden, z. B. weil wir im Kontext dieses Blogposts keine Blume verkaufen können.

Verbesserungspotential für „schlechte“ APIs

Um perspektivisch Kosten zu sparen, können verschiedene Maßnahmen hilfreich sein. Prinzipiell gilt dabei: Je strikter und kompakter eine API ist, umso einfacher lässt sich deren Qualität sichern und/oder diese erweitern. Eine solche Striktheit kann man beispielsweise dadurch erreichen, dass man die Anzahl optionaler und nullable Felder möglichst gering hält. Die Verwendung von typisierten Strukturen anstelle von einfachen Strings gehören ebenso dazu. Enumerationen als Typen sind dabei, wie hier gezeigt, mit Vorsicht zu genießen. Ab einer gewissen Größe einer API spielen in den zu berücksichtigenden Kosten auch Dinge wie kognitive Komplexität eine Rolle im Umgang mit einer Schnittstelle. Was heißt das nun konkret für unseren Blumenversand?

Blumenversand 2.0

Mit einem erneuten Blick auf die Schnittstelle lässt sich für die Felder topftyp, pflanze und erdtyp keine unmittelbare Verbesserung ableiten. Sie müssen vorhanden sein. Fehlen diese Werte, können wir die Anfrage mit einem HTTP-400 direkt ablehnen. Darüber hinaus sind UUIDs ein gängiger und weit verbreiteter Standard, somit können wir uns mit dem Teil deko beschäftigen. Die verschachtelte Unterstruktur des deko-Objekts sollte in eigene Endpunkte pro Objekttyp ausgelagert werden. Da der API-Konsument schon vor dem Aufruf der eigentlichen Schnittstelle wissen muss, welchen Typ er bei der Enumeration angibt, könnte er ebenso einen anderen, zweiten Endpunkt ansprechen. Abseits der reinen API-Optimierung würde es sich anbieten, die Auswahl oder Gestaltung der gewünschten Dekoration vor dem Absenden des Bestell-Requests vorzunehmen und diese Deko-Auftragsnummer in Form einer UUID einzusetzen. Darüber hinaus sollte der optionale Teil im „dankeschoen“-Objekt hinterfragt werden. Haben Boolean-Attribute nicht nur zwei mögliche Werte? Nein, hier haben sie sogar drei: true, false und nicht vorhanden. Dabei gibt es hier keinen Grund, auf das false zu verzichten und diesen Teil strikter zu gestalten.

Nach der Umsetzung dieser Optimierungen sieht unsere Bestell-API nun so aus:

  - POST: order?{topftyp}/{pflanze}/{erdtyp}/{duenger}/{deko}
    - topftyp: uuid
    - pflanze: uuid
    - erdtyp: uuid
    - duenger: uuid
    - deko: uuid

und die neue Deko-API wie folgt:

dankeschoen:

POST: deko/danke?{kleeblatt}
- vierblaettriges_kleeblatt: boolean

geburtstag:

POST: deko/bday?{number}
- number: int

aufdruck:

POST: deko/potprint?{text}
- text: string
POST: deko/potprint
- BODY:  grafik: Bild

„Gut“, „schlecht“ und dazwischen ... ein Fazit

Das genannte Beispiel ist auf der einen Seite natürlich konstruiert, um offensichtliche Schwächen aufzuzeigen. Hoffentlich. :)
Dennoch gibt es eine fließende Grenze, die nicht in jedem Fall so schön trennscharf ist oder eine API gar als „schlecht“ abstempelt.
Es gibt auch Fälle, in denen eine andere Lösung aus diversen Gründen nicht möglich oder vom Aufwand her vertretbar ist und das macht diese APIs nicht gleich „schlecht“ im Sinne eines Schwarz-Weiß-Denkens.
Vielmehr geht es um mögliche Kosten, die im Nachhinein entstehen können. Je „schlechter“ ich meine API im obigen Sinn gestalte, umso größer werden Wartung, potentielle Folgearbeiten und Komplexität der Weiterentwicklung. Auch steigt das Risiko, Bugs in den dahinter liegenden Systemen zu erzeugen und diese können gerade bei steigender Zahl der Konsumenten Kosten in Form von Zeit für Nacharbeiten oder Vertrauensverlust mit sich bringen.
Daher sollten Entscheidungen zu generischeren APIs mit Bedacht getroffen und vielleicht einen weiteren Moment überdacht werden.

Beitrag teilen

Gefällt mir

1

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.