Software-Architektur-Leitfaden
Hat sich in Ihrer Systemlandschaft im Laufe der Zeit auch eine Menge (Alt)-Software angesammelt? Um ihr veraltetes System zu modernisieren und auf zukünftige Anforderungen vorzubereiten, empfiehlt sich ein schrittweiser Ansatz.
Anstatt die bestehende Software komplett zu ersetzen, setzen wir auf eine iterative und inkrementelle Evolution der Architektur. Durch diese Vorgehensweise konnten wir bereits viele Kundenprojekte erfolgreich umsetzen.
Darum ist Big Bang riskant
"Many initially successful projects [suffer from] the second system syndrome - the tendency of small, elegant, and successful systems to evolve into giant, feature-laden monstrosities due to inflated expectations."
― Neal Ford, Rebecca Parsons, Patrick Kua: Building Evolutionary Architectures
Big Bang bedeutet in der Software-Entwicklung, dass eine komplett neue Software spezifiziert, entworfen und implementiert wird. Diese soll die Alt-Software nach ihrer Fertigstellung und Abnahme vollständig ersetzen.
Dieses Vorgehen birgt zahlreiche Risiken:
- Mangelhafte Anforderungsanalyse: Die Alt-Software ist so komplex, dass die Spezifikation für die neue Lösung unvollständig, lücken- und fehlerhaft ist.
- Überhöhte Erwartungen: Da es sich um eine Neu-Entwicklung handelt, werden große Hoffnungen in die neue Software gesetzt. Sie soll viele zusätzliche Anforderungen erfüllen („Second system syndrome“).
- Fehlerquelle: Die neue Software vermeidet sicherlich bekannte Fehler der Alt-Software, kann aber neue Fehler enthalten.
- Komplexe Einführungsprozesse: Das neue System einzuführen, erfordert eine detaillierte Feinplanung des Launches, eine hohe Anzahl an Dry Runs und aufwendige Fallback-Szenarien.
- Die Datenmigration von der Alt- zur neuen Software ist extrem anspruchsvoll.
- Stillstand beim Anwender: Die Fachabteilung erhält über sehr lange Zeit (ggf. Jahre) fast keine neuen Features.
- Möglicher Know-how-Verlust: Wird die neue Software durch ein anderes Team entwickelt als die bestehende, kann das zu Frust im ursprünglichen Entwicklerteam führen und motivierte Mitarbeiter verlassen das Unternehmen.
Schrittweise zum Modernisierungserfolg
Oft haben Kunden bereits ein oder mehrere Male versucht, ihre Alt-Software per Big Bang durch eine neue Lösung zu ersetzen, wenn sie ConSol beauftragen. Wir setzen dann auf eine iterative und inkrementelle Evolution der Architektur. Das heißt, wir modernisieren die Alt-Software schrittweise, anstatt alle Hoffnung auf den großen Big Bang zu setzen. Dies hat sich in der Praxis als höchst erfolgreich erwiesen.
Agiles Vorgehen ist Pflicht
"Business people fear breaking change. If developers build an architecture that allows incremental change [...], both business and engineering win."
― Neal Ford, Rebecca Parsons, Patrick Kua: Building Evolutionary Architectures
Agile Vorgehensmodelle wie Scrum oder Kanban sind ein zentraler Baustein bei der iterativen und inkrementellen Evolution der Architektur von Alt-Software. Da die agilen Methoden mit Software-Inkrementen arbeiten, startet das Entwicklungsteam sofort mit der Lösung bestehender Probleme. Die Alt-Software wird regelmäßig sichtbar verbessert, so dass der Kunde oder ein (externes) Test-Team die Resultate bereits nach kurzer Zeit prüfen kann. Durch die fortlaufende, flexibel anpassbare Planung ist es möglich, Änderungswünsche jederzeit zu berücksichtigen.
Ein weiterer Grundpfeiler des Vorgehens ist die wirtschaftliche Bewertung von Kosten und Nutzen der Verbesserungen durch den Kunden. Dadurch wird jede Verbesserung der inneren Qualität der Alt-Software und jede Modernisierung der Architektur mit der Erzeugung von Business Value verknüpft. Die Modernisierung lässt sich so anhand von betriebswissenschaftlichen Größen steuern. Dass der Kunde selbst die Verbesserungen priorisiert, rückt seine dringendsten Probleme in den Fokus.
Und auch die Kommunikation verbessert sich beim agilen Vorgehen durch den ständigen Kontakt und Austausch zwischen den Projektpartnern: Kurze, effiziente Kommunikationswege ermöglichen schnelles Feedback. Entscheidungen werden gemeinsam getroffen. Regelmäßige Meetings sowie Sprint Backlog und Product Backlog sorgen für hohe Transparenz.
Bewährte Techniken zur Evolution der Software-Architektur
"Change is the defining characteristic of software. That change – that adaption – begins with release. Release is the beginning of the software’s true life; everything before that release is gestation. Either systems grow over time, adapting to their changing environment, or they decay until their costs outweigh their benefits and then die."
― Michael T. Nygard, Release It!: Design and Deploy Production-Ready Software
Software ist nie „fertig“. Die Anforderungen an sie, ihre Umgebung und die Gegebenheiten werden sich stets ändern. Deshalb muss die Verbesserung der Software-Architektur ein kontinuierlicher Prozess sein, für den sich folgende Techniken eignen:
Evolution durch Modularisierung (Identifikation von Domänen, Erhöhen von Kohäsion)
- Identifiziere Code-Bestandteile der Alt-Software, welche zur gemeinsamen Domäne gehören.
- Modularisiere diese Code-Bestandteile (durch Refactoring und Anwendung von CleanCode-Prinzipien).
- Führe ein Interface für die Domäne ein.
Die Identifikation von Domänen und die Modularisierung sind Voraussetzung, um die Alt-Software zerteilen und gezielt modernisieren zu können.
Der Nachteil bei dieser Vorgehensweise: Einzelne Domänen sind oft dicht mit anderen Bestandteilen der Architektur verwoben. Um erfolgreich zu modularisieren, ist ein gutes Verständnis der Domäne sowie des Codes der Alt-Software erforderlich.
Modularisierung: Hochkomplex aber wichtig
Beispiel: Form, Farbe, Name oder Größe?
Welches Kriterium entscheidet über die
Zugehörigkeit eines Teils zu einem Modul?
Evolution durch Extraktion (Change by Extraction, Downsizing)
- Identifiziere unabhängige Domänen innerhalb der Alt-Software.
- Extrahiere unabhängige Domänen in ein eigenes System.
- Verbessere das System der nun eigenständigen Domäne, nutze dafür ggf. Change By Abstraction, um das System oder Teile davon neu zu entwickeln.
- Verbessere auch die Alt-Software, die nun weniger Aufgaben hat.
Dieses Vorgehen ist nur möglich, wenn es innerhalb der Alt-Software unabhängige Domänen gibt. Der Nachteil dabei: Da bestehender Code wiederverwendet wird, können „versteckte“ Abhängigkeiten zwischen dem neuen eigenständigen System und der Alt-Software entstehen.
Evolution durch Abstraktion (Change by Abstraction, Outside-in Interfaces)
- Definiere externes, „ideales“ Interface für die Alt-Software.
- Kapsle die Kommunikation mit der Alt-Software über eine Fassade / ein Fassaden-System.
- Stelle alle Clients auf das neue Interface um.
- Erstelle iterativ und inkrementell bessere neue Software für das Interface.
- Ersetze schrittweise die Alt-Software durch die bessere neue Software.
Dieses Vorgehen ist nur möglich, wenn sich eine klare Schnittstelle für bestehende Services definieren lässt. Der Nachteil: Wenn sich das Interface zu sehr an der Alt-Software orientiert, werden Fehler nicht korrigiert.
Evolution durch Abschnüren (Strangulate Bad Parts, The Strangler, Switch)
- Identifiziere einen schlechten Code-Teil innerhalb der Alt-Software, der ersetzt werden soll.
- Füge einen Switch (auch: Dispatcher, Proxy, Load Balancer) ein, der zunächst auf den schlechten Teil der Alt-Software weiterleitet.
- Schreibe eine neue Implementierung für den schlechten Teil.
- Lasse den Switch übergangsweise Last sowohl auf den schlechten Teil der Alt-Software als auch auf die neue Implementierung weiterleiten.
- Reduziere zunehmend die Last auf den schlechten Teil der Alt-Software und erhöhe die Last auf der neuen Implementierung, bis der schlechte Teil der Alt-Software nicht mehr verwendet wird.
Voraussetzung für dieses Vorgehen ist, dass sich der schlechte Teil der Alt-Software, den man ersetzen möchte, zum Beispiel über ein Interface abgrenzen lässt.
Der Nachteil ist, dass sich das neue System an der Interaktion der anderen Systeme mit der Alt-Software orientieren muss. Eventuell nutzen die anderen Systeme undokumentierte Eigenschaften der Alt-Software, sind abhängig von unspezifiziertem Verhalten und etablierten Workarounds.
Evolution durch Klonen und Löschen (Clone and Slash, Change by Split)
- Identifiziere Anwendergruppen.
- Klone gesamte Alt-Software für jede Anwendergruppe.
- Lösche im Klon, was die Anwendergruppe nicht braucht.
- Extrahiere ggf. Gemeinsamkeiten (aber Code-Duplizierung gegenüber zu starken Abhängigkeiten).
- Optimiere für jede Anwendergruppe.
Voraussetzung für dieses Vorgehen ist ein eigenes (Produkt-)Team pro Klon.
Der Nachteil dabei ist, dass die Klone via Datenbank für lange Zeit gekoppelt sind.
Hexagon-Architektur ist Ziel-Architektur
Das Ziel der Software-Architektur sollte immer die Trennung zwischen Fachlichkeit und Technik sein. Dafür eignet sich insbesondere eine Hexagon-Architektur (nach Alistair Cockburn; auch: Ports and Adapters, Onion Architecture nach Jeffrey Palermo, Clean Architecture nach „Uncle Bob“ / Robert C. Martin).
Statt in Schichten zu denken, unterscheidet man in dieser Architektur zwischen innen und außen. Das Innere enthält das Domänenmodell, d.h. die Implementierung der fachlichen Use Cases. Der äußere Bereich umfasst alles andere wie die Integration, Persistenz und UI. Die Kommunikation zwischen innen und außen erfolgt über Interfaces (Ports), welche im äußeren Bereich implementiert sind (Adapters).
Folgende Prinzipien hat die Hexagon-Architektur:
- Innen weiß nichts über außen.
- Das Innere kann mit beliebigen konkreten Implementierungen der Interfaces im äußeren Bereich umgehen.
- Die fachliche Logik ist ausschließlich innen angesiedelt.
- Die technischen Details sind ausschließlich außen.
Software, die in der Hexagon-Architektur aufgebaut ist, lässt sich leichter ändern, testen und verstehen. Im Vergleich zu einer klassischen Schichten-Architektur minimiert dieser Ansatz die Gefahr, dass fachliche Logik außerhalb des fachlichen Kerns z.B. in der Benutzerschnittstelle oder der Datenbank umgesetzt wird und damit unkontrolliert verteilt ist. Das Ergebnis: Weniger technische Schuld und eine verbesserte Wartbarkeit.
Bewährte unterstützende Techniken
Unknown unknowns are the nemesis of software systems [...]: things no one knew were going to crop up yet have appeared unexpectedly. This is why all Big Design Up Front software efforts suffer – architects cannot design for unknown unknown.
― Neal Ford, Rebecca Parsons, Patrick Kua: Building Evolutionary Architectures
Zusätzlich zu den Migrationstechniken für die Architektur unterstützen bewährte Methoden die iterative und inkrementelle Evolution der Architektur von (Alt-)Software:
- Ein Feature-Toggle ermöglicht es, ein in der Entwicklung befindliches Feature zur Laufzeit der Software an- und auszuschalten.
- Durch den Einsatz von Stability-Patterns wie Circuit Breaker oder Bulkhead wird mehr Resilienz erreicht. D.h. die Software kann auch bei Ausfällen und Störungen ihre wesentlichen Aufgaben aufrechterhalten, anstatt vollständig zu versagen.
- Mit Hilfe von Experimenten kann das Team sehr viel lernen und Entscheidungen besser treffen.
- Deployment-Varianten mit Blue/Green Deployment oder Canary Release helfen, die Software mit geringem Risiko auszurollen.
- Das Monitoring der fachlichen Services statt des Monitorings von Hosts erlaubt eine deutlich bessere Überwachung der Software.
Profitieren Sie von unserer Expertise
Noch Fragen zu unserem Software-Architektur-Leitfaden?
Lassen Sie uns sprechen!
Thomas Machata