Metriken, Struktur und Wartbarkeit von Software

Eine Softwaremetrik, oder auch kurz Metrik genannt, versucht Programmcode und Software allgemein mit Hilfe einer Maßzahl messbar und vergleichbar zu machen. Dabei können viele unterschiedliche Aspekte von Software im Vordergrund der Messung stehen, wie Umfang, Aufwand, Komplexität oder Qualität. Durch die mathematische Abbildung einer spezifischen Eigenschaft einer Software auf einen Zahlenwert wird der einfache Vergleich zwischen verschiedenen Teilen einer Software, oder auch zwischen verschiedenen Projekten ermöglicht. Die so gewonnenen Maßzahlen einer Software bilden die Grundlage für die Definition von Zielgrößen, auch KPIs (Key Performance Indikators) genannt, mit denen die Produktentwicklung gesteuert werden kann.

Da die Definition von Metriken grundsätzlich von den eingesetzten Entwicklungsparadigmen abhängen unterscheidet man grob zwischen den traditionellen und objektorientierten Metriken.

Auf Grund der Erfahrung der letzten Jahrzehnte in der Entwicklung von Softwareprodukten geht man heute davon aus dass je komplexer und umfangreicher eine Software aufgebaut ist, diese auch entsprechend aufwändiger zu warten und weiterzuentwickeln ist. Eine große Menge von Software die in der Industrie heute noch zum Einsatz kommt wurde bereits in den 1970 Jahren entwickelt und ständig an neue Anforderungen angepasst. Da sich die Menge der neu entstehenden Software nicht verringert gehen wir heute davon aus dass die effiziente Wartung und Weiterentwicklung von Software einen entscheidenden technischen Aspekt für die Zukunft darstellt. Unstrukturierte und komplexe Software wird somit in Zukunft wesentlich höhere Kosten für die Sicherung von Qualität sowie für die Wartung und Weiterentwicklung verursachen. Damit spielen Metriken und Werkzeuge zur Analyse von Software im Allgemeinen eine bedeutende Rolle in der Entwicklung und Evolution von Softwareprodukten.  

Auch wenn für den gesamten Entwicklungsprozess verschiedene Klassen von Metriken existieren, wollen wir uns in diesem Artikel auf Metriken zur Messung von Eigenschaften des Programmcodes beschränken.

Zeilenmetriken

Zeilenmetriken beschreiben den Umfang eines Programmes mit Hilfe der Programmzeilen (LOC, Lines of Code) die für die Erstellung notwendig waren. Dabei wird grob zwischen Zeilen unterschieden die Programmcode und Zeilen die Kommentare oder Leerzeichen enthalten.

Obwohl Zeilenmetriken nur einen sehr groben Überblick über ein Softwaresystem vermitteln können, liefern sie auf Ebene der Funktionen und der Programmstruktur bereits eine gute Abschätzung des Aufwands.

Da Zeilenmetriken schon sehr lange im Einsatz sind, gibt es aus der Literatur auch viele Hinweise welche Werte für ein lesbares und wartbares Programm als optimal eingeschätzt werden. Für eine Funktion bewegen sich diese Werte zwischen 40 und 50 Zeilen Programmcode. Da heute für die Programmierung in den meisten Fällen integrierte Entwicklungswerkzeuge eingesetzt werden die von einer Programmcode-Datei abstrahieren, sehen wir die Vorgabe von ca. 500 Programmzeilen pro Datei nicht mehr als kritisch an.

Aus den einfachen Zeilenmetriken kann auch bereits abgelesen werden wie das Verhältnis Programmcode zur Menge der Kommentare und zur Menge der Testfälle (Unit-Tests, etc.) beschaffen ist, was in vielen Fällen auch auf den Zustand eines Softwaresystems schließen lässt.

Komplexitätsmaße

Auch wenn Zeilenmetriken einfach zu erstellen sind und bereits eine grobe Abschätzung über ein System geben können, so liefern sie keinerlei Hinweis auf die Komplexität und die Struktur die sich im Programmcode selbst versteckt.

Komplexitätsmaße, am Beispiel Halstead und McCabe versuchen den Aufbau und die Struktur eines Programms im Detail zu analysieren und in eine Maßzahl abzubilden. Beide Metriken, sowohl die Halstead-Metrik als auch die Maßzahlen nach McCabe gehen von der Annahme aus, dass alle imperativen Programmiersprachen ähnlich aufgebaut sind. Da imperative Programmiersprachen aus Operatoren, Operanden, Verzweigungen und Schleifen bestehen, kann diese Struktur sprachübergreifend in Maßzahlen abgebildet werden.

Halstead-Metrik

Eine der ersten Metriken zur Erfassung der Komplexität eines Programms ist die Halstead-Metrik, die 1977 von Maurice Howard Halstead vorgestellt wurde. Die Halstead Metrik konzentriert sich auf die Messung der Menge der Operanden und Operatoren eines Programms und leitet daraus verschiedene Kenngrößen ab. Die Halstead-Metrik zählt Variablen und Konstanten als Operanden und Schlüsselwörter, Funktions- und Prozeduraufrufe sowie logische und arithmetische Operationen als Operatoren. Die Basismessgrößen der Halstead-Metrik werden wie folgt gebildet:

  • Anzahl der eindeutigen Operatoren (eta1) und Operanden (eta2)
  • Anzahl der verwendeten Operatoren (N1) und Operanden (N2)

