Suche
Gebaut für den Wandel

Nachhaltige Softwareentwicklung

Frank Pientka

© Shutterstock.com/Moon Light PhotoStudio

Eine aufwändig entwickelte Software soll möglichst langfristig genutzt werden. Damit sie stets den aktuellen fachlichen und technologischen Anforderungen genügt, muss die Anpassung permanent erfolgen. Gleichzeitig hat sich die Art, wie Software entwickelt wird, über die Jahre hinweg nicht nur technisch, sondern auch organisatorisch geändert. Wie und von wem eine Software erstellt wurde, hat deshalb eine größere Auswirkung auf die langfristige Qualität als die eingesetzten Technologien.

Bei der Erstellung einer Software werden häufig schon viele Kardinalfehler begangen, die eine spätere Modernisierung oder Anpassung nahezu unmöglich machen. Irgendwann ist dann der Zeitpunkt gekommen, sich von dieser Altlast zu verabschieden. Doch soweit muss es gar nicht kommen, wenn man einige wenige Erkenntnisse konsequent umsetzt.

Kleine Einheiten statt großer Monolithen

Eine saubere Schichtenarchitektur und die Wiederverwendung von Komponenten galten lange als Allheilmittel gegen die Wartungskrise. Trotzdem fressen die steigenden Wartungskosten weiterhin den größten Teil der Weiterentwicklungskosten auf. Inzwischen zählen selbst objektorientierte Projekte als Legacy zu den ungeliebten Altlasten, und dabei spreche ich nicht nur von der nicht mehr zeitgemäßen Benutzeroberfläche. Häufig wird Schichtenarchitektur aus Oberfläche, Geschäftslogik und Datenhaltung eingesetzt, um das vorhandene Wissen und die Arbeit besser organisieren zu können.

Abb. 1: Alles Wissen ist in fachübergreifenden Teams vorhanden

Abb. 1: Alles Wissen ist in fachübergreifenden Teams vorhanden

Anstatt die Arbeit, wie bei der Schichtenarchitektur, nach technischen Aspekten zu organisieren und damit die einzelnen Schichten unabhängig voneinander zu betrachten, sollte immer das System als Ganzes im Auge behalten werden. Dabei trägt das Team nicht nur Verantwortung für die Entwicklung selbst, sondern auch für den produktiven Einsatz. Um die fachlichen, technischen und betrieblichen Aspekte abdecken zu können, muss ein Team interdisziplinär aufgebaut sein. So muss ein fachübergreifendes Team (Abb. 1) das gesamte benötige Wissen für die Erstellung und den Betrieb der Anwendung haben oder sich aneignen. Dieser Ansatz reduziert nicht nur die Abstimmungskosten, sondern wirkt sich auch auf die Architektur der Lösung aus. Es wird versucht, das Anforderungsproblem ganzheitlich von der Erstellung bis zum Betrieb in kleinen Schritten angemessen zu lösen. Dabei sollte sich auf den optimalen Nutzen konzentriert werden, unnötiger Ballast und die Produktion von nicht benötigten Funktionalitäten sollten vermieden werden. Da Entscheidungen dezentral getroffen werden, können Fehler schneller und pragmatischer gelöst werden.

Automatische Tests, Integration und Installation als Teil der Entwicklung führten schließlich dazu, dass eher brauchbare kleinere fachliche Dienste und vertikale Komponenten (Abb. 2) entstanden sind. Durch einen kontinuierlichen Deployment-Prozess können diese schneller mit der nötigen Qualität ausgeliefert werden, sodass früher Rückmeldungen aus dem Livebetrieb in die Weiterentwicklung einfließen können.

Miteinander statt gegeneinander

Da bei Verfolgung des DevOps-Ansatzes Betrieb und Entwicklung eng zusammenarbeiten, werden Kommunikations- und Abstimmungsprobleme schon früh vermieden und der gegenseitige Lernprozess wird gefördert. Die in der Entwicklung eingesetzten Prozesse, Prinzipien und Werkzeuge unterscheiden sich nicht, egal ob damit eine Test-, Integrations- oder Produktionsumgebung aufgebaut wird. Derartige Umgebungen werden durch Einzelschritte gestaltet, sodass Fehler durch Änderungen transparent nachvollzogen werden können. Um Fehler jedoch zu vermeiden und eine konstante Produktionsqualität zu erreichen, ist es nötig, dass die einzelnen Schritte automatisiert und wiederholbar ausgeführt werden.

