Code: Lieber Redundanz oder lieber Reuse?

Wiederverwendung war der heilige Gral der Software-Entwicklung. Was Entwickler wiederverwenden können, müssen sie nicht noch einmal schreiben. Das ermöglicht ungeahnte Produktivitätssprünge, so dachte man. Mittlerweile hat sich aber einiges geändert – und sogar redundanter Code zeigt seine Vorteile. Auch Redundanzfreiheit ist nur ein Trade-off.

In Pocket speichern vorlesen Druckansicht 12 Kommentare lesen
Lesezeit: 7 Min.
Von
  • Eberhard Wolff
Inhaltsverzeichnis

Wiederverwendung war der heilige Gral der Softwareentwicklung. Was Entwickler wiederverwenden können, müssen sie nicht noch einmal schreiben. Das ermöglicht ungeahnte Produktivitätssprünge, so dachte man. Mittlerweile hat sich aber einiges geändert – und sogar redundanter Code zeigt seine Vorteile. Auch Redundanzfreiheit ist nur ein Trade-off.

Wiederverwendung war in den Neunzigern eine der wichtigsten Heilsversprechungen von Objektorientierung. "Code erben statt schreiben" war das Motto. Das klappte aber nicht so richtig. Auch heute noch wird Entwicklern das Motto "Don't Repeat Yourself" (DRY) eingetrichtert: Jede Entscheidung soll nur einmal getroffen werden und darf nicht mehrfach im Code vorkommen.

Die Verwendung von Code ist schon schwierig, die Wiederverwendung noch viel mehr. Schließlich muss der Code auf viele Fälle passen, eine hohe Qualität haben, leicht zu verstehen und auch gut dokumentiert sein. Die dazu notwendige Qualität zu erreichen, ist sehr aufwendig. Diesen Aufwand muss betrachten, wer behauptet, dass Wiederverwendung einen ökonomischen Vorteil darstellt. Auf der anderen Seite hat die höhere Qualität des wiederverwendbaren Codes auch weit über die Wiederverwendung hinaus Vorteile.

Trotz dieser Schwierigkeiten gibt es Code-Wiederverwendung in jedem Projekt. Jede Software nutzt die eine oder andere Open-Source-Bibliothek. Einige Bibliotheken werden in Tausenden, wenn nicht Millionen Projekten genutzt. Warum sind diese Projekte so erfolgreich? Was kann man vom Open-Source-Modell lernen?

Zu einem Open-Source-Projekt gehören nicht nur die Offenlegung des Code, sondern auch eine gute Dokumentation und ein Prozess, um Änderungen Außenstehender in das Projekt einzuarbeiten. Auch interne Projekte, die Bibliotheken für andere Projekte erstellen, können das Open-Source-Modell nutzen. Normalerweise haben interne Projekte das Problem, dass sie keinen Umsatz erzeugen, sondern nur den Projekten helfen, die Geld einbringen. Daher sind Budgets interner Projekte oft begrenzt. Sie haben außerdem Schwierigkeiten, die richtigen Prioritäten zu setzen. Wenn das interne Projekt nach Open-Source-Ideen organisiert ist, können andere Projekte zu der Code-Basis beitragen – und so das Projekt priorisiert durch die Anforderungen der Projekte und mit den Mitteln der umsatzträchtigen Projekte weiterentwickeln. Dazu gibt es auch Forschung.

Einige Unternehmen sind dazu übergegangen, solche Bibliotheken nicht nur intern zu entwickeln, sondern wesentliche interne Frameworks als Open Source zu veröffentlichen – dazu gehören nicht nur US-Unternehmen wie Netflix, sondern auch deutsche wie Otto, ImmobilienScout oder Hypoport. Warum würde man das tun? Schließlich verschenkt man so buchstäblich wichtigen Wettbewerbsvorteil. Der Grund: Durch die Veröffentlichung als Open Source müssen die Projekte ihre Qualität weiter verbessern. Und die Software wird in anderen Projekten verwendet. Das Feedback – z. B. Bug-Reports – aus den Projekten kann dazu genutzt werden, die Qualität weiter zu steigern. Viele hoffen darauf, dass externe Entwickler den Code weiter verbessern. Das ist aber eher selten der Fall – und auch Bug-Reports können schon wertvoll sein.

Dass diese Qualität von Code und Dokumentation teuer und aufwendig ist, kann eigentlich kein Argument gegen die Veröffentlichung wiederverwendbaren Codes als Open Source sein – immerhin soll der Code ja intern weitergenutzt werden. Warum würde man von seinen Kollegen erwarten, dass sie Code nutzen, die man einem Externen nicht zumuten würde?

