JAX Blog

Knigge für Softwarearchitekten

Jan 10, 2017

Flexibilität kann bei Software verschiedene Dinge bedeuten. Wir erklären das anhand des Begriffs „Komponente“, wobei diese Komponente eine beliebige Größe oder einen beliebigen Leistungsumfang haben kann, von einzelnen Klassen bis hin zu umfangreichen Systemen.

JAX 2017 Sessions mit Dr. Gernot Starke

Der Flexibilisator

Der Flexibilisator implementiert seine Komponenten oder Systeme am liebsten so: generisch, möglichst auf viele zukünftige Gegebenheiten vorbereitet, universell einsetzbar und grenzenlos flexibel in alle Richtungen. Er findet den ultimativen Kick, wenn er über den beschränkten Spezialfall der aktuellen User Story hinaus quasi ein zeitloses Denkmal der Flexibilität erschaffen kann. Kennen Sie das auch, diesen Drang nach Verallgemeinerung, den tiefen Wunsch, etwas Großes zu schaffen? Wir möchten in dieser Folge zuerst etwas über mögliche Arten der Flexibilität von Software klarstellen, auf einige Vor- und Nachteile davon eingehen und anschließend kräftiges Bashing auf Flexibilisatoren betreiben.

Flexibilität kann bei Software verschiedene Dinge bedeuten. Wir erklären das anhand des Begriffs „Komponente“, wobei diese Komponente eine beliebige Größe oder einen beliebigen Leistungsumfang haben kann, von einzelnen Klassen bis hin zu umfangreichen Systemen:

  • Flexibilität zur Laufzeit (Konfigurierbarkeit zur Laufzeit): Zur Laufzeit der Komponente lassen sich unterschiedliche Parameter setzen oder verändern. Beispiele dafür sind konfigurierbare Benutzungsoberflächen (etwa: skinnable UI), konfigurierbare Abläufe, Prozesse, Pfade, Gültigkeitsregeln oder sogar Datenstrukturen.
  • Flexibilität bezüglich Installation oder Inbetriebnahme (Konfigurierbarkeit zum Lade- oder Startzeitpunkt): Die Komponente lässt sich in verschiedenen Umgebungen in Betrieb nehmen, mit verschiedener Hardware, Betriebssystemen oder Netztopologien. Wesentliche Parameter wie Speicher, Netzwerk oder Datenbank lassen sich bei der Installation konfigurieren.
  • Flexibilität bezüglich Test: Die Komponente lässt sich in verschiedenen Umgebungen oder Konfigurationen testen. Beispielsweise ist sie unabhängig von der konkreten Ressourcenausstattung oder Konfiguration der Testumgebung. Oder es können zum Test Mocks oder Stubs für Teilsysteme eingesetzt werden.
  • Flexibilität bezüglich der Entwicklungszeit: Eine Komponente bietet eine flexible oder universell verwendbare Schnittstelle an, sodass sie in unterschiedlichen Verwendungssituationen oder von unterschiedlichen Konsumenten benutzt werden kann, möglicherweise über Grenzen einzelner Programmiersprachen hinweg.

Bei manchen Systemen freuen wir uns als Benutzer über die Konfigurierbarkeit zur Laufzeit, beispielsweise bei Firefox. Viele Aspekte seines Aussehens und auch Verhaltens können wir in der etwas unübersichtlichen, aber sehr mächtigen about:config [1] zur Laufzeit anpassen. Eine gefährliche Eigenschaft, wenn sie in die falsche Hände gerät. Es ist diese Laufzeitflexibilität, der wir uns an dieser Stelle primär widmen.

Generisch ist nicht gut

Mit ausreichend Bosheit, Dummheit oder Verwirrung ausgestattet, können wir als Benutzer unseren Browser, um bei diesem Beispiel zu bleiben, ganz schön verunstalten: Schwarze Schrift auf schwarzem Grund, den Proxyserver auf eine nicht existente Adresse eingestellt und dann noch die Adresszeile ausgeblendet. Damit ist der schöne Browser praktisch nicht mehr zu gebrauchen. Wenn der Flexibilisator voll zuschlägt, schenkt er den späteren Nutzern seiner Software eine Menge an Freiheitsgraden. Allerdings setzt er sie gleichzeitig gravierenden Risiken aus. Aber der meist jugendlich-leichtsinnige Flexibilisator denkt sich: „No risk, no fun.“ Hochgradig generisch oder allgemein verwendbar bedeutet, auf teilweise sinnvolle oder sogar notwendige Grenzen zu verzichten. Nennen wir die ultraflexiblen Produkte des Flexibilisators mal „übermäßig laufzeitkonfigurierbare Systeme“ (Overly Runtime Configurable Systems, kurz ORCS). Diese Abkürzung weckt doch hoffentlich auch bei Ihnen Assoziationen zum Herr-der-Ringe-Filmepos.

Wie kommt es zu ORCS?