Abb. 2: Von der Schichtenarchitektur zu vertikalen Komponenten

Abb. 2: Von der Schichtenarchitektur zu vertikalen Komponenten

Da in fachübergreifenden Teams jeder für alles verantwortlich ist, muss jeder alles machen können. So können Wissensinseln vermieden und der Bau von neuen Silos verhindert werden. Das Entwicklungsteam kann lokal und selbstständig entscheiden, welche Maßnahmen es ergreifen möchte und welche Technologie für sein jeweiliges Problem am besten geeignet ist. Als schöner Nebeneffekt können kleinere, überschaubarere und testbare Einheiten schneller erstellt und mit höherer Qualität ausgeliefert werden. Insofern wird mit den fachlich ausgerichteten vertikalen Komponenten die Architektur im Vergleich mit der an Technologien orientierten Schichtenarchitektur auf den Kopf gestellt.

Randbedingungen und Strukturen können sich ändern

Plattformunabhängigkeit der Anwendungen mit einer Unterstützung für Internationalisierung und Lokalisierung sind gute Voraussetzungen, damit eine Anwendung auch in anderen Bereichen als nur in der ursprünglichen Umgebung läuft. Diese Eigenschaften müssen jedoch auch in jeweils mindestens zwei Ausprägungen umgesetzt und in der veränderten Ablaufumgebung getestet werden, damit keine bösen Stolperfallen für später entstehen.

Bereits im 15. Jahrhundert wurde in der Forstwirtschaft das Prinzip der Nachhaltigkeit eingeführt, um den Ressourcenraubbau zu begrenzen, die Produktivität zu erhöhen und die Vitalität zu erhalten oder zu verbessern. Dieses Prinzip lässt sich auf die langfristige Entwicklung von Softwareprodukten übertragen. Gute Strukturen und die Überprüfung der Einhaltung einheitlicher Regeln sind für die Wartbarkeit der Software nützlich. So lassen sich abgeschlossene Einheiten als Funktionen, Klassen oder Pakete im Code gruppieren, und die Sichtbarkeit der Einheiten kann eingeschränkt werden. Doch ab einer bestimmten Codegröße reicht das nicht mehr aus, und es ist gut, wenn es auch für die Ablaufumgebung ein Modul- oder Komponentenkonzept gibt, bei dem man die zu verwendenden Abhängigkeiten und Versionen im Detail festlegen kann. Das Problem haben nicht nur Anwendungen, sondern auch Programmiersprachen wie z. B. Java. So hat sich in den ersten zehn Jahren allein die Anzahl der Klassen in der Programmiersprache verfünfzehnfacht und die Anzahl der Pakete sogar verdreißigfacht (Abb. 3). In den nächsten Java-Versionen stieg hingegen die Anzahl der Pakete gegenüber der Anzahl Klassen in geringerem Maße. Für Neueinsteiger in den Altcode, wie auch in eine ältere Programmiersprache wie Java hat das einen höheren Einarbeitungsaufwand zur Folge. Ähnliches lässt sich bei erfolgreichen Frameworks wie Hibernate beobachten, deren Funktionsumfang auf Kosten der Klarheit zugenommen hat. Oft führt das zu einer Dopplung von identischen Funktionen im Framework oder auch in Java. Manchmal wird demzufolge fälschlicherweise eine ähnliche Funktion verwendet, ohne auf den genauen Kontext zu achten. Das kann daran liegen, dass ähnliche Funktionen in einer älteren und einer neuen Variante angeboten werden. Manchmal wird jedoch auch aus Unachtsamkeit eine identische Funktion in einer anderen Klasse oder einem anderen Paket neu aufgenommen, obwohl diese bereits vorhanden war. Für die Wartung hat das gravierende Auswirkungen.

Abb. 3: Entwicklung der Anzahl der Klassen und Pakete in Java

