Experten-Check: Was ist schöner Code?
Schöner Coden - Teil 2

Experten-Check: Was ist „schöner Code“ für Software-Tester?

Redaktion JAXenter

© shutterstock.com / patpitchaya

In unserem Themen-Schwerpunkt “Schöner Coden” wollen wir uns der Frage stellen: Was ist eigentlich schöner Code? Kann man die „Schönheit“ von Programmcode auf eine bestimmte Eigenschaft reduzieren? Hilft ein bestimmtes Tool oder Framework dabei, schöneren Code zu schreiben? Im zweiten Experten-Check beantworten uns die Testing-Experten Moritz Beller, Michael Thiele, Mario-Leander Reimer und Kay Grebenstein diese Fragen.

Die Testing-Experten

moritzbeller

Moritz Beller arbeitet an seinem PhD über TestRoots in der Software Engineering Research Group der Delft University of Technology in den Niederlanden.

thiele_michael

Michael Thiele arbeitet seit April 2012 für die Saxonia Systems AG und entwickelt mit JavaFX interaktive Applikationen für Touchscreens. Seine Interessen liegen eigentlich im Backend-Bereich und der Programmiersprache Scala; durch JavaFX hat er den Spaß an GUI-Entwicklung wiederentdeckt.

reimer_mario-leander

Mario-Leander Reimer ist seit Anfang 2014 Cheftechnologe bei QAware. Er verantwortet den technischen Erfolg von Projekten im Bereich Aftersales beim Kunden BMW. Er ist Spezialist für den Entwurf und die Umsetzung von komplexen System- und Softwarearchitekturen auf Basis von Open-Source-Technologien. Als Mitglied im Java Community Process (JCP) ist sein Ziel, die Java-Plattform weiter zu verbessern und praxistaugliche Spezifikationen zu entwickeln. Er ist Speaker auf inter­nationalen Konferenzen und Gastdozent an der Hochschule Rosenheim. Mario-Leander Reimer hat in Rosenheim und an der Staffordshire University Informatik studiert und ist seit 2009 Mitarbeiter der QAware.

kay20grebenstein_0

Kay Grebenstein arbeitet als Testmanager und Testcoach für die Saxiona Systems AG, Dresden. Er hat in den letzten Jahren in Projekten unterschiedlicher fachlicher Domänen (Telekommunikation, Industrie, Versandhandel, Energie …) Qualität gesichert und Software getestet. Dabei konnte er seine Kenntnisse sowohl im klassischen als auch im agilen Umfeld erweitern.

Was hilft dir (technisch) beim schöner Coden? Welche Tools/Frameworks machen Code schön?

Moritz Beller: Schönheit von Code hat mehrere Unteraspekte. Und idealerweise sind alle erfüllt, damit er zum wirklichen Kunstwerk wird. Es gibt zum einen klare Lesbarkeit und Verständlichkeit. Wenn das nicht gegeben ist, brauchen wir nicht mehr über Schönheit zu diskutieren. Klar, in manchen Programmiersprachen, Haskell zum Beispiel, verwendet man noch Variablennamen wie p und t, inspiriert von der Mathematik. Das hat auch eine gewisse, nicht abstreitbare Schönheit. Aber sobald nicht mehr eindeutig ist, dass t z. B. für testcase steht – und das ist es in den meisten Fällen –, verliert dies schnell an Eleganz, weil es einfach unpraktisch ist. Zudem kommt Prägnanz hinzu: Ist der Code so kurz wie möglich, und so lang wie nötig? Habe ich in meinen Tests Clone, die ich im Falle von Testcode in ein Fixture auslagern könnte? Dann ist diese Eigenschaft verletzt. Zu guter Letzt: Ist der Code performant (genug)? Code zu micro-optimieren kann unglaublich Spaß machen, ist aber oft unnötig. Der schönste Code in der Praxis nützt mir jedoch nichts, wenn die Rekursion prohibitiv langsam läuft.

Ein letzter Punkt ist – und jetzt wird es philosophisch – die Ästhetik des Codes, und die wird durch die Programmiersprache und die Konventionen in deren Ökosystem (Big Endian vs. Little Endian, Spaces vs. Tabs) vorgegeben. Ich denke im Allgemeinen, dass es kein Tool oder Framework gibt, das Code per se schön macht. Das ist so ähnlich wie ein guter Fotograf mit jeder Kamera ein gutes Foto machen kann. Es gibt sicherlich Frameworks, die es leicht machen, mit ihnen zumindest architektonisch schönen Code zu produzieren, da sie eine gewisse Struktur vorgeben. Aber die Erfahrung zeigt, dass man in jeder Sprache und mit jedem Framework ohne weiteres Kauderwelsch produzieren kann. In puncto Ästhetik sind DSLs mit sehr begrenzter Syntax oft nicht zu übertreffen. Für General Purpose Languages gibt es natürlich viele Meinungen, und kein „richtig“ oder „falsch“, die es übrigens bei den beiden vorgenannten Aspekten Lesbarkeit, Prägnanz und Performanz meist schon gibt. So wie Manchen Bilder von Monet gefallen, gefällt anderen eben Picasso. Wobei ich jetzt nicht verrate, welche Sprache ich mit Picasso vergleichen würde ;-). Ich denke, dass bezogen auf Software wohl ein Bauhaus-ähnlicher Schönheitsansatz nach dem Motto „Form follows function“ angemessen ist.

Es gibt kein Tool oder Framework, das Code per se schön macht.

Zur Gesamtästhetik – und das ist eher eine Architekturfrage – muss das System stimmig zusammen passen, so ähnlich wie ein Bild ja aus einzelnen Bereichen besteht, die sich harmonisch in ein ganzes einfügen sollten. Übrigens ist Test-Code oft recht generisch und ähnelt somit eher einer DSL. Es sei denn, man hat ein richtig kompliziertes Test-Setup. Insofern passiert es selten, dass ich Test-Code sehe oder schreibe, der mich ob seiner Schönheit vom Hocker haut. Oft gibt es ziemlich genau einen sinnvollen Weg, Dinge zu testen, und dann weiß ich nicht, ob man da gleich über Schönheit sprechen muss.

Nun denken vielleicht einige: „Was soll das, wir sind Programmierer und keine Künstler, uns interessieren rein technische Fragen!“ Intuitiv stimme ich zu. Dann schaue ich mir aber Dinge wie den „Rockstar-Developer-Kult“ in unserer Community an, und dass Style-Posts wie etwa „Semikolon am Ende oder kein Semikolon am Ende“ oder „Spaces oder Tabs“ mit Abstand die am meisten diskutierten Themen auf Plattformen wie Hacker News und Reddit sind – und sehe schon Parallelen.

Michael Thiele: Den Hauptteil macht Erfahrung aus – meine eigene und die meiner Kollegen, die mir im Code Review oder durch Pair Programming Feedback zu meinem Code geben oder ich zu ihrem. Technisch hilft vor allem eine gute IDE, die man im Schlaf beherrscht und die umfangreiche Refactorings anbietet.

Mario-Leander Reimer: Für schönen Code braucht es für mich zwei Zutaten: das passende Handwerkszeug und eine Portion Handwerksstolz. In der täglichen Arbeit ist das wichtigste Tool für mich die IDE. Hier bevorzuge ich seit langem IntelliJ, es hat für mein Empfinden den besten Refactoring Support und eine super Tool-Integration verglichen mit anderen Entwicklungsumgebungen. Beides finde ich wichtig, ich kann mich so voll auf den Code konzentrieren. Gängige Refactorings wie Extract Method, Extract Constant, Pull Members Up oder Find and Replace Code Duplicates werden zum Kinderspiel. Sie lassen sich per Shortcut sehr einfach und schnell anwenden und stören so den Flow beim Programmieren nicht.

Hilfreiche Frameworks für schönen und ausdrucksstarken Code gibt es sicher viele, das ist stark abhängig vom aktuellen Kontext. Meine generelle Devise lautet: Schreibe nichts selbst, was andere schon gut gelöst haben. Hier greife ich lieber auf eine der zahlreichen Open-Source-Bibliotheken zurück, wie etwa die ewigen Klassiker von Apache Commons et. al. Schöner Coden ist für mich aber auch eine Sache der Einstellung. Es liegt an einem selbst, das Optimum aus seinem Code herauszuholen, ihn zu schleifen und zu polieren, so lange bis alles passt und glänzt.

Kay Grebenstein: Ich bin von Haus aus Tester, und für die meisten Tester ist schöner Code nicht wirklich wichtig. Sie überprüfen die Software im Rahmen von sogenannten Blackbox-Tests. Im Black-Box-Test wird ohne Kenntnisse über die innere Funktionsweise des zu prüfenden Systems getestet. Für die Ermittlung der Testfälle werden nur die Anforderungen herangezogen, und nach der Durchführung der Testfälle ergibt sich eine Aussage über die Qualität der Software. Für einen klassischen Tester ist die Software „schön“ bzw. hat ausreichend Qualität, wenn sie funktioniert.

Verantwortlich für gute Software und gute Prozesse sind alle Beteiligte.

Besonders in einem agilen Entwicklungsteam ist das anders. Hier arbeiten alle Beteiligten im Entwicklungsprozess eng zusammen. Das Team ist interdisziplinär aufgebaut und besteht aus Testern, Entwicklern und Designern. Die Verantwortung für die Qualität (und Qualitätssicherung) liegt dort, wo sie schon immer liegen sollte: Denn verantwortlich für gute Software und gute Prozesse sind alle Beteiligte. Jeder trägt aus seinem Blickwinkel und seiner Rolle einen Teil zur Qualität und Qualitätssicherung bei.

Für Tester gibt es mit dem ISTQB Certified Tester eine anerkannte Schulung und Zertifizierung, die eine gemeinsame Grundlage der Begriffe und des Vorgehens standardisiert. Damit haben Tester ein festes Mindset dazu, was Testen bedeutet. Da der Schwerpunkt der Softwareentwicklung nicht bei Werkzeugen liegt, sondern immer noch bei den Entwicklern, wünsche ich mir, dass jedem Entwickler von Beginn seiner Ausbildung an grundlegende Vorgehen zur Erstellung von schönem Code eingepflanzt werden: Allgemeingültige und feststehende Regeln, die den Arbeitsprozess und die Codequalität ständig verbessern. Aber das Lernen hört nach der Ausbildung nicht auf: Das Wissen teilen die Entwickler in CodingDojos. Hier trainieren und üben die Entwickler an Beispielaufgaben und festigen das richtige Vorgehen oder lernen neue Fertigkeiten.

Bei welchem Fehler rollen sich bei dir die Fußnägel hoch? Was ist schlechter Code?

Moritz BellerWir hatten neulich eine Diskussion über den Unterschied zwischen zwei statistischen Effektgrößen (Effect Sizes), der bekannten Cliff Delta und der weniger bekannten Vargha-Delaney A12, die beschreiben, wie sehr eine Verteilung von einer anderen abweicht. Laut StackExchange besteht angeblich ein linearer Zusammenhang. Um zu verdeutlichen, dass dies eben nicht so ist und ein Unterschied besteht, wurde mir folgender Test-Code geschickt (in R, ästhetisch, meiner Meinung nach grundsätzlich schon schwierig):

# data distributions 
a<-c(490,498,475,499,531,577,495,503,475,496)
b<-c(501,606,583,600,687,544,557,602,589,630)

#wilcoxon test
wilcox.test(a,b)

#A_12 statistics
a12 <- VD.A(b,a)
print(paste("A12 is ", a12$estimate))

#Cliff's effect size
cliff <- as.numeric(orddom(a,b,paired=FALSE)[13])
print(paste("Cliff's delta is ", cliff))

#convert cliffs to A_12 using linear dependencies
cliff2a_12 <- cliff * 2 - 1 

Hier gibt’s grobe Handwerksfehler, und wie wir sehen werden, kommen wir, indem wir diese systematisch verbessern, zu einem ganz anderen Verständnis des Code und der unterliegenden Fragestellung:

1. Die Kommentare beginnen mal mit Großbuchstaben, mal ohne, mal mit Blank und mal ohne. Ein Kommentar wilcoxon test über der Funktion wilcox.text trägt nichts zum Verständnis bei. Besser wäre da: Test for non-normality to assert we can use non-parametric effect size measures

2. Die in R verwendeten Libraries sind nicht inkludiert. Das wird zu einem Problem führen, denn die Funktion orddom ist in zwei Paketen definiert.

3. Die Relation zwischen Cliff und A12 ist genau falsch herum aufgelöst, und dies ist u. a. ein Resultat aus der schlechten Benennung der Variable als cliff2a_12. Korrekt soll nämlich getestet werden, ob A12 == Cliff und mit einer Variable cliff.converted.to.a12 ist klar, wie herum die Gleichung aufgelöst sein muss: cliff.converted.to.a12 <- (cliff + 1)/2

Darum geht es letztendlich bei gutem Code: Ich verstehe, was passiert.

Die Behauptung bei dem gesandten Code war, dass er zeigen würde, dass die zwei Tests eben gerade nicht identisch sind (a12 != cliff.converted.to.a12), wozu ein Beispiel reicht. Nach Korrektur des Codes erhielt ich jedoch das Ergebnis a12 == cliff.converted.to.a12. Da ein Beispiel nichts zeigt, fügte ich eine Schleife hinzu, die 1000 Zufallsdistributionen erzeugt und die Effect Sizes vergleicht. Et voilà!

[1] „Round 26 and the universe just broke! VDA.normal: 0.589844 VDA.cliff 0.583984“

Dies trat jedoch nur sehr selten auf und oft in einem Bereich, wo ich mir unsicher war, ob es eventuell mehr mit Rundungsfehlern zu tun haben könnte, als mit den Eigenschaften der zugrundeliegenden Verteilungen. Ich konsultierte also den Autor des Codes, und wie sich herausstelle, nutzte er eine andere Library für das Berechnen von Cliffs Delta als ich. Nun gab es einen Implementierungsunterschied zwischen beiden Libraries: Während eine asymmetrische Konfidenzintervalle per Default nutzt, tut dies die andere nicht. Und für asymmetrische Konfidenzintervalle und Distributionen, die zu ihnen führen, gilt dann auch A12 != Cliffs DeltaAll dies wäre leicht zu vermeiden gewesen, indem wir dem Code library(effsize) hinzufügen.

Aber dann wäre uns dieser fiese Unterschied in der Implementierung der beiden Libraries vielleicht gar nicht aufgefallen … Insofern hat schlechter Code manchmal auch gute Seiten, weil er dazu anregt, nachzudenken und es besser zu machen.

Michael Thiele: Meine absoluten Lieblinge sind Kommentare, die beschreiben, WAS passiert, aber nicht das WARUM. Das „Was“ sehe ich (hoffentlich) im Code. Zudem kann auch sehr cleverer Code ein Problem sein. Sprich, jemand hat eine auf den ersten Blick elegante Lösung gefunden, aber man braucht zu lange, um die „Magie“ zu verstehen. Dann lieber ein paar Zeilen mehr und die „dumme“ Lösung.

Und zu guter Letzt gibt es noch den Code, der untestbar ist – das ist auch immer ein Zeichen davon, dass ich ihn als Mensch schlecht verstehen kann. Und darum geht es letztendlich bei gutem Code: Ich verstehe, was passiert.

Bei lieblos geschriebenem Code geht mir definitiv die Hutschnur hoch.

Mario-Leander Reimer: Bei lieblos dahingeschriebenem Code, bei dem noch nicht einmal die einfachsten Regeln für saubere Programmierung eingehalten wurden, geht mir definitiv die Hutschnur hoch. In einem Sanierungsprojekt ist mir mal ein riesiges Methoden-Monster über den Weg gelaufen. Die wesentlichen Kennzahlen:

  • 1 Methode,
  • 294 Zeilen lang,
  • 93 Conditionals, davon etliche nicht erreichbar,
  • 7 als maximale Schachtelungstiefe mit einer Mischung aus if- und for-Blöcken,
  • keine Kommentare, weder auf Methodenebene noch inline im Code
  • massive Duplikate

Wo war der Entwicklerstolz als dieser Code geschrieben wurde? Und obendrein gab es für diese Methode keinen einzigen Test. Eigentlich hätte man diesen Code nicht anfassen dürfen! Wir haben es dennoch getan. Das Reverse Engineering, Testen und anschließende Refactoring hat aber mehrere Tage in Anspruch genommen.

Leider trifft man schlechten Code sehr häufig bei Tests an, diese werden allzu oft als Code zweiter Klasse behandelt. Keine Formatierung, viel Copy-und-Paste, kompliziertes Setup, ellenlange Testmethoden mit sinnfreien Asserts. Das ist mehr als unschön. Bei großen Refactorings werden die nötigen Änderungen am Test-Code schnell zur Qual. Oft fährt man besser, die Tests zu löschen und von Grund auf neu zu schreiben.

Ein weiteres Fundstück aus einem Sanierungsprojekt ist der folgende Test. Man weiß nicht genau, ob man lachen oder weinen soll. Bevor man solche Tests schreibt, sollte man lieber gar keine Tests schreiben.


public class OneTest {

    @Test

    public void test() {

        assertTrue(true);

    }

}

Wenn du eine Eigenschaft nennen müsstest, die für dich schönen Code ausmacht, welche wäre das und warum?

Moritz BellerIm Bezug auf Test-Code ist das oft ein bisschen schwierig, aber wenn man beispielsweise die volle Bandbreite der Möglichkeiten nutzt, die JUnit einem bietet, finde ich das zumindest ganz nett und elegant. In unserem Open-Source-Projekt WatchDog, ein Multi-Platform-Plug-in zum Monitoring und zur Instrumentierung von Eclipse- und IntelliJ-basierenden IDEs, benötigen wir eine bestimmte Reihenfolge, in der Test-Cases ausgeführt werden. Per Default gibt JUnit keine Reihenfolgengarantie. Wir schrieben uns ursprünglich also unseren eigenen JUnitExecuter, der TestCases lexikographisch sortierte. Nicht mega-kompliziert, aber doch ein bisschen Code:

public class LexicographicalTestOrderRunner extends BlockJUnit4ClassRunner {

    public LexicographicalTestOrderRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected List<FrameworkMethod> getChildren() {
        List<FrameworkMethod> children = new ArrayList<FrameworkMethod>(super.getChildren());
        Collections.sort(children, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod o1, FrameworkMethod o2) {
            }
        });

        return children;
    }
}

Dann allerdings erschien mit JUnit 4.11 ein Feature, das die gesamte Sortierungsklasse redundant machte und nur noch eine Zeile benötigt:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

Der schönste Code ist für mich immer noch der, den ich nicht selbst schreiben und warten muss :).

Schöner Code sollte sich wie ein (Koch-)Rezept lesen lassen.

