Beliebte Suchanfragen
|
//

Warum gute Metriken nicht gleichbedeutend mit guter Qualität sind

3.10.2011 | 6 Minuten Lesezeit

Mit einiger Regelmäßigkeit führen codecentric Experten Reviews und Qualitätsgutachten von Softwareprodukten durch. Im Fokus der Betrachtung stehen dabei zum Beispiel Gewerke, die der Auftraggeber nach Erhalt durch nicht an der Entwicklung Beteiligte begutachtet wissen möchte, oder auch In-House Lösungen, über deren aktuellen Zustand sich der Kunde ein Bild verschaffen will.

Dabei wird häufig angenommen, dass mittels automatischer Tools bereits ein ausreichend verlässlicher Eindruck von der Qualität und Wartbarkeit einer Software gewonnen werden kann, und sich damit ein aufwändiger manueller Review einsparen lässt. Anhand eines vereinfachten Beispiels soll hier erklärt werden, dass es sich hierbei um einen Irrtum handelt, und dass maschinell erstellte Metriken keineswegs ein Ersatz für eine manuelle Untersuchung sein können.

Metriken und Werkzeuge

Am Anfang einer Codeanalyse werden üblicherweise in der Tat zunächst maschinell Metriken ermittelt, um sich einen ersten Eindruck des Betrachtungsgegenstands Softwareprodukt zu verschaffen. Dabei bedient man sich oft im ersten Schritt „klassischer“ Werte – um bspw. den Umfang des Produkts überblicken zu können (Zahl der Packages, Klassen, Methoden, Codezeilen) –  als auch bekannter Qualitätsmetriken, wie z. B. der zyklomatischen Komplexität (Cyclomatic Complexity).

All diese Werte lassen sich mit diversen kostenlosen wie kommerziellen Werkzeugen zügig ermitteln und basieren auf einer automatischen Analyse des Quelltexts bzw. der Java Klassen.

Sobald sie zur Verfügung stehen, lassen sich die konkreten Ergebnisse mit Referenzwerten vergleichen, z. B. denen der Carnegie Mellon University  für die zyklomatische Komplexität.

Zyklomatische Komplexität

Ziel der Ermittlung der zyklomatischen Komplexität ist es, einen Eindruck von der Komplexität und damit indirekt der Wartbarkeit einer Software zu gewinnen.

Die o. g. Vergleichswerte der Carnegie Mellon University definieren grob vier Wertebereiche für die zyklomatische Komplexität:

  • Methoden mit Werten zwischen 1 und 10 gelten als simpel und unproblematisch zu überschauen bzw. zu testen.
  • Werte bis 20 stellen bereits deutlich komplexeren Code dar, der zwar immer noch verständlich sein, aber aufgrund der Vielzahl möglicher Fälle beim Durchlauf die Testbarkeit bereits deutlich erschweren kann.
  • Werte von 20 und darüber weisen auf Code hin, der eine unüberschaubare Anzahl von Codepfaden enthält und sich wenn überhaupt nur noch sehr schwer testen bzw. überhaupt vollständig verstehen lässt.
  • Methoden mit nochmals erheblich höheren Werten, z. B. 50 oder größer, können definitiv als unwartbar bezeichnet werden.

Häufig steigt die Komplexität im Laufe der Lebensdauer einer Codebasis an, da neue Features eingebaut und Erweiterungen an bestehenden vorgenommen werden. So findet immer neuer Code Einzug, wobei aber oft eine „kleine“ Änderung den Aufwand für ein Refactoring der betreffenden Programmstelle nicht zu rechtfertigen scheint.

Der Effekt ist, dass das Risiko neuer Fehler bei Änderungen immer weiter steigt, proportional mit der wachsenden Komplexität, da ungewollte Seiteneffekte nicht vorher abgeschätzt werden können. Dem ließe sich mit einer genügend hohen Testfallabdeckung zumindest ein Stück weit begegnen. Nicht selten fehlt es aber gerade bei besonders komplexen Methoden an Testfällen, da mit höherer Komplexität des Anwendungscodes auch der Aufwand für sinnvolle, die zahlreichen verschiedenen Code-Zweige abdeckende Testimplementierungen erheblich wächst. Es handelt sich hierbei um einen regelrechten Teufelskreis, aus dem im Nachhinein nur schwer auszubrechen ist.

Das erlaubt die folgende einfache und wenig überraschende Schlussfolgerung: Geringere Komplexität vereinfacht die Wartung, erleichtert das Schreiben von Tests, vermindert damit das Fehlerrisiko und ist somit ein Indikator für gute Qualität.

Angewendet auf eine Codebasis mit 10.000 Methoden sei folgendes Ergebnis angenommen:

  • 96% – 9600 Methoden: CC < 17 : akzeptabel
  • 3% – 300 Methoden: 17 < CC < 20 : grenzwertig
  • 1% – 100 Methoden:  20 <= CC : zu hoch

Die o. g. Folgerung aufnehmend stellt sich die Frage: Lässt dieses Ergebnis den Schluss zu, dass Komplexität in dieser Codebasis kein großes Problem darstellt?

Die Antwort lautet: Nein.

Der Grund ist, dass die Aussage, dass „nur“ 1% aller Methoden eine zu hohe Komplexität aufweisen, allein wenig Bedeutung trägt, da völlig unklar bleibt, ob es sich bei diesen 100 Methoden zum Beispiel um solche handelt, die zentrale Geschäftslogik enthalten und somit überproportional wichtig für die Gesamtapplikation sind.

