Software-Code 15.05.2018, 14:44 Uhr

Gezielte Evolution statt wahlloser Wucherung

Es gibt viele Gründe, Programmen frischen Code einzubauen. Innerhalb von Software ist Evolution wichtig, um natürlich gewachsene Verflechtungen durch eine geplante Architektur zu ersetzen.
(Quelle: mmatee / Shutterstock.com)
Entwickler sind meist stark auf den Code ihrer Anwendung fokussiert. Diesen verstehen sie als ein Ergebnis aus den umzusetzenden Anforderungen und konzentriertem Nachdenken. Tatsächlich unterliegen die Strukturen, die Entwickler schaffen, noch einer Vielzahl anderer Einflüsse, die ihnen unter Umständen aber nicht bewusst sind.
Hendrik Lösch: Coach und Consultant bei Saxonia Systems sowie Fachautor
Zu Beginn steht immer die eigentliche Entwicklungsphase. Um diese dreht sich für die meisten Programmierer im Grunde genommen ihr Arbeitsleben. Es geht um die Definition von Anforderungen, das Umsetzen des Codes und damit das Sicherstellen von neuen Merkmalen und Funktionen. Gerade in dieser Phase lernt das System hinzu und wächst in seinem Umfang. Die zweite Phase wird allgemein gern als Wartungsphase oder „Maintenance“ bezeichnet. In dieser Zeit ist das System livegeschaltet, wird von einer Vielzahl von Anwendern genutzt und muss nur durch Fehlerkorrekturen gepflegt werden. Ein Wartungsauftrag, der vor allem dazu dient, Fehler zu beheben, die andere Entwickler hinterlassen haben, gilt unter Programmierern jedoch eher als Vorhof zur Hölle denn als Pforte zum Paradies. Dies wirkt sich vor allem deshalb negativ aus, da es die Maintenance-Phase ist, die solides Wissen zu den eingesetzten Technologien, Konzepten und Fachzusammenhängen erfordert, um die Struktur des – spätestens jetzt als „Legacy“ bezeichneten – Systems nicht zu beschädigen. Solche Schädigungen können langfristig dazu führen, dass das System schlechter arbeitet. Es altert durch fehlgeleitete Anpassungen sogar schneller, da es immer weiter von der ursprünglichen Architektur abweicht.

Museumsreif

Dieser Alterungsprozess von Software ist aber nicht nur auf schlechte Programmierung zurückzuführen. Software altert auch durch den technischen Fortschritt. Darüber hinaus führen auch Veränderungen in der Gesetzgebung oder in Unternehmensprozessen dazu, dass ein Software-System nicht mehr aktuell ist. Wenn sich das System nicht mehr an Veränderungen anpassen lässt, wird es irgendwann abgeschaltet.
Diese letzte Phase wird als „Close Down“ bezeichnet. Dieser Close Down muss mit dem Kunden abgesprochen sein und den vertraglichen Rahmenbedingungen entsprechen.
Vor dem Close Down und nach der Maintenance-Phase gibt es zudem noch das „Phase Out“. In dieser Zeit ist das Ende des Systems schon offensichtlich, da nicht mehr aktiv in seine Werterhaltung investiert wird. Eigentlich wird es hier nur noch seinen letzten verbliebenen Nutzern zur Verfügung gestellt, bis sein Betrieb unwirtschaftlich wird.

Software-Evolution

Lebensphasen: Diese Grafik zeigt die vier theoretischen Lebensphasen einer Software bis zu ihrer endgültigen Abschaltung.
Es gibt noch eine weitere Phase zwischen Development und Maintenance: die „Software-Evolution“. Zwar haben sich die Begriffe des Developments und der Maintenance bereits in den Siebzigerjahren etabliert, jedoch täuschen sie darüber hinweg, dass sich damals herausstellte, dass sie nicht ausreichen, um die Besonderheiten zu beschreiben, die sich daraus ergeben, wenn Software bereits genutzt wird, obwohl ihre Entwicklung noch nicht abgeschlossen ist.
Selbst wenn also beispielsweise eine Lohnabrechnungs-Software über alle Merkmale wie automatisiertes Umsetzen von Arbeitsprozessen, Datenexporte, Validierungen und Datenspeicherung verfügt, so muss sie angepasst werden, sobald gesetzliche Regelungen geändert werden. Geschieht das nicht, arbeitet sie falsch. Wenn dies also nicht als Wartung bezeichnet werden kann, ist es dann mit der reinen Entwicklung gleichzusetzen? Im Alltag wäre diese Frage höchstwahrscheinlich mit Ja zu beantworten, weil wir tatsächlich meist unbewusst die Phasen der reinen Entwicklung und die der Evolution miteinander vermischen.

