Codequalität lehren und lernen: Erfahrungen aus 6 Jahren Programmierausbildung

Wir empfehlen Maßnahmen, um vor dem Berufseinstieg für eine bessere Ausbildung zu sorgen und eine sinnvolle Weiterbildung zu etablieren.

Lesezeit: 7 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 586 Beiträge

(Bild: Black Jack/Shutterstock.com)

Von

Der Anspruch an Code sollte nicht nur sein, dass er seinen Zweck erfüllt. Qualitativ hochwertiger Code (sog. "Clean Code") ist nicht nur funktional korrekt, sondern auch gut lesbar, einfach testbar und leicht zu erweitern. Leider haben Berufseinsteiger*innen oftmals keine ausreichende Erfahrung oder das Bewusstsein dafür. Der Fokus in den Programmierkursen liegt meistens auf der Funktionalität, die üblicherweise die einzige Bewertungsgrundlage darstellt. Aus Zeitgründen gibt es nicht ausreichend Feedback für weitere Qualitätsaspekte, was Lernenden den Eindruck vermittelt, dass Codequalität zweitrangig sei. Wenn Absolvent*innen das nicht woanders erlernen, ist der Einstieg in den Beruf schwierig. Unternehmen sind dann gezwungen, in einem Thema in Weiterbildung zu investieren, von dem sie normalerweise erwarten würden, dass ihre Softwareentwickler*innen das schon können.

Young Professionals schreiben für Young Professionals

Dieser Beitrag ist Teil einer Artikelstrecke, zu der heise Developer explizit junge Entwickler*innen eingeladen hat – mit dem Ziel, ihresgleichen, aber natürlich auch die interessierten "älteren Semester" über aktuelle Trends, Entwicklungen, Phänomene und persönliche Erfahrungen zu informieren. Die Beiträge dieser Artikelstrecke erscheinen im monatlichen Turnus. Du bist selbst ein "Young Professional" und willst Teil dieser Serie sein? Dann bewirb dich mit einem Vorschlag bei der Redaktion. Sie steht dir mit hilfreichen Tipps über den Schreib-, Redigier- und Freigabeprozess hinweg zur Seite.

2011 hat die Universität Bamberg die Programmierausbildung überdacht. Auf der Erstsemestervorlesung "Einführung in der Informatik" aufbauend, sollten zwei weiterführende, praktische Programmierkurse geschaffen werden. Als junge Doktoranden hatten die Autoren dieses Artikels die Chance, ein neues Lehrkonzept [1] zu entwickeln, das den Studierenden nicht nur weiterführende Programmierkonzepte beibrachte, sondern den Fokus auf das Schreiben qualitativ hochwertigen Codes legte.

Das didaktische Konzept basierte auf Codereviews.

Das Lehrkonzept besteht aus einer Plenarübung im Rechnerraum, die zwischen dem Vermitteln neuer Programmierkonzepte und deren sofortiger Anwendung in kleinen Übungsbeispielen alteriert. Die Lösungsvorschläge werden im Plenum interaktiv begutachtet und so lange unter Moderation der Dozenten refaktoriert, bis es keine Verbesserungsvorschläge mehr gibt und die Vor- und Nachteile aller Alternativen abgewogen sind. Die Diskussionen sind essentiell, um den Studierenden nicht nur ein Gespür für Verbesserungen zu geben, sondern auch um die Erwartungshaltung in Bezug auf Codequalität in den Programmieraufgaben zu setzen.

Der zweite Pfeiler der Lehrveranstaltungen besteht aus mehreren Programmierprojekten, die innerhalb von zwei Wochen in Kleingruppen zu lösen sind. Die Dozenten bewerten die Projekte, die circa fünf bis 15 Java-Klassen mit bis zu 2000 Codezeilen umfassen, schriftlich hinsichtlich funktionaler Korrektheit, aber auch Architektur und Codequalität. Dabei teilen sie nicht nur die erreichte Punktzahl mit, sondern gehen wie in einem Codereview detailliert auf die Codequalität und Verbesserungsmöglichkeiten mit Verweis auf die Codestelle ein. Neben dem individuellen Feedback gibt es jeweils eine Sammlung gehäuft auftretender Fehler, die mitsamt ihrer Verbesserung an alle im Kurs verschickt werden. Aus der Sammlung der typischen Fehler entstand mit der Zeit das Buch "Java by Comparison" [2].

Die mündliche Prüfung am Ende des Semesters fragt schließlich den erlernten Stoff anhand des Codes aus den Projekten ab. Die Studierenden sind aufgefordert, in der IDE den eigenen Code zu erklären und kritisch zu bewerten. Zusätzlich bekommen sie unbekannte Codebeispiele vorgelegt, die sie wie in einem Codereview beurteilen sollen.