Aus diesen Basisgrößen können dann verschiedene Maße abgeleitet werden:

  • Vokabular eta = eta1 + eta2
  • Implementierungslänge N = N1 + N2
  • Halstead Länge HL = eta1 * log2(eta1) + eta2 * log2(eta2)
  • Halstead Volumen HV = N * log2(eta)
  • Komplexität (Difficulty) D = eta1/2 * N2 / eta2
  • Aufwand (Effort) E = D * V
  • Implementierungszeit in Sekunden (Time) T = E / 18

Folgendes Beispiel zeigt die Berechnung der Halstead-Metrik:

#include <stdio.h>
// Bubble-Sort Implementation
// 
void bubble_sort(long list[], long n)
{
    long c;
    long d
    long t;
 
    for (c = 0 ; c < ( n - 1 ); c++)
    {
        for (d = 0 ; d < n - c - 1; d++)
        {
            if (list[d] > list[d+1])
            {
                // Swapping
                t = list[d];
                list[d]   = list[d+1];
                list[d+1] = t;
            }
        }
    }
}

Für das bubble_sort Beispiel ergibt die Zeilenmetrik eine Gesamtlänge von 23 LOC sowie 3 Kommentarzeilen und eine Leerzeile. Davon entfallen auf die Funktion bubble_sort selbst 19 LOC.

Für die Berechnung der Halstead-Metrik werden 11 verschiedene Operatoren (eta1) innerhalb der Funktion bubble_sort identifiziert. Die Summe der Operatoren ergibt eine Messgröße von 30 (N1):

bubble_sort, (), {}, long, for, =, <, -, ++, [], >;   

Die Menge der verschiedenen Operanden (eta2) ergibt in unserem Beispiel einen Wert von 7, sowie eine Summe (N2) von 24:

list, n, c, d, t, 0, 1;  

Daraus können mit Hilfe der Halstead-Metriken folgende abgeleitete Größen berechnet werden:

Vokabular (eta): 11 + 7 = 18

Implementierungslänge: 30 + 24 = 54

Halstead-Volumen: 225.17

Halstead-Komplexität: 17

Aufwand: 3827

Implementierungszeit: 212 Sekunden

McCabe-Metrik (Zyklomatische Komplexität)

Ebenfalls bereits 1976 wurde die McCabe-Metrik von Thomas J. McCabe eingeführt um die Komplexität eines Programmcodes mit Hilfe der Pfade auf dem Kontrollflussgraphen abzuschätzen. Die Grundidee die hinter der McCabe-Metrik steckt ist dass alle Programmverzweigungen, ausgelöst durch Bedingte-Anweisungen (wie if-Statements) oder Schleifenblöcke, die Komplexität eines Programmes erhöhen. Zusätzlich ist hier anzumerken dass die Menge der einzelnen Pfade im Kontrollflussgraphen auch die Menge der notwendigen Testfälle für eine vollständige Pfadabdeckung wiederspiegeln. Für ein Programm ohne eine bedingte Verzweigung und ohne eine Schleife ergibt die McCabe-Metrik also einen Wert von 1. Ein Programm mit einer if-Anweisung ergibt somit eine Komplexität von 2.

In unserem Beispiel bubble_sort ergibt die McCabe-Metrik den Wert 4, da 2 Schleifen und eine Verzweigung ausgeführt werden.

Maintainability Index

Nach Ermittlung der bereits diskutierten Metriken für ein ausgewähltes Projekt ist es besonders für die Wartung und Weiterentwicklung interessant besonders wartungsintensive Teile zu identifizieren. Auch während der Entwicklung eines Projekts ist es für das Produktmanagement wichtig Teile des Programmcodes zu identifizieren die sich in Zukunft zu Problemen in der Wartung der Software entwickeln werden. Gerade in der Entwicklung von Softwareprodukten ist hier mit Hilfe der besprochenen Maßzahlen eine einfache Möglichkeit gegeben um bereits frühzeitig gegensteuern zu können.

Der Zusammenhang von Wartungsintensität mit den besprochenen Maßzahlen wurde bereits viele Jahre in der Fachwelt diskutiert und es existieren unterschiedliche Berechnungsmodelle für einen sogenannten Wartungsindex (Maintainability Index). In diesem Artikel möchten wir nur auf einen Maintainability Index eingehen der von der Microsoft Entwicklercommunity definiert und ausführlich evaluiert wurde. Dieser Maintainability Index repräsentiert ein Modell das auf Basis der bestehenden Metriken entwickelt wurde, jedoch angereichert mit heuristischen Faktoren die durch empirische Untersuchungen an bestehenden Projekten von Microsoft ermittelt wurden.  

Der Microsoft Maintainability Index ist normiert auf einen Wertebereich von 0 bis 100 wobei folgende Bereiche definiert wurden:

Werte von 0-9: Sehr hoher Aufwand für die Wartung

Werte von 10-19: Mittlerer Aufwand

Werte von 20-100: Geringer oder akzeptabler Aufwand für die Wartung

Der Microsoft Maintainability Index wird wie folgt berechnet:

Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171)

Für unser Beispiel bubble_sort ergibt der Maintainability Index den Wert 55, liegt also im Bereich von geringen Wartungskosten.

Entwicklung von Methoden und Werkzeugen

Die Forschungsgruppe Software Analytics and Evolution (SAE) beschäftigt sich mit der Analyse von bestehenden Softwaresystemen sowie mit der Entwicklung von Methoden und Analysewerkzeugen zur Unterstützung von Firmen bei der Wartung und Weiterentwicklung ihrer Softwaresysteme.

Werkzeug zur Visualisierung des Maintainability Index