Der Nutzen von Qualitätsmetriken
von Moritz TiedjeHero or zero - Metriken und ihr Einsatz
Wenn Softwarequalität in meinen bisherigen Projekten ein Thema war, dann gab es den Wunsch, sie auch zu messen. Manchmal führten die Qualitätsmessungen zu echten Verbesserungen, manchmal zum genauen Gegenteil und manchmal waren sie einfach nur Zeitverschwendung. Dieser unterschiedliche Erfolg ändert nichts daran, dass sie oft in Diskussionen und im Entwicklungsalltag vorkommen. Was uns zu der Frage bringt, wann Messungen von Nutzen sind und wann nicht. Was ist sauberer Code und warum interessiert uns das? Wie messen wir, ob Code sauber ist? Wann hilft die Messung? Wann bringt sie nix? Wann ist sie sogar schädlich?
Was ist sauberer Code und warum interessiert uns das?
„Sauberer Code ist einfach und direkt. Sauberer Code liest sich wie wohlgeschriebene Prosa. Sauberer Code verdunkelt niemals die Absicht des Designers, sondern ist voller griffiger Abstraktion und geradliniger Kontrollstrukturen“ - Grady Booch
Dem kann ich mich nur anschließen. Die Metapher: “Sauberer Code liest sich wie wohlgeschriebene Prosa.” ist sehr passend. Selbst ein Prosa-Laie hat ein Gefühl dafür, was guter und was schlechter Schreibstil ist und kann Inhalte leichter erfassen, wenn sie eine intuitive, geradlinige Struktur haben. Fähige Prosa-Autoren haben einen guten Schreibstil und wissen, wie sie Texte strukturieren können. Sie haben nicht nur ein Bauchgefühl, sie können auch anderen Menschen erklären, wieso sie die Dinge so geschrieben haben, wie sie sie geschrieben haben.
In einem Prosatext würden wir aber nie auf die Idee kommen, guten Schreibstil zu quantifizieren. Kein Literaturexperte würde sagen: Wir haben Shakespeares Mittsommernachtstraum gemessen und geben ihm eine 9,7 von 10 auf dem Prosa-Qualitätsindex.
Nur gibt es bei Code andere Herausforderungen als bei Prosa. Source Code hat über seinen Lebenszyklus hinweg nicht einen Autor oder einige wenige Co-Autoren, sondern Dutzende, die den Code immer wieder erweitern, warten und anpassen müssen. Prosa ist irgendwann fertig und veröffentlicht, Code lebt so lange wie das Produkt lebt, also oft für Jahrzehnte. Wenn der Prosatext eine schlechte Qualität hat, kann man daraus lernen und es nächstes Mal besser machen. Im schlimmsten Fall liest ihn niemand. Wenn der Code eine schlechte Qualität hat, dann leidet darunter die Wettbewerbsfähigkeit des Produkts. Wir wollen darum konstant darauf achten, dass der Code trotz aller Anpassungen auf einem hohen Qualitätsniveau bleibt. Ein Qualitätsmessung-Tool, das den Zustand des Codes oft misst und einen Alarm auslöst, wenn es Einbrüche gibt, kann sehr schnell für Transparenz sorgen und bei diesen Herausforderungen eine wichtige Hilfe sein.
Wie messen wir, ob Code sauber ist?
Die Antwort in einem Satz: Wir können die Sauberkeit nicht messen.
Wenn man sauberen und unsauberen Code vergleicht, kann man viele Merkmale finden, in denen sie sich unterscheiden. Viele von diesen Merkmalen sind auch klar messbar und quantifizierbar. Die Vermutung liegt darum nahe, dass wir die Sauberkeit messen, wenn wir diese Merkmale messen.
In einem Prosatext können wir auch viele Maße erheben, die uns einen Anhaltspunkt geben, ob der Text wohlgeschrieben ist. Wenn es in einem Prosatext keine Punkte, Kommata und Absätze gibt, dagegen jedoch zahllose Rechtschreibfehler, dann können wir davon ausgehen, dass der Text mit wenig Sorgfalt geschrieben wurde. Der Umkehrschluss, dass der Text wohlgeschrieben ist, wenn er frei von diesen Merkmalen ist, ist aber offensichtlich falsch.
Wenn im Code die Klassen und Methoden endlos lang sind, viele Duplikate vorhanden sind, keine Naming Conventions eingehalten werden, sehr wenige Zeilen von Tests abgedeckt werden und zahlreiche ungenutzte Fragmente ohne erkennbaren Sinn existieren, dann können wir ebenfalls davon ausgehen, dass der Code mit wenig Sorgfalt geschrieben wurde. Der Umkehrschluss, dass der Code sauber ist, wenn er frei von diesen Hinweisen ist, ist aber falsch.
Ähnlich wie bei Prosa gibt es zahlreiche Aspekte, die essenziell, aber nicht messbar sind: Haben die Variablen sinnvolle Namen? Kann die Fachlichkeit im Code leicht nachvollzogen werden? Sind die Kommentare aussagekräftig oder redundant? Sind die Schnittstellen schlank und intuitiv? Passen die Modelle zu Ihren Verwendungen?
Es gibt viele Fragen rund um sauberen Code, die ein Mensch nicht einfach und eine Maschine überhaupt nicht beantworten kann. Wir messen mit Metriken darum nicht, ob der Code sauber ist, das ist gar nicht möglich. Wir messen lediglich Hinweise auf mangelnde Sorgfalt.
Wann hilft die Messung?
Die Antwort in einem Satz: Wenn die Entwickler sie als Hilfstool wahrnehmen.
Ein Hinweis auf einen Mangel an Sorgfalt kann hilfreich sein, um sich nach dem Implementieren eines Features erneut ein paar kritische Fragen zu stellen und eventuell Dinge auszubessern: Gibt es neue Logik, für die ich keinen Test geschrieben habe? Diese Methode ist sehr lang, ist sie noch gut lesbar? Habe ich versehentlich gegen bestehende Konventionen verstoßen? Habe ich vergessen, den Code zu formatieren?
Wenn mir zum Beispiel eine Metrik zeigt, dass eine Klasse zu lang geworden ist, dann kann ich mir die Klasse nochmal genauer anschauen. Vielleicht verbirgt sich in der Klasse ein weiteres Objekt, das man einfach extrahieren könnte. Eine Qualitätsmetrik kann Clean Codern helfen, unsaubere Arbeit zu identifizieren, die sie sonst übersehen hätten. Sie hilft also dabei, den neuen Code sauberer in das Produkt zu integrieren.
Wann bringt die Messung nix?
Die Antwort in einem Satz: Wenn wir sie ignorieren oder wenn sie selten hilfreiche Hinweise liefert.
Ein Beispiel: Ich war in einem Projekt, in dem es einen Qualitätsbeauftragten gab, der allein bestimmt hat, wie Qualität gemessen wird. Diese Verantwortung wurde auch sehr ernst genommen und zum Projektstart wurden akribisch mehrere hundert Regeln evaluiert and angepasst. Die Entwickler konnten Feedback geben, aber es gab zu viele Entwickler in zu vielen Teams, als dass diese effektiv mitentscheiden konnten. Die Regeln sollten nach Projektstart nicht mehr angepasst werden, damit die alten Messpunkte zum Stand der Qualität nicht verfälscht wurden. In dieser Konstellation existierte von Beginn an eine deutliche Abweichung zwischen den Metriken und dem, was sich die Entwickler unter Qualität vorstellten. Da sich diese Vorstellungen dann noch im Laufe des Projekts veränderten, wurde die Abweichung immer größer.
Zum Beispiel gab es eine Regel, die in Javascript-Dateien die Kommentardichte messen sollte und anschlug, wenn nicht mindestens 25% des Codes aus Kommentaren bestand. Die Entwickler verfolgten aber immer mehr den Ansatz, die Kommentare auf ein Minimum zu reduzieren und den Code so zu schreiben, dass so wenig erläuternde Kommentare wie möglich nötig waren.
Zusätzlich waren die Messungen auch noch nur auf einem gut versteckten Server sichtbar, den die Entwickler aus eigenem Antrieb regelmäßig besuchen mussten.
Wenn jeder Scan des Codes zahlreiche Warnungen hervorbringt, die aber größtenteils in den Augen der Entwickler zu keiner Verbesserung der Qualität beitragen würden, tendieren sie schnell dazu, die Metriken vollkommen zu ignorieren. Niemand verschwendet gerne Zeit oder tut Dinge, die er nicht für sinnvoll hält. Die Messungen müssen darum zu den Qualitätsvorstellungen der Entwickler passen. Damit ist nicht gemeint, dass die Entwickler ihre Qualitätsvorstellungen an die Messungen anpassen sollen. Stattdessen sollten Entwickler die Möglichkeit haben, ihre Hilfswerkzeuge selbst zu konfigurieren.
Wenn eine Metrik den Entwicklern wenig oder sogar gar nicht hilft, ist das leider kein Nullsummen-Spiel. Metriken einzurichten, zu konfigurieren, auszudiskutieren und zu warten kostet Zeit, genau wie die Identifikation von false positives.
In dem oben genannten Beispiel-Projekt wurde gegen folgende 3 Prinzipien verstoßen, wodurch die Wahrscheinlichkeit drastisch stieg, dass diese Messungen eher Zeitverschwendung waren als sinnvolle Hilfen:
- Anpassbarkeit: Die Entwickler müssen die Art, wie gemessen wird, leicht und schnell ändern können. Nur dann kann die Messungen mithalten, wenn die Qualitätsansprüche eine Evolution durchlaufen.
- Automatisierung: Der Code wir automatisch gemessen, zum Beispiel bei jeder Codeänderung oder täglich in einem Nightly-Job. Qualitätseinbrüche sind leicht sichtbar, zum Beispiel durch eine Warnung in der deployment-pipeline oder eine Mail an die Entwickler, die an den Änderungen beteiligt waren.
- KISS (Keep It Simple Stupid): Es lohnt sich selten, direkt zum Projektstart viel Aufwand in komplizierte Messungen zu investieren. Wenige simple Regeln oder etablierte Standardregelwerke reichen aus. Lange Regelwerke und hypothetische Qualitätsdiskussionen, bevor man überhaupt angefangen hat, Code zu schreiben, gehen schnell in Zeitverschwendung über.
Wann schadet die Messung?
Die Antwort in einem Satz: Wenn die Messung missverstanden wird oder wenn die Messung zum Ziel wird.
Dass eine Messung missverstanden wird, passiert leider zu häufig, gerade wenn die Beteiligten wenig über Softwarequalität wissen oder wenig Interesse daran haben. Wenn anhand einer falschen Informationsgrundlage Entscheidungen getroffen werden, können sehr leicht absurde ‚ex falso quodlibet‘-Beschlüsse entstehen.
Mir wurde einmal ein, laut Messung, ‘sauberer’ Service präsentiert. In der Tat zeigte die Messung, dass die Klassen und Methoden nicht extrem lang waren, über 80 Prozent der Codezeilen von Tests abgedeckt waren und keine der geläufigen Code Smells gefunden wurden.
Also alles gut? Keineswegs. Als ich mich im Laufe der nächsten Monate in den Service einarbeitete, stieß ich auf große Schwierigkeiten. Es gab sehr wenige Objekte im Sinne der Objektorientierung, stattdessen viele Serviceklassen, in denen in langen Programmsequenzen Daten verarbeitet wurden. Oft wurden nicht einmal Datenhalter-Klassen verwendet, sondern generische Double, Triple, Quadruple-Klassen, in denen alles Mögliche stehen konnte. Ich konnte die fachlichen Prozesse im Code nicht ohne größeren Aufwand nachvollziehen. In dem Chaos verbargen sich auch einige hartnäckige und bekannte Bugs, die zuvor nicht gefixt werden konnten und erst jetzt, im Laufe der Aufräumarbeit, offensichtlich und leicht reparierbar wurden. Einige der Beteiligten waren bestürzt und überrascht, als transparent wurde, dass der Service keine exzellente interne Qualität hatte, sondern lediglich eine beim Aufräumen sehr hilfreiche Testabdeckung.
Dass eine Messung zum Ziel wird, passiert leider auch häufig. Selten wird explizit gewünscht, dass die Messung zum Ziel gemacht wird, implizit passiert es dann aber doch. Einige Beispiele dafür:
- Ein build kann nur durchlaufen, wenn die Qualitätsmessung dies zulässt, unabhängig davon, ob die Messung false-positives enthält oder aus anderen Gründen in diesem Kontext keinen Sinn ergibt.
- Ein Projekt soll saniert werden. Das wichtigste Maß für den Fortschritt ist eine regelmäßige Qualitätsmessung.
- Das Management möchte sicher sein, dass die Entwickler eine hohe interne Qualität liefern. Darum verwendet es die Qualitätsmessungen als Überwachungswerkzeug.
- Die Entwickler wollen selbst ihrem Umfeld demonstrieren, wie gut sie sind und verweisen darum immer wieder stolz auf Ihre Metriken und deren Verlauf. Unabsichtlich wird dabei den nicht-Technikern immer wieder beigebracht, dass eine gute Messung gleichbedeutend mit guter Qualität ist.
Ist die Messung erst zum Ziel geworden, dann beginnen die Entwickler, sonderbare Dinge zu tun. Wenn eine Klasse oder Methode zu lang ist, wird sie einfach in der Mitte in zwei Teile gehauen und nicht etwa neu designt oder sinnvoll abstrahiert. Wenn die Testabdeckung zu niedrig ist, werden monsterartige Tests gebaut, die irgendwie den Code durchlaufen, egal, wieviel Laufzeit und Setup sie dabei brauchen. Das verbessert weder die Testbarkeit des Codes, noch entsteht so eine wohlbedachte Testsuite-Architektur. Lange Kataloge von Regelverstößen werden abgearbeitet, egal wie, unabhängig davon, ob sie einen Einfluss auf die Lesbarkeit haben. All diese Arbeit an der Optimierung der Messungen könnte stattdessen investiert werden, um sich über die fachlichen Inhalte des Codes bewusst zu werden und diese klar und einfach darzustellen. So eine Arbeit belohnt die Metrik auch, sie ist aber deutlich anspruchsvoller und zeitaufwändiger und darum nicht ‘sinnvoll’, wenn das Ziel nur darin besteht, in der Qualitätsmessung gut abzuschneiden.
Gute Entwickler lassen sich nicht so einfach hinreißen, unsinnige Dinge zu tun, wir wollen ja sinnvolle Arbeit machen. Allerdings sollte man nicht den schlechten Einfluss eines Umfeldes unterschätzen, das die Metrik-Zufriedenstellung mehr belohnt als die anstrengende, diskussionsaufwändige Sanierarbeit.
Fazit
Qualitätsmetriken messen nicht Qualität, sondern Hinweise auf mangelnde Sorgfalt. Klingt halt weniger schön, ist aber so. Das falsche Versprechen, dass Qualität einfach gemessen werden kann, hört sich natürlich verführerisch an. Vieles wäre einfacher, wenn Clean Code quantifizierbar wäre, dann wäre auch der Beruf des Softwareingenieurs deutlich simpler. Durch Missverständnisse und schlecht gewählte Ziele wird aus einem hilfreichen Tool für die Entwickler schnell ein falscher Transparenz-Mechanismus für das Umfeld. Code hoher Qualität kann nicht durch Druck und Kontrolle von außen entstehen und auch nicht durch ein Tool herbei gemessen werden. Code hoher Qualität entsteht durch verantwortungsbewusste Softwareingenieure, die mit viel Erfahrung, Leidenschaft und fachlichem Austausch etwas aufbauen, das sich wie wohlgeschriebene Prosa liest.