Software lebt länger, wird immer komplexer und muss laufend neuen Erfordernissen angepasst werden. Grössere Systeme können nicht einfach neu erfunden werden, sondern müssen erweitert und evolutionär weiter entwickelt werden können. Dabei bleiben die gesammelten Erfahrungen und andere wichtige Details erhalten, und sowohl Software als auch Funktionalität können Technologiezyklen und Modeströmungen überleben. Nachhaltigkeit und Langlebigkeit bekommen in diesem Umfeld eine zusätzliche Dimension, wobei die allgemeinen Qualitätsaspekte der Software-Entwicklung ihre Gültigkeit behalten.

 

Architektur

Gerade bei komplexen und grossen Softwaresystemen sind einzelne Komponenten zu unterschiedlichen Zeitpunkten entstanden und wurden auf der Basis von unterschiedlichen Technologien implementiert. Die unbesehene Verwendung von Technologien und Fremdkomponenten führt aber oft zu Abhängigkeiten, welche die Wartung und Weiterentwicklung behindern können. Deshalb muss die potentielle Lebensdauer aller Technologien und Komponenten und deren Auswirkungen auf das System, wenn sie ersetzt werden müssen, beurteilt werden. Dies gilt nicht nur für Bestandteile kleiner Softwarehersteller oder für Randtechnologien, sondern auch für die ganz grossen Hersteller und digitalen Hypes. Nicht selten entwickeln sich diese in eine Richtung, die den eigenen Entwicklungszielen entgegenlaufen und verursachen so mittel- und langfristig hohe Aufwände in der Wartung und Weiterentwicklung. Im schlechtesten Fall wird die Weiterentwicklung sogar eingeschränkt und Komponenten und Technologien müssen ausgetauscht werden.

Oft ist es schwierig, die längerfristige Entwicklungsstrategie der Hersteller einzuschätzen. Umso wichtiger ist es, die Auswirkungen jeder eingesetzten Technologie auf ein Softwaresystem abzuschätzen. Eines der Mittel, um derartige Auswirkungen zu minimieren, ist die saubere Kapselung von Drittkomponenten, so dass deren Änderung oder Austausch nicht dazu führen, dass das gesamte System angepasst werden muss.

Jedes grössere Softwaresystem besteht aus unterschiedlichen Teilen, die unterschiedlichen Einflussfaktoren für Änderungen und Erweiterungen unterliegen. Um diese Änderungen zu isolieren empfiehlt es sich, die Aufteilung in Komponenten auch unter dem Aspekt der Einflussfaktoren und deren Häufigkeit zu betrachten – so wirken sich die zwangsläufigen Änderungen und Erweiterungen im Leben der Software nur an wenigen Stellen, bestenfalls nur in einer Komponente aus. Die allgemeinen Qualitätskriterien von Komponenten gelten hier natürlich auch, im Besonderen die schmalen, sauber definierten Schnittstellen und klare Verantwortlichkeiten.

Es ist zu beachten, dass Objekte keine Komponenten sind. Wir haben also keine klare Schnittstellen von Komponenten, sondern oft eine Vielzahl von Objekten die im gesamten System herumgereicht und auch genutzt werden. Somit ist bei einer Änderung von Objekten häufig ein grosser Teil des Softwaresystems betroffen. Sammlungen von zusammengehörigen Objekten sollten deshalb zu Komponenten mit klar definierten Schnittstellen zusammengefasst werden, die in ihrer Grundstruktur von der Technologie, in der sie implementiert sind, unabhängig sind. Insbesondere muss darauf geachtet werden, dass in der Schnittstelle die darunterliegende Technologie nicht sichtbar wird, beispielsweise spezielle Datentypen oder Strukturen.

Code-Qualität

Es gilt: Code, den ein Entwickler schreibt, muss immer wieder gelesen werden können. Zwar muss dieser Code an erster Stelle vom Rechner verstanden werden, damit er ausgeführt werden kann – aber das ist nur Teil der Wahrheit. Code wird sehr häufig auch von Menschen gelesen, sei es in Reviews zur Qualitätssicherung, zur Fehlersuche, zum Verständnis und insbesondere zur Wartung und Weiterentwicklung. Genialer Code, der für den Menschen schwer lesbar ist, führt im besten Fall dazu, dass der Leseaufwand sehr hoch ist. Im schlechteren Fall führt er dazu, dass der Code falsch verstanden wird und falsche Rückschlüsse gezogen werden. Diese führen typischerweise zu fehlerhaften Anpassungen und Erweiterungen. Wird der Code von den Entwicklern nicht verstanden, führt das in der Fehlerbehebung und Weiterentwicklung oft zur Programmierung von Umgehungslösungen, die meist mit Nebenwirkungen und neuen Folgefehlern verbunden sind.