Im Folgenden stellen die Autoren die Maßnahmen zum Erreichen einer besseren Codequalität vor, wie wirksam sie waren und wie sie sich auch in Unternehmen für die gezielte Weiterbildung von Berufseinsteiger*innen nutzen lassen können.

Wie an einer Universität üblich haben wir Fachbücher wie "Clean Code" [3] oder "Effective Java" [4] in großzügiger Anzahl über die Bibliothek angeschafft, in der Lehrveranstaltung empfohlen und in deren Verlauf immer wieder darauf verwiesen. Ausgeliehen haben sie allerdings nur wenige. Über die Jahre war klar: Nur die Besten haben die Fachliteratur freiwillig studiert, der Rest gab sich mit den Vortragsinhalten zufrieden. Aus Dozentensicht war das trotz des geringen Aufwands unbefriedigend, da wir ja auch die Breite erreichen wollten.

In der Industrie sind unsere Erfahrungen ähnlich: Die Besten bilden sich freiwillig, oft auch in der Freizeit, selbstständig weiter. Diejenigen, die dazu nicht bereit sind oder es aus privaten Gründen einfach nicht möglich machen können, fallen zurück. Das bedeutet, dass die Anschaffung von Büchern in der Abteilungsbibliothek, die Bereitstellung von E-Books im Intranet oder das Abo einer Online-Lernplattform eben nicht ausreichen.

In unseren Kursen hatten wir auch die Möglichkeit, Pflichtlektüren als Hausaufgabe zu geben, die dann gemeinsam im Plenum besprochen wurden – zum Beispiel ein Kapitel aus Martin Fowlers Refactoring-Buch [5] oder ein Artikel zum Thema Concurrency [6]. Dadurch dass die Inhalte von überschaubarem Umfang waren und zudem genau zum Stoff passten, haben die meisten sie gelesen und die fachliche Diskussion war dementsprechend direkt von Anfang an auf einem hohen Niveau. In der Ausbildung ist eine solche Pflichtlektüre mit Nachbesprechung in der Veranstaltung ein geeignetes Mittel, um ein wichtiges Thema voranzutreiben. Das ist auch eine Empfehlung, die in Unternehmen funktionieren kann, wenn es gelingt, sowohl für das Vorbereiten als auch für die Besprechung Arbeitszeit vorzusehen.

Statische Codeanalyse verspricht ein unvoreingenommenes, vollständiges und vor allem günstiges Codereview, das Code-Smells automatisch und zuverlässig aufdeckt. Sie verhindert, dass unausgereifter Code in den Produktivbetrieb kommt. Von diesen Versprechungen geleitet haben sich entsprechende Werkzeuge stark verbreitet.

In der Lehre haben wir das auch ausprobiert. Wir stellten den Studierenden die einschlägigen Open-Source-Werkzeuge vor und erklärten die Einbindung in die IDEs. Das kostete Zeit, die wir in der Hoffnung auf weniger Korrekturaufwand gerne investierten. Das Ergebnis blieb weit hinter den Erfahrungen zurück. Zum einen zeigte sich, dass viele sowohl die Codeanalysewerkzeuge als auch die Standardwarnungen der IDE komplett ignorierten. Zum anderen stieg der Betreuungsaufwand während der Programmieraufgaben, da immer wieder Rückfragen kamen, wie denn Meldung "X" zu beseitigen wäre. Bei näherer Betrachtung waren das meist false-positives beziehungsweise komplett irrelevante Regeln, die getrost hätten deaktiviert werden können. Im schlimmsten Fall sahen wir obskure Workarounds für relativ einfache Funktionen, um die Warnungen loszuwerden.

Die Einführung statischer Codeanalysewerkzeuge war also mit einem mittleren Aufwand verbunden, brachte aber kaum etwas. Teils wurden die Werkzeuge ignoriert, teils gingen insbesondere die motivierten Studierenden über große Längen, um alle Hinweise systematisch zu eliminieren, was wiederum nicht förderlich für den Lernprozess war. Hier greift das Goodhart'sche Gesetz: "Wenn eine Metrik zum Ziel wird, ist sie keine gute Metrik mehr."

Im Unternehmenskontext gibt es häufig folgendes Muster: Jemand, dem Codequalität wichtig ist, stellt stichprobenhaft fest, dass sie nicht ausreichend ist. Da die Person nicht die Zeit hat, alles selbst zu reviewen, sorgt sie für die Einführung von Codeanalysewerkzeugen. Um alle Fehlerquellen abzudecken, werden dann viele der im Werkzeug vorhandenen Regeln aktiviert und als Vorgabe ausgegeben. Infolgedessen geht die Produktivität des Teams nach unten: Es wird etwa der Getter des Feldes "firstname" mit "Gets the firstname" dokumentiert. Nur hat das nichts mit Codequalität zu tun, sondern ist eine Arbeitsbeschaffungsmaßname.

