Ein Käse vom Land Schlich brüllend am Strand Verdreht rollt ein Star Den Arsch ganz verbrannt Vom Brot nicht erkannt Spielt er Fiedel im Maar In's Eis bricht ein Haar Verschluckt von Brabant Der Queue wird jetzt klar Was das Sternum verbrannt Wann es Zeit zum Schlafen war
Warum Optionals nicht serialisierbar sind
Im Java Magazin vom Juli 2017 wurde im Artikel über das Projekt „vavr“ die Klasse „Option“ erwähnt, die die Java-8-Optionals ergänzt und – hurra – serialisierbar ist. Warum ist „Optional“ selbst eigentlich nicht serialisierbar?
Zunächst einmal ist Optional als Monade keine Datenstruktur, sondern ein Container der eine Verarbeitung repräsentiert. Ähnlich einem Java-Stream der dazu dient Mengen von Objekten zu verarbeiten, aber nicht zu speichern. So ist der Stream nach Gebrauch verbraucht und nicht mehr zu verwenden, für die nächste Verarbeitung muß ein neuer her.
Oder anders ausgedrückt: Das Bierfaß bleibt in den Keller, nicht der Rollkutscher der es gebracht hat… Zum anderen ist da das Ding mit null. Optionals sollen es ja ermöglichen, daß man null-Werte in gleicher Weise verarbeiten kann wie „richtige“ Objekte die nicht null sind. Bei der Deserialisierung muß man das beachten und gegebenenfalls auf das Fehlen des Objekts reagieren. Aus Java-Sicht ist aber auch Optional ein Objekt und kann „null“ sein; bei der Deserialisierung hat man also nichts gewonnen, sondern muß neben dem Fall „Optional enthält null“ eben auch den Fall „Optional ist null“ berücksichtigen. Also besser ohne Optional einpacken und beim Auspacken gleich in ein Optional stecken.
Aus dem gleichen Grund übergibt man Optionals normalerweise auch nicht als Parameter an Methoden. Funktionen die Optionals als Ergebnis liefern sind hingegen ok – wer allerdings eine solche Methode mit dem Ergebnis „null“ verenden läßt, sollte besser den Job wechseln…
Die Mär vom Zero Turnaround
In seinem Buch „Psychology of Computer Programming“ schreibt Gerald Weinberg 1971 über verschiedene Untersuchungung zur optimalen Länge der Turnaround-Zeit, also die Zeit die vergeht zwischen der durchgeführten Programm-Änderung und dem Feedback das der Programmierer über die Auswirkungen erhält. In jenen Tagen meinte das die Zeit zwischen der Abgabe des Lochkarten-Stapels beim mainframe-Operator und der Abholung des Ausdrucks mit dem Ergebnis.
Allein die Tatsache, daß die Untersuchungen kein einheitliches Ergebnisse erbrachten muß den Programmierer von heute überraschen. Warum sollte man etwas anderes erwarten als den Wunsch nach unmittelbarem Feedback? Tatsächlich gab es schon damals Manager, die genau diese Erwartung hatten. Eine Befragung brachte das erstaunliche Ergebnis, daß ein Durchschnittlicher Tunaround von 31 Minuten als zu kurz empfunden wurde. Was soll man davon halten?
Ergänzend dazu sollte vielleicht erwähnt werden, daß in jener Zeit die Time-Sharing-Systeme aufkamen, die es dem Entwickler ermöglichten die Programm-Aufträge direkt am Terminal aufzugeben und das Ergebnis abzuwarten. Anders als heute waren sich die Entwickler erstaunlicherweise noch nicht einig, ob das Dialog- dem Batch-System vorzuziehen sei.
Unvorstellbare Ansichten? Wer heute einen Computer befragt, erwartet Antwort in Echtzeit, möglichst ohne erkennbaren Verzug — schließlich will man so schnell wir möglich weiterarbeiten. Aber es wäre doch absurd annehmen zu wollen, daß der Batch-Entwickler nach Abgabe seines Lochkartenstapels die halbe Stunde däumchendrehend auf seine Kaffeetasse gestarrt hätte. Nein,, er hat vielmehr die Wartezeit damit genutzt über sein Programm nachzudenken, die nächsten Schritte zu planen, das Ziel des nächsten Probelaufs vorzubereiten. Die Auszeit gab Gelegenheit die Augen vom unmittelbaren Problem zu lösen und den Blick über die gesamte Aufgabe schweifen zu lassen.
Die lange Wartezeit erhöhte aber auch die Wertschätzung des Entwicklers für den Testlauf. Um diesen nicht durch einen Flüchtigkeitsfehler zu unbrauchbar zu machen, haben die Entwickler mehr Sorgfalt auf die Programmierung verwandt. Wir sind heute gewöhnt, daß uns die IDE unsere Syntax-Fehler beim Eintippen sofort um die Ohren haut. Wer zwischendurch auch mal mit dem klassischen Edit – compile – run – Zyklus arbeitet, kennt den Unterschied.
Was können wir davon für die heutige Entwicklungsarbeit mitnehmen? Wartezeit ist keine verlorene Zeit wenn man sich nutzt. Schrumpft die Turnaround-Zeit auf Sekundenbruchteile zusammen läßt sie sich nicht mehr nutzen, ist also tatsächlich verloren. Der geneigte Leser mag selbst mal zusammenrechnen — und darüber staunen — was ihm da über den Tag verloren geht.
Und auch an die Wertschätzung sollten wir im immer schneller rotierenden Wirbel des TDD-Zyklus denken. Je kleiner die Änderungen im Zyklus werden, desto größer wird die Gefahr daß der Entwickler den Blick für das Gesamtproblem verliert. Das klingt abstrakt und paranoid; es soll auch nicht bedeuten, daß jeder Entwickler zwangsläufig in solchen Arbeitsstil verfällt. Ich möchte es verstanden wissen als Einladung sich selbst beim Arbeiten zuzuschauen und mal zu reflektieren, welche Auswirkungen die Arbeitsweise hat.
Ich stecke während der Leerlaufzeiten immer gern den Kopf aus dem Maschinenraum. Ist die turnarount-Zeit zu lang, kommt das Boot nicht vorwärts. Ist sie zu kurz, wird es vom Kurs abkommen.
Wo liegt das Epizentrum?
Das Zentrum eines Erdbebens liegt üblicherweise unterhalb der Erdoberfläche, weshalb man es auch bisweilen als Hypozentrum bezeichnet. Das Epizentrum hingegen bezeichnet den Ort auf der Erdoberfläche, der dem (Hypo-)Zentrum am nächsten liegt. Oder für Mathematiker: der Punkt an dem die Gerade, die durch Hypozentrum und Erdmittelpunkt definiert wird, die Erdoberfläche schneidet.
Wenn also New York als „Epizentrum der Corona-Krise“ bezeichnet wird, (zur Krise siehe hier), wo liegt dann das Zentrum? Die Frage ergiebt keinen Sinn, weil der Begriff falsch gewählt ist. Man könnte statt dessen vom Brennpunkt sprechen oder ganz einfach vom Zentrum. Mit etwas Nachdenken finden sich noch mehr Worte die korrekt benennen was hier zu benennen ist — aber eben nicht das Wort „Epizentrum“. Das ist in der Tat ein wunderbar eklatantes Beispiel, da der Begriff nicht — wie sonst — nur einfach schlecht gewählt, sondern schlicht und ergreifend falsch ist.
Warum ist das überhaupt wichtig? Und warum soll das für Code-Qualität wichtig sein? Die erste und wichtigste Regel in der Software-Entwicklung ist meiner Meinung nach
Versuche jedes Ding so zu bezeichnen, daß der Bezeichner das Ding so genau wie möglich beschreibt.
Das gilt für jedes Ding: Konzepte, Objekte, Funktionen — was auch immer. Wenn ich Äpfel möchte und von Birnen spreche, dann kommt nichts vernünftiges dabei heraus. Sobald ich aber versuche die Dinge genaustmöglich zu beschreiben, werde ich jede Ungenauigkeit die sich nicht korrigieren läßt als Fehler in meiner gedanklichen Konstruktion erkennen. Das Formulieren von Gedanken formt Gedanken.
Wenn ich mal jemanden auf den falschen Gebrauch eines Wortes hinweise, bekomme ich fast immer die gleiche Antwort: Was macht das schon? Du weißt doch was ich meine.
Wo aber ist die Grenze? Wann ist die Ungenauigkeit so groß daß ein Mißverständnis entsteht? Wieviel Ungenauigkeit kann ich mir in einer Anforderung erlauben, bevor die Umsetzung aufhört den Wünschen des Auftraggebers zu entsprechen?
Das mag ja sein, aber man kann doch darüber sprechen und Mißverständnisse klären, oder?
Natürlich kann man das, aber es setzt voraus, daß der Empfänger der Nachricht immer davon ausgeht daß der Sender nicht recht weiß wovon er eigentlich spricht. Klingt das nach der Grundlage für eine ausgewogene Kommunikation? Und was ist falsch daran vom Verfasser eines Textes — sei es eine eMail, ein Zeitungsartikel oder eine Anforderung — zu verlangen daß er sich darum bemüht zu sagen was er meint? Hat der Hörer nicht ein Anrecht darauf, daß der Sprecher versucht ihn zu erreichen?
Ok, das hört sich ja nun nicht völlig verkehrt an. Und wenn es wichtig ist, also wenn es wirklich darauf ankommt, dann muß man sich natürlich um Genauigkeit bemühen. Aber ist das ein Grund den ganzen Tag mit dem Wörterbuch unter dem Arm herumzulaufen um jeden Malapropismus damit totzuprügeln?
Man frage sich: Wie soll ich — wenn’s drauf ankommt — den genauen Begriff finden, wenn ich das nicht vorher — wenn’s also nicht so drauf ankommt — trainiert habe? Ist es nicht effizienter so ausdauernd zu trainieren, daß die Anwendung zur natürlichen geistigen Bewegung wird, die keine zusätzlichen intellektuellen Kräfte durch Bewußtmachen und Nachdenken verschwendet? Die Sprache kleidet meine Gedanken. Ist es nicht wünschenswert, mit den Worten jederzeit genau das ausdrücken zu können was man denkt? Genauigkeit der Sprache ist — wie Qualität — mehr eine Geisteshaltung, weniger eine Technik.
Und wenn mir der geneigte Leser nun vorwerfen möchte meine Sprache sei manieriert und nehme überhaupt keine Rücksicht auf den Leser, dann hat er damit vielleicht nicht ganz unrecht. Aber ganz sicher hat er dann verstanden, warum es mir so wichtig ist was ich da denke und warum ich mich stets darum bemühe, es mit meinen Worten in Einklang zu bringen.
Sprache und Qualität
Was hat Sprache mit Qualität zu tun? Was hat sie insbesondere mit Code-Qualität zu tun? Bei der (natürlichen) Sprache geht es — wie beim Code — nicht nur darum, einen Sachverhalt irgendwie auszudrücken. Es gilt, ihn so auszudrücken, daß er seien Zweck möglichst gut erfüllt. Selten geht es darum, einfach nur Information zu übermitteln, man will auch überzeugen, beeindrucken, Gefühle beim Leser wecken. Und selbst wenn es tatsächlich nur um die Information geht, wird es nötig sein, die Formulierung dem Ziel-Publikum anzupassen. Ein Text kann also einen Zweck mit mehr oder weniger Qualität zu erfüllen trachten.
Daß man von Computer-Sprachen spricht, ist mehr als eine zufällige Homonymie. Zwar geht es in erster Linie darum, einen Sachverhalt exakt und eindeutig darzulegen, aber je nach dem welche Konstrukte man für die Implementierung wählt ist das Ergebnis für den Menschen mehr oder weniger verständlich. Warum ist das wichtig? Die Maschine interessiert sich nicht dafür wie das Programm aussieht, sie führt aus was ihr gegeben wird. Beim Programmierer ist das anders
Code wird für Computer geschrieben, aber für Menschen formuliert
So sinnvoll es im Umgang mit natürlicher Sprache ist über die geeignete Formulierung nachzudenken, so sinnvoll ist es, beim Formulieren von Computer-Code über die zukünftigen Leser nachzudenken. Wie Robert C. Martin ganz richtig festgestellt hat, verbringt der Programmierer wesentlich mehr Zeit damit Code zu lesen — und zu verstehen — als damit ihn zu schreiben.
Ein anderer Aspekt der natürlichen Sprachen ist nicht so einfach auf die Computer-Sprachen zu übertragen:
Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt.
„Ludwig Wittgenstein“
Computer-Sprachen sind in der Regel Turing-vollständig. In jeder (vollständigen) Computer-Sprache läßt sich alles formulieren, was sich mit einer Turing-Maschine berechnen läßt und umgekehrt. Das heißt, daß alle Computer-Sprachen — ihren theoretischen Möglichkeiten nach – gleichmächtig sind; keine „kann mehr“ als irgendeine andere. Inwiefern begrenzt aber dann die Sprache die Welt des Programmierers?
Wer schon einmal versucht hat auch nur eine Addition mit einer Turing-Maschine zu berechnen, weiß was für eine enormer Unterschied zwischen Computer-Sprachen besteht. Nutzt man zur Lösung eines Problems eine Sprache die dafür geschaffen oder gar daraufhin optimiert wurde, sehen die Programme ganz anders aus. Der Code ist kompakter, leichter zu lesen und schneller zu verstehen, umfangreichere Probleme lassen sich damit lösen.
Das soll uns aber nicht in falsche Sicherheit wiegen. In jeder Sprache lassen sich unverständliche Programme schreiben. Ich jedenfalls kenne keine Sprache die das unmöglich macht. Man kann unter „Sprache“ auch die Art und Weise verstehen, wie man die Sprache einsetzt: Spreche ich „sauberes“ Deutsch oder bin ich nachlässig in Grammatik und Ausdruck? Wähle ich die passenden Worte oder einfach Worte die eben nur „ungefähr“ das ausdrücken was ich zu denken glaube?
Schlecht formulierter Code ist nicht nur schlecht zu verstehen. Er ist in der Regel auch schlecht zu ändern und zu erweitern. Schlechte, schlampige Sprache –- im Sinne der Verwendung -– schränkt den Programmierer ein; die Sprache — bzw. die Verwendung derselben — bestimmt die Welt des Programms.
Je mehr Sorgfalt man in die Formulierung legt, desto höher die Qualität des Textes — oder des Codes. Die Beschäftigung mit Sprache — ob natürlich oder künstlich — ist für den qualitätsorientierten Entwickler eine Notwendigkeit.
Was ist eigentlich eine Krise?
Die Frage kam mir in den Sinn, als mir mal wieder der Begriff „Software-Krise“ über die Weg lief. Ein Begriff mit dem ich nie richtig glücklich war, nicht nur, aber eben auch weil er in meinen Augen falsch gewählt ist; ein misnomer wie man im Englischen sagen würde. Was also ist eine Krise? Die Antwort ist schnell gefunden, man muß sie nur suchen…
Der Begriff ist über das Lateinische zu uns gekommen und bedeutet im Griechischen „Scheidung“ oder „Entscheidung“. Er leitet sich vom Verb krinein her, das „richten“ oder „entscheiden“ bedeutet. Seine erste Verwendung fand er in der Medizin. Dort versteht man unter einer Krise den Moment im Krankheitsverlauf, in dem die Krankheit ihren Höhepunkt erreicht und es sich entscheidet, ob der Patient leben oder sterben wird.
In dieser Bedeutung läßt sich der Begriff in andere Kontexte übertragen in denen er eine Situation bezeichnet in der ein Umstand soweit eskaliert ist, daß eine Entscheidung getroffen muß. Eine Entscheidung darüber ob die Entwicklung weitergeht oder versucht werden soll sie in eine andere Richtung zu lenken.
Ein gutes Beispiel ist die Cuba-Krise im Herbst 1962. Die Stationierung sowjetischer Atom-Raketen in Kuba stellte die US-Regierung vor die Entscheidung eine permanente unmittelbare Bedrohung zu akzeptieren oder nicht. Gleichzeitig entschied die Handhabung der Krise über den Ausbruch eines atomaren Weltkriegs. Wie die Wikipedia richtig bemerkt, läßt sich eine Krise meist erst nach ihrer Bewältigung als solche identifizieren.
Eine Krise ist also im Grunde genommen ein Punkt auf der Zeitachse. Sie markiert den Kulminationspunkt der Ereignisse, mathematisch gesehen das — gegebenenfalls lokale — Maximum einer Kurve. Nun kann man argumentieren, daß sich dieses Maximum auch über einen gewissen Zeitraum erstrecken könnte. Aber die Bedeutung des Begriffs verlangt, daß eine Entscheidung getroffen wird — auch wenn es die Entscheidung ist keine Entscheidung zu treffen.
Was bezeichnet nun der Begriff „Software-Krise“? Geprägt wurde er auf einer Konferenz die die NATO 1968 in Garmisch abhielt. Tatsächlich wurde dabei auch die alternative Bezeichnung „software gap“ verwendet:
There is a widening gap between ambitions and achievements in software engineering.
Im Wesentlichen ging es darum, daß die Komplexität der Software immer schneller anwuchs und die Software-Ingenieure mit den Mitteln der Zeit mit dieser Komplexität immer schlechter fertig wurden. In den Augen der Konferenz-Teilnehmer — oder zumindest einem Teil derselben — wurde es notwendig, Technik und Arbeitsweise der Software-Entwicklung zu ändern, damit sie mit den steigenden Anforderungen Schritt halten kann. Wenn also der Zeitpunkt für eine Entscheidung — oder Entscheidungen — gekommen war, warum dann nicht von einer Krise sprechen?
Wenn damals der Kulminationspunkt erreicht war, kann man von einer Krise sprechen. Aber es liegt im Wesen der Krise, daß eine Entscheidung — oder Nichtentscheidung — zum Ende derselben führt; in der einen oder anderen Art und Weise. Entweder hätte eine Entscheidung über die Software-Technik den Patienten gerettet oder die weitere Entwicklung hätte in eine Katastrophe geführt. Die Katastrophe ist ausgeblieben — zumindest nach meinen Kenntnisstand. Daher wage ich zu behaupten, daß die Entwicklung der Software-Entwicklung die Krise — wenn sie denn eine solche war — überwunden hat.
Wenn eine Situation über fünfzig Jahre anhält, ohne daß eine Entscheidung zu ihrer Änderung getroffen wird, dann handelt es sich nicht um eine Krise, sondern um einen Zustand.
Das, was heutzutage als „Software-Krise“ bezeichnet, ist ein Zustand und er unterscheidet sich durchaus von der Situation die Ende der 60er Jahre bestand. Die Zeit ist nicht stehen geblieben, Konzepte wurden verbessert und angepaßt, es stehen unglaubliche Mengen von Hilfsmitteln zur Verfügung. Die Entwicklung zeigt, daß die Software-Entwicklung mit den Anforderungen fertig werden kann. Das Problem liegt in den immer weiter steigenden Anforderungen an Kosten und Geschwindigkeit. Software muß immer schneller und immer billiger produziert werden, das hat Konsequenzen. Aber noch ist der Kulminationspunkt nicht erreicht und daher sollte man nicht von einer Krise sprechen sondern von einem Zustand.
Über Metriken
Immer mal wieder bin ich in eine Diskussion über Metriken für Code-Qualität geraten in deren Verlauf ich mich gefragt habe, ob meine Vorstellung vom Begriff „Metrik“ eigentlich richtig ist. Um nicht jedesmal wieder zweifeln zu müssen, habe ich versucht das zu formulieren.
Als Metrik bezeichnet man in der Mathematik eine Funktion, die den Abstand zweier Punkt eines Raum (oder einer Menge von Objekten) berechnet. Unter „Abstand“ versteht man hier einen nicht negativen (reelen) Wert oder lax ausgedrückt: eine positve Zahl die auch null sein kann. Nicht jede solche Funktion ist tatsächlich eine Metrik, sie muß noch einige Bedingungen erfüllen, die hier nachzulesen sind. Für die Metriken in der Qualitätsbestimmung sind diese aber erstmal nicht von Interesse.
Die Definition nach IEEE 1061 wird üblicherweise so wiedergegeben:
Eine Software-Qualitätsmetrik ist eine Funktion, die eine Software-Einheit in einen Zahlenwert abbildet, welcher als Erfüllungsgrad einer Qualitätseigenschaft der Software-Einheit interpretierbar ist.
Sie ist also — wie ihre mathematische Schwester — eine Funktion, die Objekten — hier sind es Software-Einheiten, oder „software data“ wie es im englischen Äquivalent heißt — Zahlenwerte zuweist.
Die Software-Metrik weist also beliebigen Software-Bestandteilen Zahlen zu. Ist die „Software-Einheit“ ein Stück Code — betrachten wir im weiteren an diser Stelle Java-Klassen — dann können wir mithilfe der Metrik jeder Java-Klasse eine Zahl zuordnen. Und weil Zahlen im Gegensatz zu Java-Klassen eine natürliche Ordnung besitzen, kann man sie sinnvoll mit einander vergleichen. Überträgt man die Zahlen-Werte auf die Java-Klassen kann man nun die Java-Klassen miteinander vergleichen.
Wenn die Metrik zum Beispiel „Anzahl der Methoden“ heißt, kann man nun sagen: ist die Zahl der Methoden in Klasse A größer als die der Methoden in Klasse B, dann kann man — analog dazu — sagen: Klasse A ist größer als Klasse B. Statt „größer“ kann man auch ein anderes quantifizierbares Attibut verwenden: besser, schlechter, schöner, häßlicher,…
Aber genau mit diesem letzten Schritt haben wir den Definitions-Bereich der Metrik verlassen. Die Metrik ist eben nichts weiter als die Funktion und wenn man so will die Ordnung die sie definiert; sie hat nichts mit ihrer Interpretation zu tun. Wenn man daher sagt: Klasse A ist besser als Klasse B weil für Metrik x gilt: x(A) > x(B) dann „interpretieren wir sie als Erfüllungsgrad einer Qualitätseigenschaft“ um mit der IEEE-Definition zu sprechen. Das gleiche gilt, wenn verlangt wird daß ein bestimmter Schwellwert über- oder unterboten werden soll.
Die Aussage
Die Klasse darf maximal 10 Methoden besitzen
verwendet die Methoden-Zahl-Metrik und fügt einen Schwellwert hinzu. Dadurch wird sie zum Güte- oder Qualitäts-Kriterium. Sie verwendet die Metrik, ist aber nicht identisch mit ihr. Das ist eine Fehlinterpretiation, die ich erstaunlich oft gehört habe.
Eine Metrik ist also zunächstmal ein Meßinstrument. Bevor man es anwenden kann, muß man sich überlegen was man mit den Ergebnissen anfängt wenn man sie hat. Und mehr noch: man muß sich erstmal darüber klar werden, ob man die Ergebnisse tatsächlich haben will. Klassen sollen in der Regel möglichst wenig Methoden haben, Methoden sollen möglichst kurz sein. Kurze Methoden heißt aber auch mehr Methoden. Sollte dann statt der Methoden-Zahl nicht lieber die durchschnittliche Methodenlänge gemessen werden?
Metriken sind — richtig verstanden — ein hilfreiches Werkzeug, aber:
A fool with a tool is still a fool
Diese Metrik ist tatsächlich zugleich ein selbsterfüllendes Qualitäts-Merkmal.
Merkmale für Code-Qualität
Nachdem wir die zu erfüllenden Anforderungen kennen, müssen wir nun Merkmale finden in denen sich die Anforderungen widerspiegeln. Merkmale, die es erlauben den Grad zu messen in dem die Anforderungen erfüllt sind. Es giebt eine unglaubliche Menge von Vorschlägen für Eigenschaften, Merkmalen oder Qualitäten die Code haben sollte oder nicht haben dürfen soll um als „guter Code“ zu gelten. Man kann diese Merkmale auch — zumindest teilweise — den genannten Anforderungen zuordnen.
Und an dieser Stelle fangen die Probleme an. Die Menge der Regeln ist riesig und die der diskutierten Auslegungen unüberschaubar. Tatsächlich ist die Vorstellung von Code-Qualität nicht absolut. Sie ist abhängig von Zeit, Kultur, verwendeter Technologie, Fachlichkeit und vielen anderen Faktoren. Die wenigen allgemeingültigen Regeln sind dermaßen abstrakt formuliert, daß sie kaum unmittelbar anwendbar sind. Und sobald man versucht ihre Anwendung zu konkretisieren, starten die Diskussionen.
Jedes Team und jede Organisation, die versucht die Code-Qualität zu verbessern muß damit beginnen ein gemeinsames Verständnis für die Qualität zu entwickeln. Ohne ein solches Verständnis findet man keine Einigung und Regeln werden entweder gar nicht erfüllt oder sie werden nur um der Erfüllung willen erfüllt. Gesetzte Regeln müssen verstanden und immer wieder auf ihre Sinnhaftigkeit hinterfragt werden. Das ist aufwändig und erfordert Engagement — wer ist bereit dafür Energei aufzubringen?
In den alten Tagen war es üblich, Code-Qualität am Verhältnis von Kommentar- zu Code-Zeilen zu messen. Je größer das Verhältnis desto besser. In der Folge sind viele Entwickler dazu übergegangen, den Code mit leeren Kommentar-Zeilen aufzufüllen. Die Metrik wird erfüllt, aber was hat das mit Qualität zu tun?
Metriken führen nicht zu Code von besserer Qualität,
sondern zu Code der die Metriken erfüllt.
Moderne Metriken scheinen besser geeignet zu sein. Man zählt die Methoden der Klasse, die Zeilen von Methoden und Funktionen, man mißt die Abdeckung des Codes mit Unit-Tests und die Kohärenz in Modulen. Aber was ändert das an der Einstellung des Entwicklers? Wer schnell viel Code generieren will (oder muß), der findet Wege die Metriken zu erfüllen ohne dabei die Code-Qualität ändern zu müssen.
Qualität ist die Einstellung des Entwicklers,
die vom Code reflektiert wird.
Macht das Regeln — und die Metriken die sie messen — überflüssig oder unsinnig? Nein. Der qualitätsorientierte Entwickler sollte alle Mittel nutzen — und zumindest ernsthaft erproben — um die Qualität zu verbessern. Die vielleicht beste Formulierung diese Gedankens stammt von Michael Feathers:
Clean Code always looks
Michael Feathers
like it was written by someone who cares.
Die Kommentar-Metrik zeigt aber noch ein anderes Problem. Keine Metrik kann messen wie hoch der Bezug ist den der Code zu seiner Semantik hat. Man könnte diese Eigenschaft als „Angemessenheit“ bezeichnen. Betrachten wir diese Zeile:
int x = a + b;
Sie ist zu umfangarm um irgendeine Aussage über ihre Qualität zu machen. Sobald wir aber die Variablen umbenennen und schreiben:
int differenz = soll + ist;
macht die Zeile überhaupt keinen Sinn mehr. Sie widerspricht sich plötzlich selbst. Die zusätzliche semantische Information die durch die Bezeichner transportiert wird ist für den Compiler schlichtweg unsichtbar. Ebenso wie die Information die gar nicht im Code enthalten ist, weil sie nur im Fachkonzept existiert.
Was können wir also über die meßbaren Merkmale von Code-Qualität sagen? Sobald wir festgelegt haben, welchen Regeln wir folgen möchten, können wir dazu Merkmale festlegen an denen wir die Code-Qualität messen können. Sobald sich unser Verständnis der Qualität ändert müssen wir die Merkmale anpassen. Ausreichend sind solche Merkmale aber nicht, die Übereinstimmung des Codes mit seiner Bedeutung können sie nicht erfassen.
Anforderungen an die Code-Qualität
Das Erste das wir brauchen sind Anforderungen. Ohne Anforderungen ist die Diskussion über Qualität sinnlos. Für funktionale Anforderungen ist das einfach: Wenn das Programm nicht richtig rechnet, ist es nutzlos — die Qualität ist dann unrettbar verloren. Man kann zwar darüber diskutiereen ob die eine oder andere funktionale Anforderung fallen gelassen werden kann, aber das ist ein Problem der Diplomatie, nicht der Technik.
Für die meisten nicht-funktionalen Anforderungen gilt das auch. Wenn es um technische Anforderungen geht — wie zum Beispiel die Reaktionszeit — lassen sich in der Regel Anforderungen formulieren, die die Merkmale für ihre Erfüllung gleich mitbringen. Am Beispiel der Reaktionszeit war das zu erkennen. Schwieriger sind Forderungen nach der Bedienbarkeit, da muß man bisweilen die humanistischen Wissenschaften bemühen um etwa festzulegen was „leichte“ oder „intuitive“ Bedienbarkeit bedeutet.
Was aber müssen wir (an-)fordern, wenn es um Code-Qualität geht? Beginnen wir am menschlichen Ende der Pipeline: Wer hat denn überhaupt ein Interesse an Code-Qualität? Für die Endanwender von Software ist entscheiden, daß die Software macht was sie soll,. Sie soll ihre Funktion erfüllen, und das schnell und unkompliziert — wie sie das tut ist dem Anwender egal; die meisten Anwender würden es auch gar nicht verstehen (keine Kritik: das ist ja auch nicht ihr Job). Die Auftraggeber — also üblicherweise diejenigen die die Herstellung der Software bezahlen — haben ein Interesse an zufriedenen Anwendern und an niedrigen Kosten. Wie die Anwender sind sie fachlich motiviert und interssieren sich ebenfalls nicht für die Technik. Und selbst die Techniker vom Betrieb haben kein ursächliches Interesse am Code. Sie benötigen eine saubere Dokumentation, aussagekräftige Log-Ausgaben und angemessene Konfigurations-Möglichkeiten.
Wer interessiert sich nun aber für den Code? Es sind die Entwickler. Leider sind es nicht immer diejenigen die den Code schreiben; aber immer diejenigen die die Anwendung warten. Sie müssen Fehler finden und fixen, sie müssen Anpassungen und Erweiterungen durchführen, sie müssen Portierungen und Updates duchführen. Sie sind die Nutznießer guter Code-Qualität, sie haben ein Interess daran — zumindest sollten sie es haben.
Damit haben wir schu schon die wichtigsten Anforderungen, die die Code-Qualität bestimmen. Sie können unterschiedlich gewichtet sein, es können andere Anforderungen hinzukommen, aber immer werden diese drei Anforderungen die entscheidende Rolle spielen:
- Wartbarkeit
- Änderbarkeit
- Erweiterbarkeit
Sie sind von Natur aus nicht-funktional, denn ob der Code wartbar oder änderbar ist oder nicht, ist völlig unabhängig davon ob er seine Funktion erfüllt oder nicht. Um kein Mißverständnis aufkommen zu lassen: Auch wenn die Code-Qualität für die Erfüllung dieser Anforderungen von entscheidender Bedeutung ist, giebt es weitere Faktoren die sie beeinflussen — allen voran die Architektur. Aber hier geht es erstmal nur um die Code-Qualität.
Code-Qualität, Vorbemerkung
Bevor man über einen Begriff diskutiert, muß man festlegen, was man damit eigentlich meint. Wir können erstmal beim landläufigen Verständnis dafür bleiben, was „Code“ ist, aber was ist eigentlich „Qualität“?
Der Bedeutung des Wortes nach bezeichent „Qualität“ zunächst „die Beschaffenkeit von irgendwas“. Meint also irgendeine Eigenart des betrachteten Objekts, wie die Farbe oder die Größe. Dem Wesen nach bedeutet (eine) Qualität also (eine) Eigenart die irgendwie ausgeprägt sein kann. Eine allgemeine oder absolute Bedeutung hat der Begriff zunächst nicht — es macht also keinen Sinn von der Qualität einer Sache zu sprechen. Auch ist noch keine Aussage darüber gemacht in welcher Weise oder Menge das Objekt die Eigenschaft erfüllt. Und auch nicht, ob dieser Zustandgut ist oder schlecht. Oder so gefragt: Hat das Objekt mehr oder weniger? Ist mehr besser oder schlechter?
Um mit dem Begriff „Qualität“ etwas sinnvolles bezeichnen zu könnne, definiert ihn die ISO so:
Grad, in dem ein Satz inhärenter Merkmale eines Objekts Anforderungen erfüllt.
Es geht darum, daß das Objekt Anforderungen erfüllen muß; gemessen an einer definierten Menge von Eigenschaften die dem Objekt zu eigen sind (also nicht zugesprochen werden). Anhand der Merkmale soll bestimmt werden, inwieweit die Anforderungen erfüllt sind; die Merkmale müssen also meßbar sein, einen Bezug zu den Anforderungen haben und Aufschluß darüber geben, ob die Anforderungen erfüllt sind.
Zu abstrakt? Ein Computer-Programm wird nicht verwendet, wenn die Berechnung zu lange dauert. Man verlangt also als Anforderung: 5 ms nach Eingabe muß das Ergebnis am Bildschirm erscheinen. Das Merkmal ist die Verarbeitungsgeschwindigkeit (daß diese vom verwendeten Computer abhängt übersehen wir hier mal). Der Grad der Erfüllung ist die Differenz zwischen den 5 ms und der tatsächlichen Ausführungszeit.
Um die Qualität bestimmen zu können benötigen wir also:
- Anforderungen
- Merkmale die die Erfüllung dieser Anforderungen widerspiegeln
- Einen Weg diese Merkmale am Objekt zu messen
- Schwellwerte, die die Erfüllung der Anforderung signalisieren
Um von der Qualität eines Objekts sprechen zu können, benötigen wir also immer einen Kontext. Es muß klar sein, welche Anforderungen vom Objekt erfüllt werden sollen. Die Anforderungen und die Mekmale, die als Indikator für deren Erfüllung dienen, müssen feststehen; dann können wir darauf verzichten sie jedesmal aufzuzählen und sprechen von der Qualität des Objekts.