Abb. 3: Entwicklung der Anzahl der Klassen und Pakete in Java

Der reine Blick auf den Sourcecode versperrt manchmal den Blick auf die dahinter liegenden Konzepte. Für den nachhaltigen Einsatz von Software sind die Ablaufumgebung und die Schritte zur Erstellung aber genauso wichtig. Deswegen sind Systemkontextdiagramme und Verteildiagramme eine große Hilfe, um diese Strukturen zu visualisieren. Die Wahrheit liegt eben nicht allein im Code, sondern auch in der ausführbaren Software.

Oft wurde eine Software auf Plattformen und mit Werkzeugen entwickelt, die heute gar nicht mehr existieren. In diesem Fall ist es hilfreich, wenn alle für den Bau benötigten Ressourcen vorhanden sind und mit einem Build-Werkzeug gebaut, getestet und verteilt werden können. Hier führt der Königsweg vom kontinuierlichen Build über die kontinuierliche Integration hin zum kontinuierlichen Deployment. Der Zustand der einzelnen Schritte wird durch einfache aggregierte Metriken visualisiert, die auf detaillierten Testprotokollen beruhen. Oft wird jedoch vergessen, dass eine Software implizierte Abhängigkeiten vom benutzten Betriebssystem mit seinen Werkzeugen hat. Deswegen sollten alle Abhängigkeiten explizit gemacht werden. In der Konsequenz müssen die benötigten externen Werkzeuge mitversioniert und in Produktion als Teil der Anwendung ausgeliefert werden. Gerade die Variantenvielfalt bei mobilen Endgeräten kann ohne einen kontinuierlichen Deployment-Prozess, der auch die Zielumgebung in der jeweils benötigten Version automatisiert und schnell zur Verfügung stellt, kaum mehr effizient abgedeckt werden.

Design for Replacement anstatt Design for Reuse

Kleine, unabhängige Softwarekomponenten haben den Vorteil, dass sie einfacher zu ändern oder zu ersetzen sind. Hier gilt der Grundsatz, dass Design for Replacement wichtiger als Design for Reuse ist. Um sicherzustellen, dass die Ersetzbarkeit erhalten bleibt, ist es nötig, dass neben der eigentlichen Zielumgebung im automatischen Build- und Deployment-Prozess alternative Versionen oder Umgebungen mitgetestet werden. Die kontinuierliche Überprüfung der Ersetzbarkeit vermeidet später größere und riskantere Migrations- und Modernisierungsschritte. Dadurch werden die späteren Aufwände und das damit verbundene Risiko minimiert. Als schöner Nebeneffekt können neuere Versionen schneller ausgetauscht werden, um z. B. Sicherheitslücken oder Fehler in Fremdkomponenten in der Produktion zu beheben, da diese bereits präventiv mitgetestet wurden. So bleibt die Anwendung in der Produktion länger stabil und durch die neu eingesetzten Versionen frisch, um auch mit wachsenden Anforderungen Schritt halten zu können.

Um die Verzögerungskosten durch nicht vorhandenes Wissen oder fehlende Informationen mittelfristig zu vermeiden, sollte angestrebt werden, alle wichtigen Informationen für Mitarbeiter transparent an einer Stelle permanent verfügbar zu machen. Für die Mitarbeiter bedeutet dies ein Umlernen mit dem Ziel, dass sie sich nicht nur in ihrem Spezialgebiet auskennen, sondern mindestens zwei weitere Fachgebiete abdecken sollten. Um sich neben seinem Spezialgebiet schnell in neue Fachgebiete einzuarbeiten oder sein veraltetes Wissen auf den aktuell benötigten Stand zu bringen, sind eine gute Grundlagen- und Methodenausbildung sowie eine hohe Lernbereitschaft wichtige Voraussetzungen. In diesem Fall spricht man von T-förmigen Mitarbeiterprofilen. Vor allem bei Wartungsprojekten ist es wichtig, ein „Kopfmonopol“ zu vermeiden. Deswegen sollten Änderungen immer von einem Paar durchgeführt werden – oft entsteht auf diese Art eine bessere Lösung. Durch das breiter verteilte Wissen wird außerdem das Personenrisiko reduziert.
Das Verhalten der Software sollte mit möglichst wenig Zusatzaufwand anpassbar sein. Funktionen ändern sich schneller als Anforderungen an Qualität. Deswegen sollte langfristig Wert auf die Erfüllung der nicht funktionalen Anforderungen gelegt werden, denn eine Investition in Qualität zahlt sich immer aus.

