Interdisziplinäre Methoden des API-Designs

Grundlagen schaffen: APIs interdisziplinär designen
Keine Kommentare

Auch wenn es einem gestandenen Knochen – der Autor dieser Zeilen zählt sich selbst mit Stolz dazu – weh tut: Die Zeiten der Unternehmen, die im Stil der aus den Abenteuern von Lucky Luke bekannten Firma ACME alles selbst machen, sind lange vorbei. Heute gedeihen primär jene Firmen, die ihre Dienste für Dritte zugänglich machen, um so eigene Entwicklungszeit einzusparen. Außer Frage steht, dass APIs hierbei mehr als hilfreich sind: Sie stehen im Zentrum dieser Bemühungen.

Beim Design von APIs denken Entwickler leider nur höchst selten darüber nach, welchen Einschränkungen die API-Nutzer-Beziehung unterliegt. In der Praxis beschränkt man sich normalerweise darauf, eine technisch gangbare Lösung zu finden. Wenn dann Themen wie Latenz der Kommunikation Eingang finden, ist dies beim durchschnittlichen Entwickler eine große Leistung.

Dieser Artikel bedient sich einer Analyse diverser akademischer Papers, um die interdisziplinären Methoden herauszufinden, die beim Design eines benutzerfreundlichen API helfen. Dies ist in der heutigen Zeit nämlich immens wichtig: Hassen Entwickler die von einem Unternehmen angebotene Programmierschnittstelle, so wandern sie zur Konkurrenz ab. Das kann auch sehr erfolgreiche Firmen treffen. So behaupten bis heute nicht wenige, dass der Untergang des Symbian-Betriebssystems unter anderem darauf zurückzuführen war, dass das API extrem schwierig und stellenweise geradezu entwicklerfeindlich (Stichworte Leaves, Deskriptoren) aufgebaut war.

Eine Frage der Herangehensweise

Die Arbeit an APIs ist insofern haarig, als sie als Entwickler davon ausgehen müssen, dass ein vergleichsweise kleiner Fehler – dies behauptet zumindest Michi Henning in „API: Design Matters“ – ob der „tausendfachen“ Verstärkung massive Konsequenzen entfaltet [1]: „Das Erzeugen eines schlechten API ist sehr einfach, während die Erzeugung eines guten API vergleichsweise schwierig ist. Leider können auch vergleichsweise unschuldige Designfehler massive Konsequenzen haben: Ein API wird nun mal nur einmal entwickelt, aber dann tausende Male genutzt.“

In den folgenden Schritten wollen wir – anfangs – davon ausgehen, dass es in Ihrem Unternehmen noch kein nach außen kommuniziertes API gibt und wir quasi mit einem „Clean Sheet“ beginnen können. Dies ist insofern vorteilhaft, als Sie in dieser Situation keine Rücksicht auf schon etablierten Code nehmen müssen. Insbesondere in großen Konzernen hat man diesen Luxus nur sehr selten. Das bei LG im Jahre 2014 veröffentlichte Paper „An API Design Process in Terms of Usability ist insofern einzigartig, als die Koreaner, bösartige Zungen sprechen hier von mehrfach, mit einem vollkommen neuartigen API ankamen, um ihrem Rivalen Samsung im Smart-TV-Bereich endlich Land zu stehlen. Die Literaturrecherche der Koreaner ergab eine Gruppe von Parametern, die gemeinsam die Qualität eines API beschreiben. Sie finden sich kurz zusammengefasst in Tabelle 1 und folgen – zumindest im Grunde – aus der Logik. Im Paper findet sich zudem eine Gruppe von Punktesystemen, die als Kriterien zur Bewertung von selbst erzeugten APIs eingesetzt werden können.

Bei der Betrachtung von punktesystembasierten Prozessen ist klar, dass der gute alte PDCA-Kreis auch beim Design eines API zu Ehren kommt: Es ist im höchsten Maße unwahrscheinlich, dass der erste Entwurf eines API auch der ist, mit dem man als Unternehmen in die Fertigung gehen sollte.