Wer kennt nicht die Kommunikationsgräben zwischen Entwicklern und Fachvertretern, die unter anderem dadurch entstehen, dass man im Bereich der Softwareentwicklung andere Begriffe und andere Strukturen als auf der Fachseite verwendet. Während der Herstellung der Software ist dies oft noch kein grosses Problem. Nach langer Zeit und mehrfachem Personalwechsel ist es dann oft sehr viel schwieriger herauszufinden, was im Code dem entspricht, von dem der Fachvertreter gerade spricht. Hier sind folgenschwere Missverständnisse vorprogrammiert. Darum sollte darauf geachtet werden, dass der Code auch von einem durchschnittlichen Entwickler problemlos gelesen werden kann und sich die entsprechenden Fachbegriffe darin wiederfinden – das erleichtert die Kommunikation, hilft Fehler zu vermeiden und reduziert den Aufwand in Wartung und Weiterentwicklung.

Es gibt viele Möglichkeiten, schlecht lesbaren Code zu produzieren – dazu gehört neben dem altbekannten Spaghetti-Code auch die immer wieder falsch eingesetzten objektorientierten Methoden. Insbesondere die extensive Verwendung von “Vererbung“ führt leider immer öfter zu unübersichtlichen und schlecht wartbaren Systemen. Da oft nicht klar ist, wer diesen Code erbt, ist nicht immer einfach erkennbar, wo denn der Code, der gerade ausgeführt wird, implementiert ist, und was eine Änderung für Auswirkung haben wird. Im Gegensatz zur Natur ist die Vererbung in der objektorientierten Programmierung nicht ein einmaliger Vorgang; jede nachträgliche Änderung der “Eltern“ wirkt sich hier auf die “Kinder“ aus, obwohl diese schon längst ein Eigenleben führen.

Entwicklungsaufwand, Prioritätensetzung

Während Softwareprojekten herrscht meistens Zeitdruck, so dass der Entwicklungsaufwand optimiert wird. Dies führt oft dazu, dass Entwickler alle verfügbaren Mittel einsetzen, ohne die langfristigen Auswirkungen abzuschätzen. Dazu gehört der Einsatz von Drittkomponenten, mit denen schnell Erfolge erzielt werden können. Schon bei der Fertigstellung der ersten Versionen tauchen dann aber oft die ersten Schwierigkeiten auf, da die Komponenten leider doch nicht ganz so gut passen. Das vervielfacht mittel- und langfristig den Aufwand – Optimierung, Umgehungslösungen oder Ersatz werden notwendig. Deshalb sollte vor einem Einsatz derartiger Komponenten immer der gesamte Aufwand über den gesamten potentiellen Lebenszyklus der Software berücksichtigt werden – obwohl dies oft den akuten Interessen der Entwicklungscrew entgegenläuft und es
entsprechend starke Persönlichkeiten braucht, um derartigen Tendenzen entgegenzuwirken.

Es gibt praktisch keine Projekte, während denen es nicht Performanceprobleme gibt, die gelöst werden müssen. Leider werden auch diese oft nur kurzfristig und aus der Perspektive eines einzelnen Entwicklers bearbeitet. Dabei ist die Betrachtung von Performanceproblemen aus der Sicht der Gesamtsystem-Architektur in der Regel erfolgreicher und bringt signifikant bessere Ergebnisse; auch wenn der Aufwand kurzfristig deutlich höher ist und die Zusammenarbeit aller Beteiligten erfordert.

Fazit

In der heutigen Softwareentwicklung wird leider zu oft und sehr kurzsichtig der Aufwand in der erstmaligen Entwicklung optimiert. Dadurch kommen die langfristigen Aspekte für eine auf Veränderungen ausgerichtete Architektur zu kurz. In der Entwicklung braucht es darum starke Persönlichkeiten, die solchen kurzfristigen Optimierungen entgegenwirken und die langfristigen Aspekte besser berücksichtigen. Dazu gehört auch die Aufgabe dafür zu sorgen, dass die sorgfältig festgelegte Architektur eingehalten wird und ein durchschnittlicher Entwickler in fünf oder mehr Jahren noch in der Lage ist, den Code zu lesen. Punkte, die sich ohne regelmässige Reviews durch einen kompetenten Chefarchitekten nicht durchsetzen lassen.

Autor: Christian Reiter, Opacc,  Dipl. Informatik Ing. ETH