Entwicklung vs. Evolution

Tatsächlich sind Entwickler im Development-Prozess so lange sicher, wie ihr Produkt noch nicht ausserhalb des Teams verwendet wird. Unter diesen Laborbedingungen muss weder auf Breaking Changes geachtet werden noch sind Hot­fixes zu erstellen, um kritische Laufzeitfehler zu beseitigen. Das Team kann seinem Zeitplan folgen und ist fast frei von unangenehmen externen Einflüssen. In dem Moment, in dem das System jedoch veröffentlicht wird, muss es sich in der realen Welt behaupten. Dann ist plötzlich darauf zu achten, wie die Software bisher verwendet wurde. Sie wird mit tatsächlichen Arbeitsabläufen konfrontiert, und dies kann nicht nur zu Fehlern, sondern auch zu Änderungen bei den Anforderungen, also Change Requests, führen. Wie wichtig dabei die Priorisierung und Definition von Fehlern, Anforderungsänderungen und Merkmalen ist, zeigt sich in den Gerichtsprozessen, die sich nur um die Frage drehen, welche Schadenssummen sich ergeben, weil ein Zulieferer vermeintlich nicht adäquat auf ein gemeldetes Fehlverhalten reagiert hat.
Software entwickelt sich nach dem ersten Release evolutio­när entlang den tatsächlichen Gegebenheiten. Über die Jahre hinweg nimmt der Anteil der Umsetzung von Merkmalen und Funktionen immer weniger Raum ein. Damit sinkt die wertsteigernde Arbeit und wird durch werterhaltende Tätigkeiten wie Stabilisierung, Optimierung und sogar Sanierungen ersetzt. Je nachdem wie geordnet der Entwicklungsprozess auch nach einem Release noch verläuft, ergibt sich da­raus, wie stabil die internen Strukturen der Software sind und wie schnell sich diese an neue Gegebenheiten anpassen kann. Darüber hinaus steigen aber auch die fixen Betriebskosten im Vergleich zu den laufenden Entwicklungskosten. Die Unterscheidung zwischen der Entwicklungs- und der Evolutionsphase ist daher gerade aus Sicht der Kosten sehr wichtig. Während die Entwicklung am Anfang gern als einmaliger Kostenblock angesehen wird, sind die Kosten des Betriebs schlecht einzuschätzen. Das Bereitstellen von Servern, ein Support, der Kundenanfragen bearbeitet, das Vorhalten von Entwicklerkapazitäten, um kritische Fehler zu beseitigen, und Ähnliches können sich auf Dauer zu einem weitaus grösseren Posten entwickeln, als es die ursprüngliche Entwicklung war.
An dieser Stelle lassen sich dann auch nur schwer Einsparungen gegenüber der Entwicklung erzielen. Während sich ein Entwicklungsprojekt im schlimmsten Fall stoppen lässt und sich die Kosten somit schlagartig verringern, bedeutet der Stopp während der Evolutionsphase einen Close Down mit all seinen Folgen. Das heisst: Mit dem Übergang von der Entwicklungsphase hin zur Evolution geht der Betreiber Verpflichtungen ein, denen er zumindest zeitweise noch folgen muss, selbst wenn es sich finanziell eigentlich schon nicht mehr rechnet.

Evolution vs. agil

Wenn die Phase der Software-Evolution so bedeutend ist und ihre Definition nun schon über vierzig Jahre zurückliegt, wie kommt es, dass sie so unbekannt ist? Einer der Hauptgründe dafür liegt wahrscheinlich in einer besonderen Ausprägung der Theorien hinter der Software-Evolution: der agilen Software-Entwicklung. Scrum, Kanban und andere Methodiken bewirken, dass sich Entwickler den besonderen Gegebenheiten nach einem Software-Release aktiv stellen, indem man sie durch die direkte Beteiligung der Fachbereiche möglichst früh herausfordert.
Gerade das iterativ inkrementelle Vorgehen von Scrum-Teams spiegelt einen zentralen Gedanken der Software-Evolution wider. Die Idee dahinter ist, dass am Ende jeder Iteration ein Inkrement der Software steht, das theoretisch auch produktiv eingesetzt werden könnte. Während der Iterationen durchläuft die Software eine Form der Evolution, die kontinuierlich überwacht wird. Mutiert sie bei diesem Prozess in eine ungewollte Richtung, kann dieser Mutation entgegengewirkt werden, bevor sie sich schädlich auswirkt.

