Die Autoren von “The Art of Readable Code [Amazon-Werbepartner] ” haben über einen Zeitraum von 5 Jahren den teameigenen wie auch fremden Quellcode in großem Umfang analysiert. Sie wollten wissen was schlechten Source Code ausmacht und an welchen Kriterien man dieses festmachen kann. Ihr schlichtes und dennoch allgemeingültiges Fazit: Source Code muss vor allem lesbar sein. Nicht Testbarkeit, Design, Modularisierung usw. müssen die primären Ziele sein, sondern die Tatsache ob ein Fremder (oder man selbst später) den Source Code zügig versteht. Das Beste: Gut lesbarer Source Code führt meist automatisch zu guter Struktur, Testbarkeit usw.
Folglich gibt es für die Verfasser eine zentrale Metrik, an welcher die Qualität von Source Code zu messen ist (bezeichnet als “The Fundamental Theorem of Readability”):
Source Code sollte so geschrieben werden, dass ein anderer Entwickler ihn in minimaler Zeit versteht.
Also lasst die Stoppuhr mitlaufen, wenn jemand euren Code zu verstehen versucht. :)
Das Buch besteht aus einer Vielzahl von Tipps und praktischen Beispielen, die zeigen wie wir Softwareentwickler gut lesbaren Quellcode produzieren. Dabei wird das Rad nicht neu erfunden. Viele Hinweise sind dem erfahrenen Entwickler (hoffentlich) bekannt und können auch anderer Fachliteratur entnommen werden. Dennoch hat das Buch einen ganz besonderen Reiz. Es ist eine kompakte und programmiersprachenunabhängige Zusammenfassung, die uns sehr anschaulich, u.a. mit lustigen Cartoons, die wichtigsten Kriterien für guten Source Code einimpft.
Im ersten von vier Abschnitten geht es in dem Buch um “oberflächliche Verbesserung”, wozu insbesondere das Thema Naming, Formatierung und Kommentare gehören.
Das wohl wichtigste Kriterium für gut lesbaren Source Code ist die “Benamung” von Variablen, Methoden/Funktionen, Klassen und Modulen. Ein Name ist schnell vergeben. Einen ausdrucksstarken und verständlichen zu finden, ist jedoch nicht trivial. Namen sollten einen beschreibenden Informationsgehalt beinhalten und wenig Interpretationsspielraum beinhalten. Dabei darf er aber auch nicht zu lang werden, da sonst die Lesbarkeit wieder leidet.
Ein Beispiel: Was ist schlecht an der Benennung der folgenden Konstante ?
SHOPPING_CARD_ITEM_LIMIT = 15
Bedeutet es, dass keine 15 Gegenstände in den Warenkorb gelegt werden dürfen (also maximal 14)? Oder sind 15 Items noch erlaubt, aber keine 16 mehr? Klar wird es, wenn man MAX (oder MIN) verwendet. Der versierte Programmierer weiß, dass MAX immer als “inklusive” definiert ist. In diesem Beispiel darf der Käufer also bis zu 15 Dinge shoppen:
MAX_SHOPPING_CARD_ITEMS = 15
Die Kunst ist, sich bei der Vergabe von Namen in eine fremde Person zu versetzen. Stelle Dir immer die Frage: Würde die fremde Person genau verstehen, was gemeint ist?
Hier ein paar weitere Tipps zum Thema “good Naming”:
Zu den oberflächlichen Verbesserungen, welche deutlich die Lesbarkeit des Source Code verbessern, gehört die Formatierung. Stelle sicher, dass Dein Quellcode so formatiert ist, dass auf den ersten Blick Zusammenhänge richtig erkannt werden können.
Vergleiche beispielsweise folgende Codeschnipsel mit gleichem Inhalt aber unterschiedlicher Formatierung, dann weißt Du was gemeint ist. :)
List config = [[ "timeout", 123, null, FIRST_PARAM_ALLOWED],
[ "firstInFirstOutUsage", null, "<>", null ],
[ "useragent", 9934.34, "sdfwekljlwerwerwer", NO ]]
eingerückt:
List config = [
[ "timeout", 123, null, FIRST_PARAM_ALLOWED ],
[ "firstInFirstOutUsage", null, "<>", null ],
[ "useragent", 9934.34, "sdfwekljlwerwerwer", NO ]
]
Noch wichtiger als die absolut perfektionierte Formatierung ist jedoch eine einheitliche Regelung im Projekt:
Einheitliche Formatierung ist wichtiger als überall die “richtige” Formatierung zu verwenden!
So ist es z.B. unerheblich ob die Klammersetzung so
class Logger { .... };
oder so
class Logger { ….. };
erfolgt. Beides ist ok, es sollte jedoch nur eine Variante von allen Teammitgliedern verwendet werden. Haltet eure Formatierungskonventionen kurz und schriftlich fest und verwendet die IDE-Formatierungs-Tools um diese durchzusetzen.
Eine strikte und moderne Auffassung besagt, dass der Source Code an sich nicht dokumentiert werden sollte. Der Code muss so geschrieben sein, dass er selbsterklärend und somit leicht lesbar ist. Das einzige, welches dokumentiert werden sollte, ist das “Warum”. Also die Frage wieso diese Methode/Klasse/Modul usw. notwendig ist. Die Dokumentation würde in so einem Falle also ausschließlich Fragen zu Designentscheidungen und fachlichen Anforderungen erläutern. Die Kommentare würden keinesfalls erläutern, “was” der Source Code macht.
Für die Autoren des Buches ist dieser Ansatz zu radikal. Sie plädieren für einen gesunden Mittelweg. Selbstverständlich ist die Dokumentierung des “Warums” richtig und notwendig. Beim Schreiben des Quellcodes sollten wir unsere Gedankengänge und Entscheidungen immer kurz und präzise im Code kommentieren. Darüber hinaus gibt es jedoch durchaus Fälle, wo ein kurzer Kommentar über das “Was” die Lesbarkeit des Codes deutlich erhöht.
Nicht sinnvoll sind Kommentare…
Sinnvoll sind Kommentare…
Nach den oberflächlichen Verbesserungen für lesbaren Code, kümmern sich die Autoren um das Thema wie komplexere Logiken und Schleifenkonstrukte verständlich bleiben.
Ihre Ansage an uns ist dabei folgende:
Alle Bedingungen, Schleifen und andere Konstrukte, die den Kontrollfluss beeinflussen, sollten so “normal und natürlich” wie möglich formuliert werden -- also in der Form, dass ein Leser nicht anhalten und Source Code wiederholt lesen muss.
Im Folgenden ein paar -- aus meiner Sicht wichtige und selbstverständliche --Tipps der Autoren
Die linke Seite in Bedingungen sollte die variablere sein, die rechte die konstantere:
Also
if (length >= 10) // good
anstatt
if (10 <= length) // bad
Weiter gilt für die Frage was in den if-Zweig vs. else-Zweig geschrieben werden soll, folgendes:
Zusammenfassend weitere wichtige Tipps, um die Lesbarkeit von komplexen Logiken zu verbessern:
Beim Engineering geht es darum große komplexe Probleme in kleine Einheiten zu teilen und diese für eine Gesamtlösung geschickt zusammenzuführen. Das gleiche Prinzip gilt auch für das Software Engineering. Die Autoren zeigen im dritten Teil des Buches, was dabei besonders zu beachten ist, so dass die eigene Software maximal lesbar bleibt. Freunde des Refactorings werden sich hier wohl fühlen.
Im ersten Abschnitt geht es um die Extrahierung von Code in eigenständige Funktionen (Refactor: Extract Method). Weitere oben habe ich dieses Verfahren nicht ohne Grund bereits als “Nr.1 für lesbaren Code” bezeichnet.
Die Autoren betrachten das Extrahieren von Funktionalität insbesondere für den speziellen Fall von unabhängigen Teilproblemen (“unrelated subproblems”). Anhand eines JavaScript-Beispiels zur Bestimmung eines Ortes, der am nahesten zu einer gegebenen Lokation liegt, machen sie es deutlich:
Vorher:
// Return which element of 'array' is closest to the given latitude/longitude.
// Models the Earth as a perfect sphere.
var findClosestLocation = function(lat, lng, array) {
var closest;
var closest_dist = Number.MAX_VALUE;
for (var i = 0; i < array.length; i += 1) {
// Convert both points to radians.
var lat_rad = radians(lat);
var lng_rad = radians(lng);
var lat2_rad = radians(array[i].latitude);
var lng2_rad = radians(array[i].longitude);
// Use the "Spherical Law of Cosines" formula.
var dist = Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad) +
Math.cos(lat_rad) * Math.cos(lat2_rad) *
Math.cos(lng2_rad - lng_rad));
if (dist < closest_dist) {
closest = array[i];
closest_dist = dist;
}
}
return closest;
};
Nachher (Berechnung der Distanz ausgelagert):
var spherical_distance = function(lat1, lng1, lat2, lng2) {
var lat1_rad = radians(lat1);
var lng1_rad = radians(lng1);
var lat2_rad = radians(lat2);
var lng2_rad = radians(lng2);
// Use the "Spherical Law of Cosines" formula.
return Math.acos(Math.sin(lat1_rad) * Math.sin(lat2_rad) +
Math.cos(lat1_rad) * Math.cos(lat2_rad) *
Math.cos(lng2_rad - lng1_rad));
};
var findClosestLocation = function(lat, lng, array) {
var closest;
var closest_dist = Number.MAX_VALUE;
for (var i = 0; i < array.length; i += 1) {
var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitude);
if (dist < closest_dist) {
closest = array[i];
closest_dist = dist;
}
}
return closest;
};
Also: Wann immer Source-Code wiederverwendet wird (“Redundanzen”) oder ein unabhängiges Teilproblem aus vorhandenem Code extrahiert werden kann, gönnt euch eine neue Funktion, Methode oder Klasse. Aber auch hier gilt: Findet ein gesundes Mittelmaß. Man könnte theoretisch jede kleinste Teil-Funktionalität in eine eigene Funktion auslagern. Dies wäre kontraproduktiv und würde die Lesbarkeit verschlechtern.
Weiter geht es in dem Buch mit der Frage, warum es für lesbaren Software-Code extrem wichtig ist, Aufgaben nicht zu vermischen. One Task at a Time. Sonst ergeht es unserem Quellcode wie diesem Mann:
Das Ziel ist es folglich die Aufgaben in unserem Code nicht zu vermischen. Mit anderen Worten: Vermeide Seiteneffekte und behandle in einem Code-Block immer nur eine Funktion/Fachlichkeit.
In dem Buch wird dieser Vorgang auch als “Defragmentierung” beschrieben. Dabei sollen unterschiedliche Aufgaben in unterschiedliche Funktionen ausgelagert werden. Jedoch auch innerhalb eines Codeblocks sollten zusammenhängende Aufgaben gruppiert werden:
Ein weiterer Abschnitt im dritten Teil des Buches beschreibt ein einfaches Verfahren, dass uns hilft, den Code besser zu strukturieren und lesbarer zu machen. Die Idee leitet sich von dem berüchtigten Albert Einstein Zitat ab:
”Was man der eigenen Großmutter nicht erklären kann, hat man nicht wirklich verstanden.” (Albert Einstein)
Das gilt gleichermaßen für unseren Source Code. Wenn wir nicht in einfachen Worten wiedergeben können, was unser noch zu schreibender Quellcode tun soll, wird dieser höchstwahrscheinlich nicht sehr lesbar werden. Von daher empfehlen die Autoren folgendes Vorgehen auszuprobieren:
Im Buch werden hierzu ein paar Beispiele aufgezeigt. Das Verfahren überzeugt. :)
Ein letzter kleiner -- aus meiner Sicht aber wichtiger -- Hinweis in diesem Abschnitt: Um prägnante und damit lesbare Software zu programmieren, muss man wissen was die Programmiersprache und die API/Bibliotheken alles anbieten.
Der letzte Abschnitt im dritten Teil des Buches, lautet: “Weniger Code schreiben”.
Kernfrage: Was ist der am besten lesbare Source Code?
Einfache Antwort: Der, der gar nicht geschrieben wurde!
Der Les- und Wartbarkeit eures Software-Projektes tut ihr einen großen Gefallen, wenn ihr folgende Hinweise zum Reduzieren von Code beachtet:
Im letzten Teil des Buches veranschaulichen die Autoren das Gelernte sehr anschaulich an zwei Praxisbeispielen. Zum einen wird gezeigt wie Unit-Tests lesbar geschrieben werden und wie sich testbarer Code und Lesbarkeit gegenseitig befruchten. Zum anderen wird Schritt für Schritt anhand der Implementierung eines “Minuten/Stunden Counters” gezeigt, wie die Konzepte aus dem Buch iterativ in einem Projekt angewendet werden können.
Die Quintessenz aus dem Buch könnte man wie folgt zusammenfassen:
Versetze dich in einen neuen Kollegen, der Deinen Source Code verstehen muss. Würde ihm das leicht fallen? Falls Du einen Kollegen zur Hand hast noch besser: Frage ihn, ob er Deinen Code versteht (“Kurzes oberflächiges Code Review”). Wenn nicht, verbessere ihn oder füge Kommentare hinzu.
Das Buch ist sehr empfehlenswert, um in kompakter Form wesentliche Konzepte zum Programmieren von lesbarer Software zu erlernen bzw. aufzufrischen. Es kann u.a. bei Amazon [Amazon-Werbepartner] oder innerhalb eines Safari-Abos bei O’Reilly betrachtet und erworben werden.