Kriterium Kurzbeschreibung
Eindeutigkeit Der Name eines Elements des API entspricht seiner Aufgabe.
Bequemlichkeit Ein API belästigt seine Nutzer so wenig wie möglich.
Primitivität Ein API erledigt nur eine Aufgabe.
Dokumentation Ein API ist hinreichend dokumentiert.

Tabelle 1: Vier Kriterien führen – zumindest laut LG – zum Erfolg

In der Literatur findet sich, über Ratschläge hinausgehend, nur wenig Information zu Designprozessen, die zur Entwicklung von APIs vorgesehen sind. Das von Jasmin Blanchette veröffentlichte und als PDF zum Download bereitstehende „The Little Manual of API Design“ ist an dieser Stelle gleich in zweierlei Hinsicht eine Ausnahme. Erstens ist es einer der wenigen Texte, der sich mit der Thematik überhaupt auseinandersetzt. Zweitens kann Blanchette trotz ihrer langjährigen Tätigkeit bei Trolltech, dem Unternehmen hinter dem GUI-Framework Qt, eine Unmenge von Erfahrung im API-Design vorweisen. Schließlich gibt es kaum ein anderes Framework, das dem „Sand der Zeit“ derart Widerstand geleistet hat wie Qt. Und drittens gibt es in der Welt von Qt nur vergleichsweise wenig durch API-Änderungen hervorgerufenes Drama: Das Unternehmen darf mit Fug und Recht als Quelle von Best Practices dienen.

Eins, zwei, hop!

Als erste Amtshandlung empfiehlt Blanchette eine sorgfältige Ermittlung der diversen Requirements: Analog zum klassischen Requirements Engineering gilt auch hier, dass nach Möglichkeit alle Kunden, die irgendwie vom API betroffen sein könnten, in den Entscheidungsprozess eingebunden werden müssen.

Bedenken Sie an dieser Stelle auch, dass Entwickler im Allgemeinen ungehalten reagieren, wenn man den Funktionsumfang von APIs später einschränkt. Es ist also ratsam, auch Marketing und Rechtsabteilung zu befragen, um nicht später rechtliche Probleme zu bekommen oder gar einen Konkurrenten heranzuzüchten, der einen selbst überflüssig macht. Ein klassisches Antipattern wäre das zu weit gefasste erste API von Twitter, das zeitweise die offiziellen Mobile-Clients des Unternehmens kannibalisierte.

Best Practice Nummer zwei ist eigentlich ein klassisches Antipattern: Laut Blanche beginnen Entwickler an dieser Stelle gerne damit „drauf los zu hacken“. Der Lohn dieser Mühen ist normalerweise ein API, das nur wenig einheitlich wirkt und in den diversen Usabilitybenchmarks schlecht bis absolut miserabel abschneiden wird. Eine sauberere Lösung besteht darin, im ersten Schritt eine Gruppe von Use Cases zu erstellen, die verschiedene Nutzungsszenarien des zu erzeugenden APIs abdecken. Diese dienen zur Befeuerung eines uralten Mottos: Mache einfache Dinge einfach, während schwierige Dinge möglich werden. Usabilityfreaks fällt beim Lesen dieser Anweisung sofort das von PalmSource veröffentlichte „Zen of Palm“ ein, in dem unter anderem festgestellt wurde, dass der Schwierigkeitsgrad der Erfüllung einer Aufgabe proportional zum „Intelligenzgrad“ des Ausführenden sein sollte.

BASTA! 2018

Testing in production

mit Nico Orschel (AIT GmbH & Co. KG)

Hinter dieser auf den ersten Blick überheblich klingenden Formulierung verbirgt sich die Idee, dass ein Entwickler, der „nur“ eine Liste von Edelmetallpreisen ausgeben will, normalerweise wesentlich weniger Zeit in das API investieren wird, als ein Entwickler, der an einem Arbitrage-Handelssystem auf Basis der Plattform arbeitet und dementsprechend massives Interesse und Lernbereitschaft mitbringt.

Der dritte Schritt in Blanchettes Anweisungen mag für Clean-Sheet-Designs nicht zutreffen: Im Interesse der didaktischen Komplexität wollen wir ihn trotzdem ansehen. Er empfiehlt an dieser Stelle einen Blick auf ähnliche APIs, sowohl im Markt als auch im hauseigenen Produkt. Microsoft ist an dieser Stelle ein absolutes Antibeispiel. So gibt es beispielsweise in jedem XAML-Dialekt eine eigene Art und Weise, um Data Binding zu realisieren. Dass dies der Freude des Entwicklers nur wenig zuträglich ist, folgt aus der Logik. Zudem spricht nichts dagegen, ein intelligentes Designparadigma von einem Kollegen zu kopieren. Es muss nicht alles, was von einem Unternehmen vertrieben wird, auch wirklich von diesem selbst erfunden worden sein.

An der vierten Stelle findet sich ein klassisches Henne-Ei-Problem: Als Entwickler steht man nun vor der Frage, ob man zuerst die eigentliche Implementierung oder die Dokumentation erstellt. Ich empfehle an dieser Stelle, unbedingt der Dokumentation den Vortritt zu geben: Hinter dieser auf den ersten Blick widersinnig oder gar akademisch klingenden Vorgehensweise verbirgt sich der Gedanke, dass man beim Vorziehen der Implementierung dazu geneigt sein kann, schwierig zu implementierende, aber für den Endanwender leicht verwendbare, Lösungen zu verwerfen und stattdessen auf einfache realisierbare Methoden zurückzugreifen. Dass dies eine schlechte Idee ist, beweist das weiter oben abgedruckte Zitat.

An die fünfte Stelle stellt Blanchette sodann einen Prozess, der Kennern des PDCA-Kreises bekannt vorkommen sollte: Der Entwickler soll damit beginnen, Freunde und Kollegen nach bestem Wissen und Gewissen zu nerven, bis sie Feedback geben.

Nach dem erfolgreichen Durchlaufen dieses Feedbackkreises sind wiederum Sie als Entwickler gefragt: Versuchen Sie, einige Programmierbeispiele zu entwickeln. Hierbei müssen sie nicht unbedingt alleine arbeiten. Wenn sie Kollegen und Geschäftspartner ebenfalls dazu einbinden können, wird das gesammelte Feedback von höherer Qualität sein.

Des Betatesters Dilemma

In Literatur zum Beta Testing wird immer wieder auf das Problem hingewiesen, dass in einer Betaversion erstellte Informationen von den Nutzern zu einem gewissen Zeitpunkt nicht mehr als „zerstörbar“ angesehen werden. Dies ist für den Entwickler insofern hinderlich, als er am Anpassen der Datenstrukturen gehindert wird.

Ein ähnliches Problem kann beim Design eines API auftreten: Der absolute Worst Case wären Horden von Anrufen von wütenden Geschäftspartnern, die wegen einer API-Änderung den gesamten bisherigen Beispielcode wegwerfen müssen. Der einzige Weg zur Umgehung dieses Problems besteht darin, Entwickler immer und immer wieder darauf hinzuweisen. Dass dies – insbesondere bei öffentlichen Tests – die durch API-Änderungen entstehende Unzufriedenheit nicht komplett eliminieren kann, ist ein Argument für „geschlossene“ Betatests mit ausgewählten Entwicklern.

Im Grunde genommen ist der erste Durchlauf des PDCA-Zyklus an dieser Stelle abgeschlossen. Blanchette empfiehlt Entwicklern allerdings, noch drei Kriterien zu überprüfen. Erstens sollten Sie beachten, dass das API erweiterbar sein sollte. Ob man, wie teilweise empfohlen wird, versuchen sollte, für jede Klasse mit virtuellen Methoden zur Überprüfung der Flexibilität drei „reale“ Implementierungen zu finden, sei dahingestellt. Ebenso variiert die Erforderlichkeit dieser Empfehlung von Fall zu Fall.