Dass Unternehmen statische Codeanalyse "erfolgreich" einsetzen, möchten wir nicht in Abrede stellen. Aus unserer Erfahrung ist sie allerdings kein geeignetes Mittel, um ein Gespür für hochqualitativen Code zu erlernen. Für Studierende, aber auch für Berufseinsteiger*innen läuft der Einsatz statischer Codeanalyse zumeist auf das unreflektierte Befolgen von Regeln hinaus, statt die Dinge zu hinterfragen. Selbst bei einer sinnvoll konfigurierten Regelauswahl tritt oftmals ein ähnlicher Effekt wie bei der Fachbuchanschaffung ein: Den Guten hilft es, noch einen Flüchtigkeitsfehler zu tilgen, die Schwächeren werden frustriert und ignorieren die Werkzeuge in Gänze. Hilfe könnte sein, die Regeln gemeinsam im Team zu besprechen und zu diskutieren – und dabei eben ein Gespür zu schaffen, wann man nach Benedikt von Nursia eher die Regel beugen soll als den Menschen.

Wenn mehrere Entwickler*innen in einem Projekt zusammenarbeiten, ist der Drang da, die Arbeit irgendwie aufzuteilen. Naiverweise könnte man N Entwickler*innen jeweils 1/N der Funktionalität zur Implementierung geben und nach einer gewissen Zeit die Einzelteile zu einem Produkt zusammensetzen. Klassisches "Divide and Conquer" anhand definierter Schnittstellen. So ähnlich haben viele unserer Teams die Projekte bearbeitet. Leider haben die Teile nach gut einer Woche Programmierung nicht immer so zusammengepasst wie geplant. Auch wenn das Zusammenstöpseln zur Abgabe noch geklappt hat, zeigte sich bei der Prüfung, dass jeder nur den eigenen Teil gut konnte. Die Versuche der Studierenden, sich den Code gegenseitig "vorzustellen", haben oft nicht gereicht. Ganz anders war das bei denen, die den Code gemeinsam entwickelt haben.

Beim Pair Programming bedient die eine Person das Keyboard, während die andere über die nächsten Schritte nachdenkt. Diese Art, ein Programm zu entwickeln, ist für Neulinge keine leichte Sache. Sie ist aber der beste Weg, um eine hohe Codequalität zu erreichen. Nicht nur, dass vier Augen mehr sehen als zwei; um auf eine gemeinsame Lösung zu kommen, muss diskutiert und abgewogen werden. Das erfordert Übung und eine hohe Sozialkompetenz. Am Anfang ist für viele der soziale Druck hoch, wenn sie sich oft vertippen oder ihnen die Funktionsnamen der Standardbibliothek nicht mehr einfallen. Hier wollen wir nicht darauf eingehen, wie Pair Programming funktionieren kann [7], sondern dazu ermutigen, es einfach auszuprobieren.

Mob Programming [8] geht sogar noch einen Schritt weiter. Hier sitzen mindestens drei Personen vor einem Rechner und erreichen dadurch eine noch bessere Qualität. Wir haben das als didaktisches Mittel in der Lehrveranstaltung bei der Besprechung der Übungen genutzt, sprich mit allen Studierenden vor dem Beamer. Es ist ein großer Aufwand, der sich dann rechtfertigen lässt, wenn die Qualität und der Wissenstransfer maximiert werden sollen.

Studierende, die wirklich gemeinsam die Projekte bearbeitet hatten, lieferten nicht nur bessere Ergebnisse, sondern brachten auch in der mündlichen Prüfung die besseren Argumente. Unternehmen können daraus lernen, indem sie vor allem ihren neuen Entwickler*innen die Möglichkeit geben, zu zweit oder in Gruppen zu programmieren. So können diese schnell eingelernt werden und früh zum Produktivcode beitragen. Insbesondere das Pairing von Berufseinsteiger*innen mit erfahrenen Kräften maximiert den Wissenstransfer und kann ein traditionelles Onboarding größtenteils ersetzen. Die Motivation ist dank schneller Fortschritte höher, der Bildung von Wissenssilos wird entgegengewirkt. Deswegen die Empfehlung an Young Professionals: Macht so viel Pair und Mob Programming wie möglich, um von den erfahrenen Kolleg*innen zu lernen.

Codereviews sind das zentrale Element in den Lehrveranstaltungen, da wir überzeugt sind, dass die gemeinsame Diskussion das geeignete Mittel ist, um Codequalität zu erlernen. Wie eingangs beschrieben, bestanden die Lehrveranstaltung, die Korrektur der Hausaufgaben, die Sprechstunden und Tutorien und auch die Abschlussprüfung aus Codereviews. Das schult das Auge für Code-Smells und verbessert die Argumentationsfähigkeit.