Wenn Scrum nicht funktioniert

Software-Evolution: Diese von Tom Mens und Serge Demeyer beschriebene Phase ist eine weitere Phase zwischen Development und Maintenance, die im Alltag nicht gesondert betrachtet wird.
Je nachdem wie im Team dann weiter vorgegangen wird, kann es sein, dass dieses Inkrement auch tatsächlich veröffentlicht oder zunächst nur einem begrenzten Personenkreis zur Verfügung gestellt wird. Im zweiten Fall erfolgt dann ein Release in gebündelter Form mit den Ergebnissen mehrerer Sprints. Gerade zu Beginn ist es auch bei agilen Projekten üblich, dass das Team zunächst mehrere Sprints lang am Projekt arbeitet, bevor die erste Version der Software an die eigentlichen Nutzer verteilt wird. Dies soll sicherstellen, dass die Software schon über genügend Funktionalität verfügt, damit sie im Alltag der Nutzer auch sinnvoll einzusetzen ist. In jedem Fall wandelt sich aber auch hier der Ablauf, sobald die Laborbedingungen wegfallen und sich die Software mit den ersten Fehlerberichten und Änderungsanforderungen aus dem Livebetrieb konfrontiert sieht. Dies kann dann auch einer der Momente sein, in denen Scrum für manche Teams nicht mehr zu funktionieren scheint, denn ihre eigentliche Stärke und ihre ableitbare Planbarkeit erhalten Scrum-Projekte dadurch, dass mit einer Iteration ein fester  Zeitbereich definiert wird, in dem das Team seine Arbeit selbstverantwortlich ausführen kann.
Eigentlich sollten während eines Sprints keine Änderungen an den Aufgaben vorgenommen werden. Kommen plötzlich weitere Arbeitspakete hinzu, dann stört dies den Ablauf von Regelterminen (wie Review, Planning oder Retro), führt zu nicht abzuschätzenden Mehraufwänden und macht damit die ursprünglichen Einschätzungen zunichte.
Wenn jedoch die Schätzungen des Teams zu oft und zu weit von den tatsächlichen Werten abweichen, zerstört dies wiederum das Vertrauen in das gesamte Vorgehen, wodurch agile Arbeitsweisen schnell einmal komplett in Frage stehen können.

Besser früh auf Kanban wechseln

Typische Kritikpunkte am agilen Vorgehen wie „endlose Entwicklungszeiten“, „unerwartet hohe Aufwände“ und „unkoordinierte Aufgabenbewältigung“ ergeben sich somit nicht zuletzt auch, weil die eigentliche Evolution von Software falsch verstanden und ungünstig auf sie reagiert wurde. So lässt sich den beschriebenen Schwierigkeiten beispielsweise durch eine Verkürzung der Sprint-Zeiten auf zum Beispiel eine Woche entgegenwirken oder indem gleich von Scrum auf Kanban umgesattelt wird.
Zuvor aber wäre zunächst zu prüfen, warum so oft auf Meldungen aus der Produktion reagiert wird, und ob dies überhaupt in einer Geschwindigkeit passieren muss, die den gesamten Entwicklungsprozess aus den Angeln hebt. Häufig kann hier schon geschickte Planung und Priorisierung auf Kundenanfragen helfen. Dazu ist jedoch klar einzuschätzen, welche Dinge tatsächlich wie zu handhaben sind. Das betrifft einen der Kernpunkte der Wissenschaft hinter der Software-Evolution.

Gezielte Software-Evolution