Empfehlenswert ist es auf jeden Fall, interne APIs nicht einfach so nach außen herauszufeuern: Die falsch geschriebene Funktion SHStripMneumonic im Win32-API ist ein vergleichsweise moderates Beispiel dafür, was bei Zuwiderhandlung dieser Anweisung passieren kann. Zudem warnt Blanchette davor, dass APIs nicht übermäßig komplex ausfallen sollten: Ein von der Nutzerschaft in keiner Weise verlangtes Feature sollte nicht freigegeben werden, da es die Dokumentation länger macht und das Produkt dann schwieriger zu erlernen erscheint.

Auf ins Technische!

Empfehlungen geben den Weg vor, der zu einem lebensfähigen API führt. An der Carnegie Mellon Universität arbeitende Forscher schlugen 2007 eine grafische Repräsentation vor, die die verschiedenen, beim Design eines API auftretenden technischen Fragen in eine Gruppe von Räumen unterteilt [2].

Das Problem ist hierbei, dass die einzelnen Fragen mehrere Bereiche tangieren. Als grobe Namensräume gehen die Strukturanalyse und der Aufbau der individuellen Klassen hervor. Die beiden sind allerdings nur teilweise voneinander getrennt: Design Patterns haben die unangenehme Eigenschaft, sowohl die individuellen Klassen als auch den Gesamtaufbau zu tangieren (Abb. 1).

Abb. 1: Auch die einzelnen Entscheidungen beim Implementieren eines API sind interdisziplinär

Abb. 1: Auch die einzelnen Entscheidungen beim Implementieren eines API sind interdisziplinär

Die im Haus LG im oben erwähnten Paper aufgestellte Numerik dürfte funktionieren, wird im Paper aber nur teilweise vorgestellt. An der TU Wien beschäftigten sich Scheller und Kühn mit einem mathematischen Verfahren, das die Bewertung von APIs transparent gestalten sollte und – mit etwas Fantasie – auch zur Ermittlung von guten und schlechten Designideen herangezogen werden kann [3].

Beginnen wir mit der Betrachtung von fünf Parametern, die als „High-Level“ eingestuft wurden. Die Anzahl der gesamten Member des API, also der Funktionen, Typen und Co., wird als von vergleichsweise geringer Bedeutung eingeschätzt. Die Erhöhung eines API um 90 Member führte nicht zu einer wesentlichen Steigerung der Suchdauer. Der Einwand, warum dies in Zeiten von IntelliSense und Co. noch von Relevanz ist, ist berechtigt und falsch zugleich: Findet sich ein Entwickler aus irgendeinem Grund in einem API nicht zurecht, so sucht er mitunter wahllos nach passenden Kandidaten. Desto länger die Liste der potenziellen Kandidaten, desto länger dauert die Suche.

Wesentlich relevanter ist derweil die Reduktion der Parameter einer Funktion. Die beiden Autoren stellen einen exponentiellen Zusammenhang zwischen der Parameteranzahl und der Zeit fest, die ein Entwickler benötigte, um die Struktur des jeweiligen Elements zu verstehen. Spezifisch werden im Paper für Funktionen mit einem, zwei und vier Parametern die Zeiten 7, 12 und 20 Sekunden genannt. Ergo: Das Eliminieren von Parametern zahlt sich auf jeden Fall aus.

Kriterium Nummer drei zielt direkt darauf, das API für Codevervollständigungssysteme wie IntelliSense greifbarer machen. Dies wird dadurch erreicht, dass möglichst wenige Methoden das gleiche Präfix aufweisen sollten.

Zu guter Letzt fanden die beiden Forscher noch zwei Kriterien, die nur in manchen Situationen Anwendung finden: Die Anzahl der Overloads kann das Erfassen der korrekten Parameterkombination verlangsamen, während Rückgabewerte logischerweise zu Ärger führen, wenn man sie recyclen muss. Aus der Logik folgt allerdings auch, dass Funktionen per se einen Rückgabewert anliefern müssen. Es handelt sich hier also um ein notwendiges Übel.

Diverse Lästlinge

Das weiter oben genannte API-Paper der Technischen Universität Wien geht auch mit einigen anderen „Lieblingen“ der akademischen Programmierung hart ins Gericht. Erstens wird die Präsenz von XML-Dateien mit Konfigurationsinformationen als höchst negativ eingeschätzt. Wenn Ihr Programm also ohne die Möglichkeit von Konfigurationsänderungen ohne Rekompilation auskommen kann, ist es im Interesse der Verständlichkeit empfehlenswert, die Konfiguration im Code abzuwickeln.

Ein weiterer Liebling sind Fluent-Interfaces. Das mit Abstand am weitesten verbreitete ist mit Sicherheit Microsoft LINQ, das in .NET zum Datenbankzugriff verwendet wird. Die Evaluation der beiden Forscher ergab, dass Fluent-Interfaces bei erfahrenen Benutzern insofern populär sind, als die mit diesen Methoden realisierten Interfaces an natürliche Sprache angepasst sind und vergleichsweise kompakt ausfallen. Auf der Sollseite steht allerdings, dass Fluent-Interfaces vom Aufbau her komplett andersartig sind als gewöhnliche Interfaces: Das führt unterm Strich zu einer wesentlich steileren Lernkurve. Die Empfehlung der Forscher ist hierbei, dass man von Interfaces zur Eliminierung von „langen Ketten“ absehen sollte. Dahinter verbirgt sich beispielsweise Code nach folgendem Schema:

var aFS= Directory.GetFiles ( " c :\\akten\\" )
.Select ( file = > new FileInfo ( file ) . Length )
.Average () ;

Zu guter Letzt wird empfohlen, den Entwickler beim Einsatz des API nicht zum Bejagen von Konfigurationspfaden zu animieren. Ein fiktives Negativbeispiel wäre beispielsweise folgende Edelmetallinformationsklasse, zu deren Nutzung der Name eines Servers fix erforderlich ist:

public PMHunter(String _url) {
. . .
}

Ein intelligenter Ansatz wäre, dem Element von Haus aus einen vernünftigen Standardwert anzulegen, um dem Entwickler den mentalenergieraubenden Kontextwechsel zu ersparen. Wie in den weiter oben geschilderten Situationen gilt allerdings auch hier, dass diese Vorgehensweise nicht für alle Situationen angemessen ist. Das API-Design ist nun mal eine Wissenschaft, die sich nicht ausschließlich auf Schwarz und Weiß beschränkt.

Vermeide Akademisches

Factory-Patterns gelten als Nonplusultra im Bereich der Generierung von Klasseninstanzen. Ein großer Vorteil ist, dass der Entwickler des API nach Belieben statt einer Instanz der Basisklasse ein abgeleitetes Objekt retournieren kann, um das Funktionieren des API besser an die aktuelle Situation anzupassen. Der gravierende Nachteil dieser Vorgehensweise ist indes, dass man als Entwickler keine Konstruktoren nutzen kann.

Ellis, Stylos und Myers führten die Feldstudie „The Factory Pattern in API Design: A Usability Evaluation“ mit zufällig ausgewählten Entwicklern verschiedener Fähigkeitsgrade durch. Die befragten Coder mussten eine Gruppe von Aufgaben unter Nutzung zweier API-Varianten realisieren: auf der Fabrikmethode basierende APIs (Factory-APIs) und konstruktorbasierte APIs. Die vernichtende Feststellung war, dass konstruktorbasierte Programmierschnittstellen eine direkte Korrelation zu wesentlich höheren Erfolgsraten aufwiesen. Hier die (vom Autor übersetzten) Originalworte der Forscher: „Unsere Resultate zeigen, dass der Factory-basierte Ansatz signifikant die Benutzung des API durch Programmierer verschlechtert.“

Im Hause Microsoft führte man mit demselben Forscherteam weitere Versuche durch. Diesmal wurde ausprobiert, welche der beiden folgenden Methoden der Initialisierung besser funktioniert:

var foo = new FooC(); 
foo.Bar = barV; 
foo.Use();
var foo = new FooC(barV);
foo.Use();

