Graham Brooks bricht eine Lanze dafür, Code nicht mit der Gießkanne zu refaktorisieren, sondern den Aufwand dort zu treiben, wo es gerade besonders nutzt. Das hört sich sinnig an.
Noch sinniger wird es, wenn Graham als Nützlichkeitsindikatoren Giftigkeit (Toxicity) und Unbeständigkeit (Volatility) angibt.
Giftig (als Steigerung von schmutzig) ist der Code dort, wo bestimmte Metriken besonders schlimme Werte annehmen. Unbeständig ist er dort, wo häufig an ihm gearbeitet wird.
In Kombination ergibt sich eine Giftigkeit x Unbeständigkeit Matrix, die klar sagt: Refaktorisiere, wo häufiger Veränderungskontakt mit hoher Giftigkeit besteht; aber lass Code stabilen (relativ) ungiftigen Code in Ruhe. Das hört sich sinnig an.
Quelle: Graham Brooks
Nun fragt Jens Schauder in einem Tweet aber zurecht: Und wie misst man nun Giftigkeit? (Die Unbeständigkeit ist kein Problem; sie lässt sich aus einem VCS Log herauslesen.)
Graham sagt nichts dazu. Er verweist vielmehr auf diesen Artikel von Erik Dörnenburg. Der zeigt dann ein konkreteres Giftigkeitsdiagramm und gibt konkrete Metriken an. Auch sehr sinnig.
Wenn Sie nun aber einmal diese Grafik genauer anschauen,…
Quelle: Erik Dörnenburg
…dann fällt Ihnen sicher auf, dass sich im Grunde nur bei vielleicht 10 (7,5%) von 132 Dateien die Giftigkeit aus mehr als Zyklomatischer Komplexität (ZK) und Methodenlänge (ML) ergibt.
Die Werte anderer Metriken spielen diesen beiden gegenüber kaum eine Rolle - oder sie korrelieren mit ihnen positiv: wo ZK+ML groß ist, da ist z.B. auch der Fan-Out (efferente Kopplung) groß.
Ich frage mich deshalb: Was soll dieser ganze komplizierte Metrikenkram? Wenn man sonst nichts zu tun hat, kann man sich damit einen schönen Tag machen und wichtig aussehen. Für die Entscheidung an der Praxisfront jedoch reichen einfachere Heuristiken. Und um nicht mehr als Heuristiken geht es ohnehin. Denn auch die exaktesten Berechnungen von Metriken sollten nicht darüber hinweg täuschen, dass Giftigkeit vor allem im Auge des Betrachters entsteht.
Ich mache es mir in der Codebeurteilungspraxis einfacher. Mein Fokus liegt auf nur zwei Metriken:
- LOC: Je länger eine Codeeinheit, desto schmutziger und schließlich giftiger ist sie. (Oder genauer: Es geht eigentlich um Anweisungen und nicht um Lines. Aber LOC hat sich einfach eingebürgert als Begriff.)
Wieviele LOC eine Codeeinheit hat, kann man sehen. - Zyklomatische Komplexität: Je höher die ZK, desto schmutziger und schließlich giftiger ist eine Codeeinheit.
Wie die ZK einer Codeeinheit ist, kann man abschätzen nach der Zahl der Kontrollanweisungen (Fallunterscheidungen, Schleifen) und ihrer Schachtelungstiefe.
Auch hier gibt es natürlich eine gewisse positive Korrelation: bei wenigen LOC wird die ZK eher gering sein, bei vielen LOC wird sie tendenziell auch steigen.
Dennoch möchte ich auf keine der Metriken verzichten. Auch wenn sie korrelieren, repräsentieren sie unterschiedliche Aspekte des Codes. LOC steht für mich eher im Zusammenhang mit dem SRP. Und ZK steht für mich eher im Zusammenhang mit Testbarkeit.
Ab welcher Länge oder Komplexität wird Code nun aber nicht nur schmutzig oder giftig? Meine persönlichen Alarmglocken gehen an, wenn eine Methode länger als eine Bildschirmseite ist, also bei LOC > 20-30, und/oder mehr als 2-3 Kontrollanweisungen enthält und/oder die Schachtelungstiefe > 2 ist.
Letztlich geht es aber für das Refaktorisieren weniger um absolute Zahlen. Refaktorisiert werden sollte dort, wo die Giftigkeit “nur” größer ist als woanders. Das gilt für Methoden mit 10.000 LOC und ZK 20 vs 8.000 LOC mit ZK 15 genauso wie für Methoden mit 100 LOC und ZK 10 vs 80 LOC mit ZK 6.
Und dieses Verhältnis sollte dann auch noch im Lichte der Unbeständigkeit betrachtet werden. Da stimme ich Graham Brooks zu.
Darüber hinaus interessiert mich allerdings noch die afferente Kopplung (AK, Fan-In). Denn wenn Codeeinheiten von vielen anderen referenziert werden, haben sie eine große Strahlkraft. Änderungen/Fehler an/in ihnen wirken sich leicht auf weite Teile der Software aus. Das bedeutet, Codeeinheiten mit hoher afferenter Kopplung verdienen ein besonderes Augenmerk.
Wenn ich eine Liste mit Codeeinheiten und ihren Metriken wie die folgende hätte
Codeeinheit(Name, LOC, ZK, AK, Unbeständigkeit)
würde ich sie so sortieren:
- Zuerst Codeeinheiten mit hoher Unbeständigkeit...
- ...dann solche mit hoher afferenter Kopplung...
- ...dann solche mit hoher LOC...
- ...dann solche mit hoher ZK.
Komplizierter muss die Entscheidungsgrundlage für die Refaktorisierung nicht sein, finde ich. Es sind keine komplizierten Schwellenwerte nötig, keine weiteren Metriken. LOC und ZK kann man sogar mit dem unbewaffneten Auge recht gut beurteilen. Für Unbeständigkeit und AK ist Werkzeugunterstützung nützlich.