Es ist damit auch noch keine Aussage darüber getroffen, ob nicht trotz bzw. gerade wegen der hohen Komplexität der Code mit umfassenden Unit-Tests abgedeckt wurde, und somit seine fachliche Korrektheit verifizieren werden kann, um  Schutz vor Regressionen zu erlangen. Doch dies lässt sich schließlich über eine weitere automatische Analyse feststellen…

Test Coverage

Zur Ermittlung des Grades der Testfallabdeckung stehen verschiedene Tools, z. B. Clover, Cobertura oder Emma zur Verfügung. Sie liefern durch Beobachtung der beim Ausführen von Unit Tests berührten Codezeilen eine gute Einschätzung der Fachcodeabdeckung durch Testfälle.

Zwar lässt sich keine allgemein gültige Abdeckung festlegen, da sinnvolle Werte immer auch von der zu testenden Anwendung abhängen – bspw. ist eine vollständige Erfassung von trivialen Bean Setter- und Getter-Methoden oft nicht unbedingt nötig – aber um genügend Sicherheit für mögliche Refactorings oder Erweiterungen zu haben, sollten Werte von wenigstens 80% angestrebt werden.

Angenommen, im vorliegenden Beispiel läge die Testabdeckung einschließlich insbesondere der 100 sehr komplexen, wichtigen Methoden bei durchschnittlich 85%, sollte sich dann nicht folgern lassen, dass die Qualität des Produkts entsprechend gut sein muss, weil immerhin ein Großteil des Codes von Tests erfasst wird?

Auch hier lautet die Antwort: Nein.

Denn selbst bei hoher Testabdeckung ist lediglich belegt, dass mit den konkreten in den Testfällen verwendeten Daten die verschiedenen Ausführungszweige innerhalb des getesteten Fachcodes wenigstens einmal zur Ausführung kommen. Zwar erkennen die erwähnten Werkzeuge auch, wie oft welche Anweisungen im Rahmen einer Testsuite durchlaufen werden, aber um als getestet zu gelten, genügt bereits eine einzige Ausführung.

Außerdem sagt der Abdeckungsgrad von 85% nichts darüber aus, welche 15% des Codes ungetestet verbleiben. Handelt es sich hierbei – wie nicht selten – zum Beispiel um Code zur Fehlerbehandlung, kann dies zur Laufzeit empfindliche Auswirkungen haben.

und so weiter…

Das bisher gesagte lässt sich auf so gut wie alle automatisch ermittelten Metriken übertragen: Jeder maschinelle Analyseprozess kann bestenfalls Anhaltspunkte liefern, welche Teile einer Codebasis gezielt manuell betrachtet werden sollten. Sie erleichtern damit den Einstieg in die Untersuchung großer Projekte, sind jedoch für sich allein genommen nicht ausreichend, ja sogar manchmal irreführend.

Im Rahmen einer kürzlich real durchgeführten Untersuchung ließen die anfangs ermittelten, überwiegend guten bis sehr guten Werte für die „Standardmetriken“ wie die zyklomatische Komplexität, oder auch die nach Robert C. Martin zur Einschätzung von Kopplungs- und Abstraktionsgraden, zunächst einen positiven Eindruck entstehen.

Auch weitere Untersuchungen mittels statischer Analysetools wie CheckstyleFindBugs  oder Sonar zeigten eine für die Gesamtgröße des Projekts nicht unerwartete Menge von Problemen auf, die sich weitgehend durch verhälnismäßig kleine Korrekturen beheben lassen würden.

Doch trotz dieses zunächst vergleichsweise unkritischen Eindrucks aus der Summe der automatischen Untersuchungen, stellte sich am Ende der Betrachtung heraus, dass das Gesamtprodukt eklatante und schwerwiegende Mängel aufwies, die einer Produktivsetzung dringend entgegenstanden.

Dazu zählten zum Beispiel grundlegende Probleme mit Nebenläufigkeit, unwirksame Caches, deutliche Schwächen in Exception Handling und Fehlerbehandlung allgemein, offensichtliche Performanceprobleme (unnötige und häufige Aufrufe von Remote Services in zentralen, engen Programmschleifen) und einiges mehr.

Fazit

Wird die Qualität – und damit das Risiko des produktiven Einsatzes – einer Software nur aufgrund automatisch ermittelter Messungen und Metriken beurteilt, sind leicht Fehleinschätzungen möglich.

Zu viele Faktoren, die die letztliche Qualität der Gesamtlösung beeinflussen, lassen sich nur bedingt oder gar nicht maschinell erfassen. Zwar existiert eine Vielzahl ausgezeichneter und bewährter Tools, doch dürfen deren Resultate nicht für sich als Endergebnis einer Qualitätsanalyse missverstanden werden, sondern lediglich als Indizien, an welchen Stellen der Codebasis sich eine manuelle Betrachtung als lohnenswert erweisen könnte.

Im vorliegenden Fall hätte eine Überführung der Software in die Produktion zu weitreichenden und kritischen Konsequenzen geführt, da unbemerkt Daten hätten verfälscht werden, oder es zu fatalen Totalausfällen hätte kommen können.

Zwar ist eine manuelle Untersuchung keine Garantie für fehlerfreie Software, aber dennoch ist – zum Glück – auch in der IT Erfahrung und Gespür noch immer nicht durch Automatismen zu ersetzen.

|

Beitrag teilen

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.