Die Herausforderungen bei der Wartung von veralteter Software
Einen schnellen, schön verständlichen Überblick, das ist es, was sich viele im Leben wünschen. Besonders bei historisch gewachsenen Softwaresystemen. Sogar die Entwickler selbst brauchen hin und wieder einen umfangreichen Überblick über das System, auch wenn sich der Fokus während der Entwicklungsphase und danach in der Wartungsphase durchaus unterscheidet.
Wo haben sich Monsterklassen gebildet? Wo verheddern sich die Abhängigkeiten? Wo sind zu viele Klassen miteinander verbunden? Bin ich an meiner mentalen Grenze oder ist der Code zu komplex? Solche Fragen können essenziell sein, um ältere Software nicht als Komapatient am Leben zu erhalten, sondern als agilen robusten Rentner, den man nur montags zum Kaffeekränzchen sieht.
Wir stellen vor: Bisquat2: Vereinfachte statische Analyse
Unser Bisquat2 betreut Software unabhängig von ihrem Rentenstatus. Gute Erfahrung mit statischer Qualitätsanalyse wird meist bei kontinuierlicher Anwendung gemacht. Dabei wird der Status in regelmäßigen Abständen kontrolliert, damit Aggravationen präzise behandelt werden können.
Oft integrieren Firmen Tools wie Sonarqube in den Entwicklungsprozess. Unsere Erfahrung lehrt uns hier allerdings, dass viele Entwickler die Ergebnisse nicht richtig interpretieren einschätzen können, oft missdeuten und allgemein eine Antipathie gegen die ständigen Warnungen entfalten. Bis zu dem Punkt, an dem viele Entwickler die Meldungen schließlich vollständig ignorieren und abschalten. Das kennen wir aus dem echten Leben, nur dass man sich dann doch mal zum Arzt schleppt, wenn die körperlichen Warnsignale unüberhörbar werden. Also warum nicht das Gleiche mit hochkomplexen Softwaresystemen machen?
Mit Bisquat2 sollen lange Werkzeugketten und Unmengen von aufeinander folgenden Arbeitsschritten vermieden werden. Einmal Code hochladen, Prüfwerkzeug auswählen, noch einmal die Defaultwerte überprüfen, Knopf drücken und los. Quasi ein Schnelldurchlauf der ärztlichen Vorsorge, um kleine Zipperlein zu orten und beseitigen zu können.
Die statische Analyse gibt durch verschiedene Analyzer jeweils sehr gute Einblicke zu Komplexität, Abhängigkeiten, Inhalt und Qualität der einzelnen Quelldateien und bietet im Anschluss mit dem Antipattern- und Hotspot-Analyzern die Möglichkeit, sich einen systemweiten Überblick zu verschaffen. Gleichzeitig werden Bilder aus den Daten erzeugt, die auch dem Software-Laien die Problematiken einfach darlegen.
Inhaltsanalyse: LOC-, NOM- und CR-Metriken
Der Content-Analyzer ist für die größte Datenmenge – der Grundstruktur – verantwortlich. So werden hier die Länge der Klassen (LOC, „Lines Of Code“) und Methoden, die Anzahl der Methoden (NOM, „Number Of Methods“), die Anzahl übrig gebliebener „TODO“s oder „FIXME“s und das Verhältnis Kommentar/Code (CR, „Comment Ratio“) berechnet. Diese sogenannten Metriken wie LOC, NOM und CR, werden auch von anderen Analysewerkzeugen berechnet und es gibt allgemein anerkannte Grenzwerte, um die Qualität von Software einzuschätzen und Antipatterns berechnen zu können.
Überschreiten gerade Metriken wie LOC oder NOM die Grenzwerte, kann man davon ausgehen, dass der Code unübersichtlich ist und sich meist in wenigen übergroßen Klassen ballt. Falls „TODO“s oder „FIXME“s im Code entdeckt werden, deutet dies meist auf unvollständigen oder suboptimalen, überflüssigen oder sogar nicht-funktionierenden Code hin.
Die Kommentarrate, festgehalten in CR, sollte ausgewogen, heißt nicht zu groß und nicht zu gering, sein. Kommentare sind wichtig für nachfolgende Entwickler, um ein Verständnis für den Code zu gewinnen. Komplexe Stellen verdienen hier natürlich mehr Aufmerksamkeit. Mit einer Gesamtübersicht durch die Kommentarrate kann man gut abschätzen, ob hier zur Genüge getan oder Übertrieben wurde. Wichtig sind sinnvolle Kommentare. Auskommentierter Code hingegen ist in sich selbst schon ein Problem, den der Analyzer ebenfalls gezielt sucht.
Abhängigkeitsanalyse: DIT, NOC und CBO verstehen
Abhängigkeiten werden von unserem Dependency-Analyzer untersucht, wobei auch hier Metriken berechnet werden. So zeigt DIT („Depth of Inheritance Tree“) die maximale Länge zwischen dem Knoten und der Wurzel des Vererbungsbaumes. Wenn dieser Wert zu hoch ist, ist die Vererbung innerhalb der Klassen zu verworren. Andersherum lässt ein zu kleiner Wert erkennen, dass objektorientierte Programmierung nicht gebührend genutzt wird.
NOC („Number Of Children“) ist genauso Teil davon den Vererbungsbaum zu verstehen, dabei werden die beerbten Klassen ein Level unter der Elternklassen gezählt.
CBO („Coupling Between Objects“) stellt hingegen dar, mit wie vielen anderen Klassen eine Klasse durch beispielsweise Gebrauch von Variablen oder Methoden verbunden sind. Falls die Abhängigkeiten in ihrer Anzahl zu hoch werden, so wird die Wartung und Instandhaltung von Software stark behindert. Änderungen in einer Klasse haben in großen Abhängigkeitsnetzen Auswirkungen auf fast alle Klassen, die mit dieser verstrickt sind. Der Aufwand wird unüberschaubar.
Etwas abstrakter bezieht sich RFC („Response For a Class“) auf die möglichen Methodenaufrufe mitunter auch aller verbundenen Klassen, die von einer Klasse aus getätigt werden können. Auch hier deutet eine hohe Zahl auf Unübersichtlichkeit und schlechte Wartbarkeit hin.
Komplexitätsmessung: zyklomatische Komplexität und WMC
Ein weiteres wichtiges Standbein ist der Complexity-Analyzer in der statischen Analyse. Hier werden zyklomatische Komplexität, „Nesting“ und WMC („Weighted Methods per Class“) berechnet.
Die zyklomatische Komplexität zählt alle Entscheidungspunkte der Funktionen in einer Klasse, wie beispielsweise alle Zweige von if-else-Verkettungen, switch-case-Statements, for- oder while-Schleifen. Beim Nesting wird die Tiefe dieser Entscheidungspunkte beleuchtet. Wenn mehrere Verkettungen und Schleifen ineinander übergehen, zum Beispiel wäre das Nesting bei einer Schleife in einer If-Bedingung, hier auf Level zwei.
Die Metrik WMC zählt die zyklomatische Komplexität (das ist die Anzahl linear unabhängiger Pfade) aller Methoden einer Klasse zusammen, um so die Gesamtkomplexität der Klasse bestimmen zu können.
Quality Analysis: Identifying Magic Numbers and LCOM
Als viertes führen wir den Quality-Analyzer an. Dieser analysiert die Klassen nach „Magic Numbers“, „Empty-Catch-Blocks“, „Hardcoded Credentials“ und zählt die Anzahl der Testmethoden. Außerdem wird die Metrik LCOM („Lack of Cohesion in Methods“) berechnet.
„Magic Numbers“ deuten auf hart kodierte Zahlen hin. Treten solche hart kodierten Variablen öfter auf, wird der Aufwand diese bei Bedarf Stück für Stück zu ändern unnötig hoch. Außerdem sind die alleinstehenden Zahlen oft schwieriger zu verstehen als eine Konstante. Besser werden Zahlen in statischen Variablen festgehalten, die ohne Umstand wiederverwendet werden und leicht an einer Stelle geändert werden können. „Hardcoded Credentials“ hingegen deuten auf hart kodierte Passwörter oder Zugangsdaten hin, die ein Sicherheitsrisiko darstellen.
„Empty-Catch-Blocks“ decken nicht abgefangene Exceptions auf, welche ausgeworfen wurden, wenn im Prozess etwas nicht funktioniert. Exceptions haben immer einen „Catch-Block“, der beschreibt, was getan werden soll, wenn im vorherigen Code eine Ausnahme, also Exception, geworfen wurde. Wenn dieser leer ist, weiß das Programm nicht, wie die Ausnahme gehandhabt werden soll. Ein ganzer Ablauf wird eventuell einfach übersprungen und nicht einmal eine Fehlermeldung deutet darauf hin, was passiert.
Mit der Metrik LCOM wird berechnet wie viel Prozent der Klassenmethoden eine bestimmte Klasseninstanzvariable verwenden. Damit wird verdeutlicht wie eng die Methoden einer Klasse zusammenhängen oder zusammengehören. Ist die Zusammengehörigkeit recht niedrig, also wenn LCOM hoch ist, sollte man gegebenenfalls neue Klassen erstellen, deren Methoden zusammenpassende Aufgaben behandeln.
Erkennen von Antipatterns: Brainclass, Godclass, and Mehr
Mit diesen vier Analyzern behandeln wir die wichtigsten Metriken, die auf Unstimmigkeiten im Code hindeuten. Unser Antipattern-Analyzer berechnet aus diesen Metriken dann Antipatterns, die auffällige und problematische Klassen vertreten. So haben wir die „Brainclass“, die zu viel Funktionen in sich vereint, oder die „Godclass“, die gewöhnlich eine riesige Klasse ist, die zu groß und unübersichtlich geworden ist. „Butterfly“, „Breakable“ oder „Hub“ sind Antipatterns, die sich mit Abhängigkeiten unter den Klassen beschäftigen und geballte, verhedderte Knotenpunkte aufzeigen. Das wären dann im übertragenen Sinn ernsthafte Krankheiten, die besonderer Fürsorge bedürfen, damit der alternde „Körper“ wieder rund läuft.
Erforderliche Fürsorge bei Software ist das Refactoring, also Umstrukturieren, damit sie wieder übersichtlich und gut wartbar wird. Also immer agil in die nächste Runde des Alterungsprozesses.
Um all diese Sachverhalte deutlich und einfach darzustellen, nutzen wir viele Visualisierungen. Wie Ärzte nun ja auch Röntgenbilder oder Ultraschall nutzen, um in den menschlichen Körper hinein zu sehen. Statt Erkenntnisse aus Bildern zu gewinnen, erzeugen wir umgekehrt Bilder basierend auf bereits generierten Daten und den daraus gewonnenen Erkenntnissen. Durch unsere zahlreichen visuellen Hilfen können Daten effizient und anschaulich erklärt werden. Welche Klassen sich problematisch entwickelt haben, sieht man somit sofort.
Eine Auswahl dieser Grafiken werden in dieser Blogreihe noch vorgestellt.
Lesen Sie Teil 1 aus der Blogreihe hier.
Nächster Beitrag