Es gibt noch ein ganz anderes Problem mit Code-Wiederverwendung. In einigen Situationen erzeugt die Wiederverwendung von Code Deployment-Abhängigkeiten. Eine Änderung an dem Code erzwingt ein erneutes Deployment einer Vielzahl von Services. Konkretes Beispiel: Ein Projekt hat zur Vereinfachung der Nutzung eines zentralen Service eine Bibliothek geschrieben. Durch eine Änderung des Service mussten die Bibliothek bei allen Clients geändert und die Services neu deployt werden. Da es sich um ein Microservice-System handelt, sprechen wir über 50 zu ändernden Services, die über verschiedene Teams verteilt sind. Nehmen wir an, die Änderung und die notwendigen Tests dauern pro Service einen halben Tag, dann sprechen wir von 25 PT. Und dann ist das Deployment der Services zu koordinieren. Wer einmal eine solche Situation erlebt hat, wird sich wünschen, dass es bei dem einen Mal bleibt.

Deployment-Abhängigkeiten sind also ein Problem. Eine Code-Abhängigkeit führt nicht unbedingt zu einer Deployment-Abhängigkeit: Wenn die Schnittstelle des Service sich abwärtskompatibel ändert, sodass die alten Client-Bibliotheken weiterhin funktionieren, liegt keine Deployment-Abhängigkeit vor. Die Clients sind nicht neu zu deployen, sondern können mit dem alten Code weiterlaufen, da der weiterhin unterstützt wird.

Gemeinsamer Code kann noch aus anderen Gründen problematisch sein. Wenn mehrere Komponenten ein gemeinsames Datenmodell haben, wird es schwer änderbar: Die Komponenten müssen Änderungen am Datenmodell koordinieren – dadurch werden Änderungen aufwendiger. Ein solches Datenmodell kann leicht aus einer gemeinsamen Client-Bibliothek entstehen. Ähnliches gilt für anderen fachlichen Code.

Domain Driven Design betrachtet mit Strategic Design die Entwicklung ganzer Systeme. Das Pattern Bounded Context bedeutet, dass ein Domänenmodell nur in einem bestimmten Context gültig ist. Ein Beispiel: Für einen Kunden ist bei der Lieferung die Lieferadresse relevant, für die Bezahlung dagegen die Rechnungsadresse interessant und Kreditkarten-Daten, Konten oder PayPal-Accounts. Es ist sinnvoll, diese Daten jeweils im zuständigen Modul zu speichern und nicht in einem globalen Modell. Dann lassen sich nämlich die Module einfacher ändern – selbst eine Änderung an dem Bezahlprozess, bei dem zusätzliche Daten notwendig sind, kann auf das Modul für Bezahlungen begrenzt werden.

Das bedeutet: Den Kunden einmal zu modellieren und wiederzuverwenden, ist schlicht nicht möglich, weil in jedem Bounded Context ein anderes Modell notwendig ist. Insbesondere die Wiederverwendung von Datenmodellen ist also problematisch. Wenn überhaupt sollte Wiederverwendung also vor allem auf technischen Code begrenzt sein.

Wenn die Wiederverwendung von Code so viele Nachteile hat, was ist dann die Alternative? Redundanz. Der Kunde kann mehrfach implementiert werden – eben in jedem Bounded Context einmal. Ebenso lässt sich statt einer gemeinsamen Client-Bibliothek der Zugriff auf einen Dienst mehrfach implementieren. Dadurch entstehen natürlich Redundanzen.

Aber: Redundanzfreiheit ist ein Trade-off. Redundanzfreiheit hat den Vorteil, dass eine Implementierung nur einmal vorgenommen wird und daher zum Beispiel bei einem Fehler auch nur eine Änderung an einer Stelle notwendig ist. Nachteil sind die Abhängigkeiten: Mehrere Module nutzen eine gemeinsame Bibliothek, in der die gemeinsamen Funktionen implementiert sind. Das führt zu Abhängigkeiten, und die können – wie wir gesehen haben – problematisch sein.

Am Ende geht es darum, Software effizient zu implementierten. Wiederverwendung von Code kann sinnvoll sein – beispielsweise nutzen alle Projekte Open-Source-Bibliotheken. Wiederverwendung stellt hohe Ansprüche an die Qualität des Codes. Die hohe Qualität hat auch über Wiederverwendung hinaus Vorteile, erhöht aber auch den Aufwand. Noch schlimmer: Wiederverwendung führt zu Abhängigkeiten – und das kann das Deployment und die Änderung von Software verkomplizieren. Diese Prozesse muss man im Griff haben. Also ist Wiederverwendung oder Redundanz nur ein Trade-off – wie so viele andere Entscheidungen auch. Redundanzfreiheit und Wiederverwendung sollten nicht ohne Abwägung des Trade-offs als Ziel in einem Projekt gesetzt werden.

Danke an die innoQ-Kollegen Andreas Krüger und Michael Vitz für die Diskussion über eine frühe Version des Blog-Posts! ()