Die Ergebnisse der Studie wiesen eine klare Präferenz für Create-Set-Call aus. Verschiedene Programmierertypen führten zwar unterschiedliche Gründe für ihre Entscheidung an, verhielten sich aber unterm Strich identisch.

Schritt für Schritt

Diomidis Spinellis schlug in seinem auch heute noch lesenswerten Klassiker „Code Reading: The Open Source Perspective“ vor, dass die Portierung von Code mit dem Versuch der Kompilation beginnen sollte. Bei der Entwicklung eigener Systeme ist es in vielen Fällen hilfreich, wenn man nicht „out of the blue“ loscoden muss [4].
Ein Weg zur Befriedigung dieser Bedingung ist das Anbieten einer Gruppe von Programmbeispielen, die die Realisierung einiger häufiger Einsatzszenarien des API demonstrieren. Hierbei ist es allerdings wichtig, nicht zu viel auf einmal zu realisieren. Insbesondere wenig diensterfahrene Entwickler werden gerne mal von Fehlerbehandlungslogik „eingeschüchtert“.
Bei sehr komplexen Systemen, die mit einem Plug-in für eine oder mehrere IDEs ausgeliefert werden, bietet es sich zudem an, die Autocomplete-Funktion der IDE durch Dokumentationskommentare oder sogar eine komplett eigene Neuimplementierung zu unterstützen. Robillard und Duala-Ekoko [5] analysierten das Verhalten von Programmierern, die sich mit einem neuartigen API auseinandersetzten. Hierbei fielen zwanzig Fragen auf, die häufiger auftraten. Besonders interessant war (neben Ärgernissen über Factories) folgende:

  • Kann ich mit diesem API die Aufgabe X erfüllen?
  • Welche Schlüsselworte beschreiben das API am besten (Vorbereitung einer Suche im Web)?
  • Was tut die Klasse X eigentlich?
  • Welche Unterklasse von X ist für die Lösung meiner Aufgabe ideal geeignet?

Zu guter Letzt sei darauf hingewiesen, dass es, bis zu einem gewissen Grad, sinnvoll sein kann, die hauseigene Supportabteilung zumindest teilweise auf Stack Overflow loszulassen. Entwickler suchen bei Problemen gern und häufig mittels Google, und die in Jeff Atwoods Portal bereitgestellten Fragen und Antworten erfreuen sich im Allgemeinen bester Sichtbarkeit. Außerdem gibt es immer wieder Studien, die APIs anhand ihres Rankings in Stack Overflow bewerten.

Fazit

Auch wenn es auf den ersten Blick insignifikant erscheint: Wer wenig Denkaufwand in das Design seiner Programmierschnittstelle investiert, muss sich nicht darüber wundern, wenn er bei der Entwicklerschaft nur wenig Anklang findet. Die hier besprochenen Grundlagen und interdisziplinären Methoden dürften allerdings ausreichen, um Ihnen einen markanten Vorsprung gegenüber der Konkurrenz zu verschaffen. Und natürlich das alte Credo beachten: häufig testen!

Literaturverzeichnis

  • [1] Henning, Michi: „API: Design Matters“, Queue 5.4 (2007), S. 24 – 36
  • [2] Stylos, Jeffrey; Myers, Brad: „Mapping the space of API design decisions“, IEEE Symposium on Visual Languages and Human-Centric Computing, VL/HCC 2007, IEEE, 2007
  • [3] Scheller, Thomas; Kühn, Eva: „Automated measurement of API usability. The API concepts framework“, Information and Software Technology Vol. 61, 2015, S. 145 – 162
  • [4] Spinellis, Diomidis: „Code reading: The Open Source Perspective“, Addison-Wesley Professional, 2003
  • [5] Duala-Ekoko, Ekwa; Martin P. Robillard: „Asking and answering questions about unfamiliar APIs: An exploratory study“, Proceedings of the 34th International Conference on Software Engineering, IEEE Press, 2012

Entwickler Magazin

Entwickler Magazin abonnierenDieser Artikel ist im Entwickler Magazin erschienen.

Natürlich können Sie das Entwickler Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

X
- Gib Deinen Standort ein -
- or -