Wenn sich die Infrastruktur ändert, kann das Auswirkungen auf die Software haben. Deswegen spielt gerade bei einer verteilten Anwendung die Robustheit gegenüber Netz- und Dienstausfällen eine große Rolle. So sollte die Software möglichst sanft mit Fehlern in der Infrastruktur umgehen und die Möglichkeit haben, in den Notbetrieb umzuschalten z. B. mit dem Schutzschaltermuster von Michael Nygard. Diesem Muster zufolge bleibt das System mit seinen Grundfunktionen verfügbar, auch wenn einige seiner verwendeten Dienste temporär nicht erreichbar sind. Nach einer festgelegten Zeit und wieder verfügbarer Erreichbarkeit wird dann automatisch zurück auf den Normalbetrieb umgeschaltet.

Fazit

Alles hat seine Zeit. Deswegen sollte man sich rechtzeitig von Dingen trennen, die auf einer früheren falschen Annahme oder Umsetzung beruhen. Wenn die Dinge nicht mit überschaubarem Aufwand und Risiko repariert werden können, sollte man sich von alten Ansätzen oder nicht mehr gewarteten Fremdkomponenten trennen. Man sollte sich von unnötigem Ballast losreißen und diesen schon bei der Entstehung vermeiden. Eine gezielte regelmäßige Aktualisierung der Software erleichtert spätere Aktualisierungen. Oft lassen sich damit größere Migrations- und Modernisierungsschritte vermeiden. Notwendige Änderungen sollen frühzeitig angegangen und besser isoliert betrachtet werden. Leichen im Design und Bomben im Codekeller entfernt man sofort, bevor sie größeren Schaden anrichten. Das Problem der Altsoftware sollte von seiner Entstehung her gesehen werden (ad fontes). Nach dem Gesetz von Conway sind die Strukturen von Systemen durch die Kommunikationsstrukturen der sie umsetzenden Organisationen beeinflusst.

In einer Microsoft-Research-Studie wird dieses Gesetz für Windows Vista bestätigt. Die dort entstandene Komplexität und die daraus folgende Fehlerrate lassen sich direkt aus der dafür zuständigen Organisationseinheit bei Microsoft ableiten. Als eine Konsequenz wurde die Codemenge in der Nachfolgeversion Windows 7 auf ein Maß reduziert, das sogar noch unter dem von Windows XP liegt. Deswegen reicht es für eine verbesserte Wartungsqualität nicht aus, nur die Symptome in der Software zu bekämpfen. Auch der Prozess, wie Software entworfen und entwickelt wird, muss an aktuelle Anforderungen angepasst werden. Das kann heute mit anderen Organisationsformen erfolgen, als es früher möglich war. Kommunikation und Entscheidungen haben sich heutzutage gegenüber früher dramatisch geändert. Dies bedeutet, dass wir mit anderen Organisationsformen auch zu anderen Ergebnissen kommen. Anstatt die Architektur an künstlichen Technologien- und Wissensgrenzen aufzubauen, sollte alles Wissen für die Erstellung und den Betrieb der Software im Projektteam hinreichend vorhanden sein. Das Team ist sowohl für die Erstellung als auch für den Betrieb zuständig, um Silodenken zu vermeiden und die Kommunikation zu verbessern.

Aufmacherbild: The designing new connection technology von Shutterstock / Urheberrecht: Moon Light PhotoStudio

Geschrieben von
Frank Pientka
Frank Pientka
  Frank Pientka ist Senior Architect bei der MATERNA GmbH in Dortmund. Er ist seit mehreren Jahren im Bereich Java EE tätig. Seine Schwerpunkte sind Applikationsserver, Portalserver und Datenbanken. Dazu hat er auch schon mehrere Fachartikel und ein Buch über Geronimo veröffentlicht.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: