Innerliche Aspekte von Software-Qualität

Innere Aspekte der Software-Qualität

Im vorherigen Teil des Blogs ging es um die von aussen wahrnehmbaren Aspekte von Software-Qualität. In diesem Teil es Blogs geht es um die von aussen nicht unmittelbar sichtbaren oder wahrnehmbaren Aspekte von Software-Qualität, nämlich um den Software-Code.

Code-Qualität

Die Qualität des Programmcodes wirkt sich in hohem Mass auf die Qualität und den Lebenszyklus der Software aus. Mit der Code-Qualität wird das Fundament für geringe Fehleranzahl, Testbarkeit und Wartbarkeit gelegt. 

Der typische Lebenszyklus einer Software sieht folgendermassen aus: Sie wird einmalig in der Implementierungsphase erstellt oder entworfen und dann über viele Jahre kontinuierlich gewartet, weiterentwickelt, mit neuen Features versehen, mit Daten befüllt, bis sie schliesslich ausser Betrieb genommen oder durch eine andere Software ersetzt wird.

In der gesamten Lebensspanne der Software wird demzufolge viel mehr Aufwand in Wartung und Weiterentwicklung gesteckt als in die erstmalige Erstellung.

Software-Hersteller tun also gut daran, darauf zu achten, dass diese Weiterentwicklung unter guten Voraussetzungen und risikoarm erfolgen kann. 

Regressionsfehler und Software-Qualität

Wenn durch die Weiterentwicklung von Software Fehler in bereits existierendem Code produziert werden, sprechen wir von „Regressionsfehlern“. Regressionsfehler sind Fehler, die nach einer Änderung des Codes auftreten, obwohl sie zuvor nicht aufgetreten sind.  Sie treten typischerweise an Stellen auf, die von der Anpassung des Codes nicht direkt betroffen sind.  

Ein fiktives Beispiel: In einer Software wird ein neues Feld in einen Dialog eingefügt. Dafür werden die zugrunde liegenden Datenstrukturen angepasst. Andere Teile der Software können jedoch nicht mit den angepassten Datenstrukturen umgehen und verhalten sich nun plötzlich unerwartet.

Um solche Fehler zu vermeiden, ist es wichtig, Regressionstests durchzuführen. Dadurch kann sichergestellt werden, dass die bestehenden Funktionen des Systems auch nach der Weiterentwicklung wie erwartet funktionieren.

Streng genommen muss bei jeder Weiterentwicklung oder Änderung auch nur einer Code-Zeile die gesamte Software immer wieder getestet werden. Nur so kann sichergestellt werden, dass sich keine Regressionsfehler eingeschlichen haben.

In der Praxis wird dies oft vernachlässigt. Denn es ist viel zu aufwändig und teuer, bei jeder kleinen Änderung die gesamte Software manuell zu testen. In der Regel werden nur Stellen getestet, die von der Anpassung direkt betroffen sind, im günstigsten Fall auch benachbarte Bereiche. 

Automatisierte Tests und Software-Qualität

Der Aufwand, um Regressionsfehler zu vermeiden, steigt exponentiell mit der Anzahl der Features und Funktionen der Software. Der Testaufwand für Software, die über lange Zeit weiterentwickelt wird, steigt daher immens an.

Mittel- und langfristig kann dieser Testaufwand nur mit automatisierten Softwaretests reduziert werden. Menschliches Testen muss auf ein Minimum reduziert werden.

Damit Software überhaupt automatisiert getestet werden kann, müssen die Komponenten weitestgehend entkoppelt und isoliert sein. Es muss festgelegt sein, wie die Komponenten miteinander kommunizieren, und durch regelmässiges Refactoring sollte der Code aktuell gehalten werden. 

Entkopplung und Kapselung und Software-Qualität

Software besteht im Inneren aus vielen einzelnen Komponenten, die unabhängig voneinander arbeitsfähig sein sollten. Sie sollten voneinander entkoppelt und in sich gekapselt sein. 

Ist das nicht der Fall, so wird der Einfluss der Komponenten aufeinander um so grösser, je stärker die Komponenten vernetzt und verzahnt sind. Damit steigt die Wahrscheinlichkeit, dass bei einer Änderung einer Komponente eine andere Komponente in Mitleidenschaft gezogen wird. 

Hier gilt: Code, der für die Ausführung im Auftrag von mehreren Akteuren geschrieben wird, verletzt das Prinzip für eindeutige Verantwortlichkeit[1]. Wird dieses Prinzip verletzt, dann wird der Code über die Zeit degenerieren und zu einer grossen Matschkugel[2] werden. 

Was zunächst spassig klingt, hat einen ernsten Hintergrund. Code wie dieser ist nicht mehr wartbar. Jegliche Weiterentwicklung, wie z.B. das Hinzufügen von neuen Features, wird fast immer unerwünschte Nebeneffekte haben. Damit entstehen mit jedem neuen Feature auch Defekte. Meist entstehen diese an Stellen, die keine direkten Nachbarn des angepassten Codes sind. 