Der Erfolg von einem Codereview hängt daran, dass Lernende verstehen, warum ihre Implementierungsvariante ein Problem darstellt. Denn nur wenn sie dieses einsehen, können sie eine Lösung als Verbesserung annehmen. Das bedeutet, dass man den Code nicht nur knapp kritisieren sollte, sondern durchaus die Hälfte des Texts über das Problem schreiben kann. Auf der Lösungsseite schlägt man idealerweise eine konkrete Verbesserung in Form von Code vor und beschreibt deren Vorteile knapp.

Unternehmen raten wir deshalb, Ähnliches zu tun. Einsteiger*innen brauchen Feedback, mit dem sie etwas anfangen können, um sich zu verbessern. Die Abnahme von Code wird viel einfacher, wenn sie in kleinen Häppchen passiert und früh die Kriterien für die Codequalität diskutiert werden. Hierbei ist Pairing optimal, aber auch schriftliche Codereviews in einem Pull-Review-Workflow sind wertvoll, da er üblicherweise von jemandem verfasst wird, der an der ursprünglichen Entwicklung unbeteiligt war. So bekommt man eine weitere Perspektive auf den Code. Wenn man nur begrenzte Zeit hat, sollte man ökonomisch vorgehen: zuerst die schlimmen Probleme angehen, danach die weniger gewichtigen.

Universitäten sind ein Ort des Lernens. Es sollte aber jedem klar sein, dass es mit einem Abschluss nicht getan ist, sondern es im Berufsleben mit dem Lernen genauso weitergeht, nur dass es vielleicht nicht mehr so klar ist, wer die Dozent*innen sind. Als (Young) Professionals sind alle gleichermaßen Lehrende und Lernende. Deshalb ist es wichtig, dass jeder einen Lernanspruch an sich selbst hat sowie das Lernen im Unternehmen wertgeschätzt und entsprechende Maßnahmen gefördert werden. Durch das Vorleben dieses Anspruches in der Lehrveranstaltung konnten wir unsere Erwartungen an die Studierenden setzen und waren dementsprechend zufrieden mit deren Lernerfolg. Dazu benötigten wir jedoch viel Zeit für Diskussionen in und nach der Lehrveranstaltung und noch mehr zeitlichen Aufwand für die schriftlichen Reviews der Hausaufgaben.

Der Aufwand war ebenso groß bei den Studierenden, die sich regelmäßig darüber beschwert haben. Rückblickend hat sich aber gezeigt, dass wir viele richtige Entscheidungen getroffen haben. Dafür sprechen nicht nur die sechs Nominierungen und schließlich der Gewinn des Preises für gute Lehre, sondern auch, dass diejenigen, die bei uns im Kurs sehr gut waren, das Wissen später auch im Berufsleben rüberbringen konnten.

Für die Leser*innen empfehlen wir: Schafft eine Lernkultur und greift zum beschriebenen Werkzeugkasten, der bei weitem nicht abschließend ist. Code-Retreats, Hackathons, persönliches Mentoring, Katas et cetera sollten ihn ergänzen. Der langfristige Erfolg hängt jedoch nicht an punktuellen Maßnahmen oder einem elaborierten Styleguide für guten Code, sondern von dem täglichen Anspruch an das eigene Handwerk ab. Wer neu im Team ist, der schlägt dem Team-Lead Verbesserungen vor und findet Gleichgesinnte, um die Entwicklungsprozesse fortwährend zu verbessern.

Linus Dietz forscht an der Technischen Universität München zu Mobilität und Empfehlungsdiensten für Reisende. Dr. Simon Harrer ist seit zwei Jahren Senior Consultant bei INNOQ und sorgt dank Remote Mob Programming von Zuhause aus für zufriedene Kunden. Gemeinsam haben sie aus ihrer Erfahrung in Open-Source-Software und durch die Lehre an der Universität Bamberg das Buch "Java by Comparison" verfasst und sind regelmäßige Redner in User Groups und Softwarekammern.

  1. Linus Dietz, Johannes Manner, Simon Harrer, Jörg Lenhard; Teaching Clean Code. 1st Workshop on Innovative Software Engineering Education, 2018
  2. Simon Harrer, Jörg Lenhard, Linus Dietz; Java by Comparison; The Pragmatic Bookshelf 2018
  3. Robert C. Martin; Clean Code; Prentice Hall 2008
  4. Joshua Bloch; Effective Java; Addison-Wesley Professional 2018
  5. Martin Fowler; Refactoring; Addison-Wesley Professional 1999
  6. Igor Sorokin, Alex Miller; Core Java Concurrency
  7. Kent Beck; Extreme Programming Explained: Embrace Change; Addison-Wesley Professional 2005 (2. Aufl.)
  8. Mark Pearl; Code with the Wisdom of the Crowd; The Pragmatic Bookshelf 2018

(ane)