Aufgabenfelder nach Tom Gilb: Software entwickelt sich nach dem ersten Release evolutionär entlang den tatsächlichen Gegebenheiten.
Der Unterschied zwischen der heutigen Wissenschaft der Software-Evolution als Teilgebiet der Informatik und der agilen Entwicklung besteht erst einmal darin, dass sich die Software-Evolution mit viel grösseren Zeitabschnitten beschäftigt und aus diesem Grund auch mit sehr lange laufenden Projekten befasst ist. Während also der agilen Software-Entwicklung das Feld der Neuentwicklung überlassen wurde, betrachtet die Software-Evolution, wie sich Entscheidungen während des Entwicklungsprozesses auf lange Sicht auswirken und wie man diesen Auswirkungen begegnen kann. Genau das macht sie sehr bedeutend, denn Entwickler neigen dazu, Software eher auf der Ebene von Quellcode-Zeilen zu betrachten. Die monetären Aspekte einzelner Tätigkeiten und Bestandteile sind ihnen häufig nicht direkt bewusst. Auf Unsauberkeiten innerhalb einzelner Klassen reagieren sie gern schnell und bereinigen unschönen Code in Windeseile. Doch der andere Blick auf die tatsächlichen Strukturen hinter einem solchen System, wie Subsystem-, Modul- oder Komponentensicht, ist seltener, wobei gerade diese es sind, die die Überlebensfähigkeit einer Software auf lange Sicht ausmachen.
Dies zeigt sich beispielsweise auch in einem grundsätz­lichen Missverständnis in Bezug auf Refaktorisierungen. Der Entwickler und Autor Martin Fowler schrieb schon 2004, dass seine ursprüngliche Absicht hinter dem Begriff „Refactoring“ eigentlich in lokal begrenzten Strukturanpassungen lag, die sich nicht auf das Verhalten der Software auswirken. Demnach sollte eine Refaktorisierung die Software nicht länger als zwei Minuten unbrauchbar machen.
Im Alltag wird Code aber immer wieder auch in grösserem Mass umstrukturiert, und dabei werden ganze Entwicklungsteams über Tage hinweg behindert. Im Englischen würde man von einem Restructuring sprechen.

Rütteln an den Grundfesten

Umstrukturierungen sind innerhalb von Software-Systemen nach einigen Monaten oder Jahren notwendig, um die natürlich gewachsenen Verflechtungen aufzubrechen und durch eine geplante Architektur zu ersetzen. Dazu muss die Architektur aber vor der Umstrukturierung auch wirklich geplant worden sein und darf sich nicht während des Vorgangs per Zufall ergeben. Ist sie jedoch ein Ergebnis des Zufalls, wird sie sich auf Dauer ebenfalls als ungünstig herausstellen. Das Missverständnis zwischen Refaktorisierung und Restrukturierung ist somit, dass Entwickler immer wieder meinen, sie würden nur eine kleine Änderung am Code vornehmen; tatsächlich rütteln sie dabei aber oft an den Grundfesten der Applikation, da sie sich nicht ausgiebig mit den tatsächlichen Zusammenhängen beschäftigt haben. Die Krux an dieser Situation ist, dass die Evolution von Software solcherlei Umstrukturierungen auf jeden Fall notwendig macht. Unkontrollierter Aktionismus jedoch ist nicht hilfreich.

Es muss sich rechnen

Die Wissenschaft der Software-Evolution bietet hier Mittel und Wege zur Bewertung. Dazu gehören neben den viel beachteten Code- und Strukturmetriken auch Verfahren zum Umgestalten der Architektur und Möglichkeiten der Kosten-Nutzen-Analyse.
Letzteres mag verwundern, ist aber wichtig, da auch die Software-Entwicklung zum ökonomischen Handeln verpflichtet ist. Nur weil ich Code verändern kann und er mir in seinem jetzigen Zustand nicht gefällt, bedeutet dies noch lange nicht, dass es auch wirtschaftlich sinnvoll ist, ihn zu verändern. Aus diesem Grund hat sich das Ideal etabliert, dass Refaktorisierungen nie einzeln durchgeführt werden sollten. Vielmehr sollten sie immer in Kombination mit einem umzusetzenden Merkmal oder mit dem Beheben von Programmierfehlern verbunden werden. Auf diese Weise werden die strukturellen Anpassungen dann zu einem Teil der eigent­lichen Aufgabe (das neue Merkmal) und können besser eingearbeitet werden.
Das macht den Gesamtaufwand ökonomischer und relativiert ihn schon durch die Umsetzung der Aufgabe. Umstrukturierungen in grösserem Mass mit dem Ziel, technische Schulden grossflächig abzubauen, folgen im Grunde dem gleichen Muster. Sie können entweder erfolgen, wenn die Umsetzung neuer Merkmale ohne sie nur zu noch grösseren technischen Schulden führen würde oder wenn sie der Sanierung bestimmter Teilbereiche der Software vorangestellt werden können, was beispielsweise dann passiert, wenn das User Interface auf eine andere Technologie gehoben werden soll.

Quellen

  1. Wikipedia, Gesetz von Conway
  2. Tom Mens, Serge Demeyer, Software Evolution, ISBN 978-3-540-76440-3
  3. Harry M. Sneed und Richard Seidl, Softwareevolution
  4. Tom Gilb, Evolutionary Delivery vs Waterfall Model, ACM Software Engineering Notes, Juli 1985
  5. Martin Fowler, RefactoringMalapropism
  6. Donald Knuth

Hendrik Lösch
Autor(in) Hendrik Lösch



Das könnte Sie auch interessieren