Hat man keine oder lediglich manuelle Softwaretests, bleiben diese Defekte unentdeckt, die Software wird fehlerhaft ausgeliefert und die Defekte werden erst durch die Kunden entdeckt. Die Software wird somit zur Bananensoftware[3] – sie reift beim Kunden. 

Softwarehersteller tun also gut daran, kontinuierlich und penibel darauf zu achten, die Innereien der Software sauber voneinander zu trennen und durchgängig automatisiert zu testen.

Anders ausgedrückt: Einzelne Komponenten sollten ihr Verhalten beibehalten, auch wenn sie in einem grösseren Kontext eingesetzt werden. Sie sollten ihr Verhalten nicht unerwartet verändern.

Refactoring

Als Refactoring bezeichnen wir die Veränderung des Quelltextes von Programmen, ohne das beobachtbare Verhalten der Software zu beeinflussen. 

Warum bloss sollte etwas Funktionierendes verändert werden, ohne dass sich etwas ändert?

Putzen

Die Frage  ist in etwa ähnlich zur Frage: Warum sollte ich meine Wohnungen putzen? Vor dem Putzen ist sie meine Wohnung, hinterher ist sie meine Wohnung. Sie ist hinterher nur sauberer als vorher. Die Funktion bleibt exakt dieselbe. 

Die Frage nach dem Sinn des Putzens stellt sich normalerweise nicht. Es erscheint logisch, dass wir unser Eigentum von Zeit zu Zeit auf einen aktuellen Stand bringen und ab und an sogar noch renovieren. Auch unsere Fahrzeuge werden regelmässig gewartet und Verschleissteile ausgetauscht. 

Das tun wir, um auch zukünftig Freude daran zu haben und Ausfälle zu vermeiden.

Auch das ist eine Form von “Refactoring”:  Wir verbessern den aktuellen Zustand der Gegenstände, ohne deren Funktion zu verändern. 

Für Software ist die Sachlage ähnlich: Aufgrund von Weiterentwicklungen der Software altern andere Teile der Software im Vergleich zu den Weiterentwicklungen. Dies verlangt nach einer Renovierung, einem Herausputzen dieser alten Software-Stellen – einem Refactoring.

Das Gefährliche: Zunächst macht es keinen grossen Unterschied, ob ein Refactoring stattfindet oder nicht. Je länger das Refactoring hinausgezögert wird, desto grösser werden die technischen Schulden[4]. Desto länger dauern Funktionserweiterungen. Und desto fehleranfälliger wird die kommende Weiterentwicklung.

Je komplexer die Software geworden ist, desto aufwändiger werden die Regressionstests.  Gibt es keine automatisierten Tests, so wird das Refactoring oft bis ins Unendliche aufgeschoben.  Getreu dem Motto “Never change a running system” wird Refactoring nur dann erfolgen, wenn es überhaupt nicht mehr anders geht. 

Die Folge: Die Software wird immer schwieriger anzupassen und bleibt irgendwann im Status Quo stecken. An dieser Stelle angekommen hilft oft nur noch die komplette Neuerstellung (Rewrite).

Vorhandene und regelmässig ausgeführte Tests und regelmässiges Refactoring sind demzufolge wichtige Kriterien für eine gute und kontinuierlich aufrechterhaltene Software-Qualität.


[1] Single-responsibilty principle, https://en.wikipedia.org/wiki/Single-responsibility_principle

[2] Big Ball of Mud, https://de.wikipedia.org/wiki/Big_Ball_of_Mud

[3] Bananenprinzip, https://de.wikipedia.org/wiki/Bananenprinzip

[4]Technische Schulden,  https://de.wikipedia.org/wiki/Technische_Schulden

Ausblick auf die nächste Folge

In der nächsten Folge sehen wir uns einige Fragestellungen und Indikatoren an, die uns erlauben eine Idee davon zu bekommen, wie es wohl um die Qualität einer Software bestellt ist.


Die Autoren

Jürgen Burger ist Gründer und Geschäftsführer von SIMIO.

Er verfügt über eine langjährige Beratungserfahrung und eine umfassende Kenntnis des Anbietermarktes im Kontext Produktkommunikation. In diesem Zusammenhang unterstützt er Unternehmen immer wieder auch bei der Auswahl von Software-Systemen.

Ralf Trapp ist Gründer der tronet GmbH und geschäftsführender Gesellschafter der procelo GmbH.

1992 hat Ralf während des Studiums seine erste Softwarefirma gegründet. Seitdem lässt ihn die Verbesserung der Software-Entwicklung nicht mehr los und er ist leidenschaftlicher Evangelist für bessere Software.


© procelo GmbH 2024