Michael Thiele: Schöner Code sollte sich wie ein Buch oder (Koch-)Rezept lesen lassen. Ich möchte in jeder Abstraktionsebene nur mit den dort herrschenden Abstraktionen konfrontiert werden, d. h. in den obersten Schichten sollte nur die Fachlichkeit der Domäne auftauchen. Technische Aspekte und auch Workarounds sollen so gut es geht gekapselt sein und erst auf tieferen Ebenen in Erscheinung treten.

Mario-Leander Reimer: Das auf genau eine Eigenschaft zu reduzieren, fällt mir schwer. Zusammengefasst würde ich sagen: Schlichte Eleganz macht für mich schönen Code aus. Erst wenn man nichts mehr weglassen kann, ist der Code fertig, er ist klar strukturiert, ausdrucksstark und verständlich.

Kay Grebenstein: Da sich die Softwarequalität als abstrakter Begriff  selbst nicht quantifizieren lässt, wurden Qualitätsmodelle wie die ISO 25010 geschaffen, die anhand festgelegter Kriterien eine Einordnung ermöglichen. Der Faktor „schöner Code“ steckt nach ISO 25010 im Kriterium Wartbarkeit. Wartbarkeit ist die Einfachheit, mit der ein Softwaresystem oder eine Komponente modifiziert werden kann, um Fehler zu beheben, Performanz oder andere Attribute zu verbessern oder Anpassungen an die veränderte Umgebung vorzunehmen (Christop Bommer: Softwarewartung. Grundlagen, Management und Wartungstechniken). Oder anders ausgedrückt: Je besser und schneller eine Software angepasst werden kann, desto wirtschaftlicher ist es.

Software ist nicht gleich Software und ein Vergleich gestaltet sich schwierig. Zum einen gibt es da die organisatorischen Unterschiede, die sich aus der Tatsache ergeben, dass ein Projekt per Definition ein einmaliger Prozess ist. Aufgrund dessen sind die Anforderungen an den Programmcode als auch deren Priorisierung ebenfalls von Projekt zu Projekt unterschiedlich. Zum anderen gibt es verschiedene Programmiersprachen, die je nach Struktur sich unterschiedlich darstellen lassen.

Am Ende ist die einzige wirklich wichtige Aussage zum Thema „schöner Code(n)“ der einzig wahre Grund, warum man es tun sollte.

Aus diesem Grund gibt es Kennzahlen (Harry M. Sneed: Software in Zahlen: Die Vermessung von Applikationen), die dem Zweck dienen, die Transparenz innerhalb des Softwareentwicklungsprozesses zu steigern, als auch die Qualität des Quellcodes zu bestimmen. Um diesen Zweck zu erfüllen, ist es unumgänglich, nicht nur Aussagen bezüglich der Codequalität, sondern auch hinsichtlich Komplexität und Quantität zu treffen. So erhält man ein vollständiges Verständnis über den Programmcode und kann eine Aussage über seine Schönheit treffen.

Aber am Ende ist die einzige wirklich wichtige Aussage zum Thema „schöner Code(n)“ der einzig wahre Grund, warum man es tun sollte. Es sind nicht Wirtschaftlichkeit oder Kennzahlen. Die einzige Kennzahl, die aus meiner Erfahrung in Projekten wichtig war, ist „WTF“. Wir sollten schöneren Code schreiben, weil es uns hilft, den Code besser zu verstehen, schneller Anpassungen vorzunehmen und Fehler zu vermeiden. Kurz gesagt: Schöneres Coden macht den Entwickler, den Tester und das ganze Team glücklich und vermeidet WTF-Momente.

shutterstock_107815670Schöner Coden
In unserer Reihe zum Thema „Schöner Coden“ stellen wir Experten verschiedener Disziplinen die Frage: Was ist eigentlich schöner Code? Welche Eigenschaft hat schöner Code aus der Sicht eines Software-Architekten? Und wie blicken Tester auf dieses Thema? Hier finden Sie eine Übersicht der bisher veröffentlichten Experten-Checks:

Geschrieben von
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.