Gestern ist mir mal wieder unschöner Programm-Code über den Weg gelaufen. Das hat mich dazu animiert, mal die häufigsten Fehler aufzulisten, die ich immer mal wieder sehe und über die ich mich dauerhaft ärgern kann.
Dabei geht es mir noch nicht mal um irgendwelche abstrakten Programmiermodelle oder best practices im Architekturbereich. Es geht um kurze, alltäglich Codestücke, die man eigentlich nicht falsch machen kann. Und doch wird es gerne falsch gemacht. Es geht auch nicht um Syntaxfehler, die filtert der Compiler raus. Sondern einfach nur um schlechten Code, der irgendwie funktioniert.
1. Unklare Vorstellungen über boolsche Werte
Wenn ich solche Abschnitte sehe, stellen sich mir die Nackenhaare auf:
boolean isEnabled = myButton.getEnabled();
if (isEnabled == true) { ...
Die Variable isEnabled enthält einen boolschen Wert, die IF-Entscheidung erwartet einen. Warum dann noch diesen zusätzlichen Vergleich dazu packen. Damit sich der Compiler auch wirklich sicher ist, dass der Wert true ist? Und dann könnte man konsequenterweise doch auch „if ((isEnabled == true) != false)“ schreiben.
Mir signalisiert so eine Vorgehensweise, dass der Entwickler nur eine vage Vorstellung von boolschen Werten und ihrer Verwendung besitzt.
2. Überflüssige Kontrollstrukturen
Dieses Beispiel geht in die gleiche Richtung wie Beispiel 1.
boolean isEnabled = myButton.getEnabled();
if (isEnabled) {
otherButton.setEnabled(true);
} else {
otherButton.setEnabled(false);
}
Hier wird ohne nachvollziehbaren Grund eine komplette Kontrollstruktur aufgebaut. Dabei kann ich mich gar nicht über die verschenken Nanosekunden ärgern. Aber jeder andere Entwickler erwartet hier, dass etwas passiert, was schwergewichtiger ist als eine einfache Zuweisung: „otherButton.setEnabled(isEnabled)“.
Leider kommt dieser Fehler in der Praxis immer wieder vor. Auch hier liegt vermutlich oft einfach ein Defizit im Bereich „mal darüber nachdenken, was ich gerade mache“ vor.
3. Verdrehte Ausdrücke
Eines meiner Lieblingsaufreger, besonders gern von alten C Programmierern gemacht:
if ( 7 == anzahlWochentage) ...
Das liest sich einfach schlecht – Obi-Wan Kenobi Grammatik. Wenn man den Ausdruck umgangssprachlich formulieren würde, würde man sagen „Wenn die Anzahl der Wochentage gleich 7 ist“ und keinenfalls „Wenn 7 die Anzahl der Wochentage ist“. Auch wenn der Inhalt der gleiche ist, stolpert man über die falsche Reihenfolge, der Gedankenfluss wird gestört, die Aufmerksamkeit ist beeinträchtigt.
Die Herkunft dieses Anti-Patterns ist klar. Uralte C Compiler (älter als 30 Jahre), haben die Anwender leichtfüßig in Fehler der Art „if (anzahlWochentage = 7)…“ laufen lassen. Durch das vergessene zweite Gleichheitszeichen wird aus dem Vergleich eine Zuweisung. In der umgedrehten Schreibweise führt das zu einem Compilerfehler, da ein konstanter Wert nicht als Left-Value verwendet werden kann. Aber auch vor 30 Jahren gab es schon „lint“ für solche Probleme. Alle modernen Compiler werfen hier deutliche Warnings aus, so dass man diesen Fehler eigentlich nicht mehr machen kann.
Ein Entwickler, der sich dieser Schreibweise bemächtigt, löst bei mir schnell den Verdacht aus, dass er sich nicht um seine Warnings kümmert. Oder, dass er so viele Warnings hat, dass er sie gar nicht mehr kontrollieren kann. Beides ist schlecht, der Programmierer bewegt sich Haarscharf an der Grenze zum Chaos.
4. Magic Numbers
Die Verwendung von symbolischen Konstanten statt Zahlen im Quellcode ist mittlerweile fast überall Standard. In vielen Firmen gibt es Style Guides, die die Verwendung von Magic Numbers ausdrücklich verbieten.
Der Nutzen von Konstanten liegt auf der Hand. Wenn ich fünf Produktgruppen habe und im Programmcode alle Schleifen mit der Ziffer 5 laufen lasse, habe ich bei einer Erweiterung auf 6 Gruppen massiven Aufwand. Man kann ja nicht einfach per Suchen und Ersetzen alle 5 gegen eine 6 austauschen. Statt dessen muss man sich jede Verwendung genau darauf hin ansehen, ob die Zahl der Gruppen gemeint ist. Zudem gibt es weitere Verwendungen, der Index der letzten Gruppe beträgt 4 und nicht 5. Darauf muss man also auch achten.
Übel wird es, wenn ein Programmierer zwar den Wortlaut der Regel befolgt aber den Sinn nicht verstanden hat (oder nicht verstehen will). Dann kommen solche Konstruktionen vor:
const FIVE = 5;
...
for (var group = 0; group < FIVE; group++) ...
Der Wortlaut der Regel wurde befolgt, im Programmcode selber gibt es keine Magic Numbers. Aber das Ziel wurde verfehlt. Ich sehe der FIVE in der for Schleife nicht an, was für einen Zweck sie hat. Besonders übel wird es, wenn sich die Zahl der Gruppen auf 6 erhöht:
const FIVE = 6;
So eine Vorgehensweise kann man nur begrenzt mit Unfähigkeit begründen. Das grenzt schon an Sabotage.
5. Unsinnige Kommentare
Vernünftige Kommentare zu erstellen, ist nicht unbedingt einfach. In manchen Betrieben gibt es eine Festlegung, was kommentiert werden muss. Bei uns ist es z.B. so, dass alle public Methoden mit einem JavaDoc Header versehen werden müssen. Kommentare innerhalb einer Funktion sind aber kaum über einen Style Guide definierbar. Wenn man dann einfach einen bestimmten Prozentsatz von Kommentarzeilen fordert, entstehen solche Konstruktionen:
customers++; // customer wird eins hochgezählt
Ein unglaublich blöder Kommentar, der genau das wiederholt, was der Programmcode ganz offensichtlich ausführt. Solche Kommentare sind nicht nur nutzlos. Sie sind sogar schädlich. Spätestens dann, wenn sich der Programmcode mal ändert und der nutzlose Kommentar nicht angepasst wird. Dann habe ich einen Widerspruch im Quellcode der schwere Irrtümer auslösen kann.
Kommentare sollten im Normalfall NICHT beschreiben, was passiert. Das wird durch den Programmcode am Besten beschrieben. Und wenn der schwer verständlich ist, dann sollte man sich lieber Gedanken über eine bessere Schreibweise statt über einen Kommentar machen.
Kommentare sollten beschreiben WARUM etwas passiert. Denn dieser Teil ist nicht aus dem Code abzuleiten. Vielleicht aus dem Kontext. Oder aus dem Pflichtenheft (liegt das fünf Jahre später noch in einer aktualisierten Form vor?).