Bekommen normale Entwicklungsteams halbwegs vernünftige Anforderungen, so entsteht hoffentlich klar strukturierte Software, die eben genau diese Anforderungen erfüllt. Schematisch finden Sie das in Abbildung 1 oben dargestellt (Variante 1), vom klar umrissenen Problem zur klar abgegrenzten Lösung. Sind die Anforderungen hingegen nur teilweise klar, ergibt sich eine leicht diffuse Lösung (in der Abbildung Variante 2). Schlimm wird es, wenn Anforderungen völlig vage oder unklar daherkommen, also im klassischen Fall von unterspezifiziert à la „die wissen nicht, was sie wollen“. Das finden Sie in Variante 3 der Abbildung: wolkige Software, die auf zu viele Eventualitäten vorbereitet ist, ohne klare Abgrenzung. In praktisch allen Fällen enthält Variante 3 viel viel mehr Code als Variante 1, ist viel größer und ungleich schwerer verständlich. Von Wartbarkeit mögen wir in Variante 3 schon gar nicht mehr reden.

Unser Flexibilisator bringt dieses Schema kräftig durcheinander. Er schafft es nämlich, aus einer glasklar und präzise formulierten Anforderung ein schwer fassbares Oeuvre zu produzieren – ein nach allen Seiten offenes Wunderding der Softwarekunst. In Abbildung 2 zeigen wir sein Wirken schematisch.

Flexibilisatoren verletzten die Forderung nach Kohäsion. Sie bauen Systeme oder Komponenten, die wesentlich mehr als eine einzige Sache erledigen. Jetzt können Sie berechtigt sagen: „Ist doch gut, wenn das System schön flexibel ist und sich an viele Nutzerbedürfnisse anpassen lässt.“ Das ist richtig. Doch dem halten wir Aufwände und Kosten entgegen – schematisch in Abbildung 3 dargestellt. Kompakte, wohlstrukturierte Software, bestehend aus Komponenten hoher Kohäsion hat tendenziell deutlich geringere Erstellungs- und Wartungskosten als ORCS.

Die Kurve verläuft nicht linear. Ein bisschen Flexibilität über die heutigen Anforderungen hinaus hält die Wartungskosten und -aufwände im Rahmen. Doch noch mehr Flexibilität und die Kosten steigen exponentiell. Wir sehen primär drei Gründe, die zu ORCS führen:

  • Unsäglich schlechte, unklare und vage Anforderungen.
  • Die Sippschaft der Flexibilisatoren, die es trotz klar umrissener Aufgaben- oder Problemstellung schaffen, ihre Lösungen noch generischer, noch allgemein verwendbarer, noch konfigurierbarer zu gestalten.
  • Schließlich ist da noch die Freude am Komplexen: Es bereitet manchen unsägliche Freude, komplizierte oder komplexe Probleme zu lösen. Warum sollen wir nur das triviale Problem der aktuellen Anforderung lösen, wenn wir doch ganz leicht ein viel spannenderes Problem darum herum erfinden können?

Fazit

Sicherlich gibt es noch viele andere Gründe, warum Software ORCS-mäßig wird. Sicherlich gibt es auch viel mehr Vorteile (laufzeit-)flexibler Systeme, als wir sie hier dargestellt haben. In jedem Fall aber gilt: Je mehr Stellschrauben ein System besitzt, desto schwerer lässt es sich ändern, desto aufwendiger ist das Testen, desto teurer sind Bugfixes und desto schneller degradiert es zu höllischem Treibsand, in dem Nebenwirkungen nicht mehr zu kalkulieren sind. Besonders flexible und generische Komponenten sind meist umfangreicher und schwerer verständlich als ihre spezifischen Pendants. Verzichten Sie auf die Herausforderung der Allgemeinverwendbarkeit oder Generalität. Finden Sie den adäquaten Kompromiss zwischen zu starr und zu flexibel. Bereiten Sie Ihre Software für sehr wahrscheinliche Änderungen und Ergänzungen vor, aber nicht für alle potenziell denkbaren Eventualitäten, die dann doch nicht eintreten und auch nicht gebraucht werden.

Links & Literatur
[1] Firefox Configuration: http://lifehacker.com/the-best-about-config-tweaks-that-make-firefox-better-1442137111
[2] Pope, Daniel: „Misconfigure your browser“: http://mauveweb.co.uk/posts/2007/10/misconfigure-your-browser.html

 


Alle News der Java-Welt:

Behind the Tracks

Agile, People & Culture
Teamwork & Methoden

Clouds & Kubernetes
Alles rund um Cloud

Core Java & Languages
Ausblicke & Best Practices

Data & Machine Learning
Speicherung, Processing & mehr

DevOps & CI/CD
Deployment, Docker & mehr

Microservices
Strukturen & Frameworks

Performance & Security
Sichere Webanwendungen

Serverside Java
Spring, JDK & mehr

Software-Architektur
Best Practices

Web & JavaScript
JS & Webtechnologien

Digital Transformation & Innovation
Technologien & Vorgehensweisen

Domain-driven Design
Grundlagen und Ausblick

Spring Ecosystem
Wissen in Spring-Technologien

Web-APIs
API-Technologie, Design und Management