Fünf Maßnahmen für mehr Codequalität

the next big thing Golo Roden  –  65 Kommentare

Die Codequalität zu verbessern, ist vielen Teams ein wichtiges Anliegen. Dabei gibt es einige grundlegende Maßnahmen, die man mit verhältnismäßig überschaubarem Aufwand anwenden kann. Welche sind das?

Code wird von Entwicklerinnen und Entwicklern geschrieben, doch sieht er je nach Autorin oder Autor durchaus unterschiedlich aus. Das zeigt sich bereits an einfachen Beispielen wie der Art der Einrückung, dem Umgang mit Leerzeilen oder der Vorgehensweise bei der Klammersetzung. Viele dieser Dinge betreffen primär die visuelle Gestaltung des Codes, nicht seine Funktionsweise – weshalb es hier häufig kein pauschales "richtig" oder "falsch" gibt.

Es gibt aber durchaus andere Beispiele, in denen unterschiedliche Stile auch Auswirkungen auf die Funktionsweise haben können. In JavaScript ist es beispielsweise ein großer Unterschied, ob man den Operator "===" oder "==" für den Vergleich zweier Werte heranzieht: Arbeitet der erste typsicher, konvertiert der zweite intern gegebenenfalls, weshalb die beiden Operatoren in vielen Fällen zu unterschiedlichen Ergebnissen kommen.

Wenn man Code liest, versucht man nachzuvollziehen, was sich die Autorin oder der Autor bei dessen Entwicklung gedacht hat. Für viele der genannten stilistischen Unterschiede gibt es dabei gar keine speziellen Gründe, die Schreibweise ist oftmals mehr oder weniger Zufall. Das Beispiel mit den Vergleichsoperatoren zeigt aber, dass es auch anders sein kann: Gründe können hier Unwissenheit, Flüchtigkeit oder auch Absicht sein.

Da man rein durch Lesen nicht herausfinden kann, was die ursprüngliche Intention war, muss man in solchen Fällen im Zweifelsfall nachfragen, was Zeit kostet. Daher bietet es sich an, verbindliche Richtlinien für das Team aufzustellen, wie Code zu formatieren und zu schreiben ist, und diese Richtlinien im Idealfall auch noch automatisiert zu überprüfen. Genau diese Aufgabe übernehmen Linter wie ESLint, was für JavaScript und TypeScript geeignet ist.

Das Ziel ist, Code nicht mehr ansehen zu können, wer ihn geschrieben hat. Das ist an einigen Stellen zum Scheitern verurteilt. Beispielsweise lässt sich die Benennung von Variablen nicht zuverlässig und einheitlich über einen Linter steuern, aber in vielen anderen Fällen funktioniert es, und es hilft dabei, Code lesbarer und einheitlicher zu gestalten, sodass man weniger über dessen äußere Form und mehr über dessen Sinn nachdenken kann.

Je weniger beliebig die äußere Form ist, desto weniger Spekulation ist erforderlich, warum Code auf eine bestimmte Art geschrieben wurde, und desto eher kann man sich auf den eigentlichen Inhalt konzentrieren. Daher ist ein Werkzeug zum automatisierten Erzwingen von Coding-Standards ein wichtiger Baustein für eine hohe Qualität.

Coding-Standards

Code testen

Für die innere Qualität, also das korrekte Verhalten von Code, gibt es Tests. Diese sollten nicht von Hand ausgeführt werden, denn es geht nicht darum, lediglich einmal zu zeigen, dass der Code korrekt funktioniert. Stattdessen soll nach jeder Änderung, jeder Erweiterung und jeder Anpassung erneut gezeigt werden können, dass der Code nach wie vor auf die gleiche Art funktioniert. Tests sind also vielmehr ein Sicherheitsnetz, das vor unbeabsichtigten Fehlern schützt.

Tests verhindern also, dass man bereits behobene Fehler erneut einbaut oder dass man aus Versehen Code kaputt macht, der bereits funktioniert hatte. Natürlich ließe sich dafür eine Liste von Testfällen von Hand schreiben, aber diese immer und immer wieder gleichermaßen, gewissenhaft, sorgfältig, verlässlich und reproduzierbar von Hand abzuarbeiten, ist nahezu unmöglich und extrem zeitaufwendig.

Genau aus dem Grund sind automatisierte Tests so wichtig. Die Frage, die sich daraus ergibt, ist allerdings, wann diese Tests geschrieben werden sollten. Methoden wie TDD schlagen vor, Tests noch vor der eigentlichen Implementierung zu schreiben. Das ist zwar eine gute Fingerübung, funktioniert aber nur dann mit vertretbarem Aufwand, wenn das Ziel bereits halbwegs klar ist.

Wenn das nicht gegeben ist, empfiehlt es sich, zunächst einen prototypischen "Proof of Concept" ohne Tests zu entwickeln, mit dem man Erfahrung sammeln und Annahmen validieren kann, diesen anschließend wegzuwerfen und dann die eigentliche Implementierung mit Tests vorzunehmen. Tests sind also so oder so unverzichtbar, und gemeinsam mit Coding-Standards liefern sie ein gutes Fundament für eine gute Qualität.

Code testen

Code-Reviews

Code muss allerdings auch stets noch von Menschen geprüft werden – allein schon, um beispielsweise sicherzustellen, dass die Tests auch tatsächlich sinnvolles Verhalten testen. Außerdem sagen Tests nichts über die Art der Implementierung aus, ob sie zielstrebig und effizient ist, ob der gewählte Algorithmus optimal passt oder nicht, sondern nur darüber, ob das von außen messbare Ziel erreicht wurde. Daher gibt es Code-Reviews.

Der Sinn von Reviews ist, Fehler aufzudecken und die gewählte Vorgehensweise zu hinterfragen. Der Fokus sollte also auf inhaltliche Aspekte gelegt werden, nicht auf äußere (dass die äußere Form passt, dafür sorgt bereits der Linter). Die Frage ist allerdings, wie man das macht. Letztlich gibt es zwei Optionen:

  • Zum einen kann man Code zügig überfliegen, um oberflächliche Fehler wie Schreibfehler, fehlende Kommentare und Ähnliches zu finden.
  • Zum anderen kann man sich auch die Zeit nehmen, sich in den Code hineinzudenken und versuchen, nachzuvollziehen, wie er entstanden ist.

Es liegt auf der Hand, dass das zweite weitaus sinnvoller, aber eben auch viel zeitaufwendiger ist. Daher sind Code-Reviews zwar äußerst wünschenswert, oftmals aber in ihrer Idealform nur schwierig umsetzbar.

Code-Reviews

Pair Programming

Eine gute Alternative beziehungsweise Ergänzung dazu bildet das Pair Programming. Dabei arbeiten zwei Entwicklerinnen oder Entwickler gemeinsam an einem Codeabschnitt und wechseln sich gegenseitig mit Tippen ab. Immer derjenige, der nicht tippt, denkt mit und hinterfragt das Vorgehen.

Durch dieses Vier-Augen-Prinzip fallen gemachte Fehler, falsche Annahmen und unglückliche Entscheidungen viel schneller auf, als wenn eine Person für sich allein arbeitet. Im Prinzip handelt es sich bei Pair Programming, wenn man so will, um einen in Echtzeit durchgeführten kontinuierlichen Review. Der stetige Austausch führt letztlich zu früherem Feedback und damit auch zu besseren Lernerfolgen und weniger Inselwissen.

Zudem lässt sich Pair Programming auch hervorragend mit Screensharing und Audio- beziehungsweise Videokonferenzen als Remote-Variante umsetzen.

Pair-Programming

Collective-Code-Ownership

Wenn die Paare immer wieder tauschen und sich neu zusammenfinden, hinterlässt das die Frage, wer für Code verantwortlich ist. Die einfache Antwort darauf lautet, dass jeder für jeglichen Code zuständig ist. Das bedeutet nicht, dass jeder ohne Rückfrage alles entscheiden und ändern kann, aber es bedeutet, dass sich jeder zumindest verantwortlich fühlen sollte, auch am Code anderer mitzuwirken.

Es empfiehlt sich, ähnlich der Organisation von Open-Source-Projekten, einige wenige Entwicklerinnen und Entwickler als Maintainer pro Modul auszuwählen, die sozusagen die Hoheit über die Strategie und Vision für dieses Modul innehaben. Wer etwas ändern, erweitern oder ergänzen will, muss zunächst das Gespräch mit den Maintainern suchen und die gewünschte Änderung mit ihnen abstimmen.

Dadurch wird die Strategie eines Moduls nicht verwässert und es bleibt eine gewisse Konsistenz erhalten. Zugleich sind aber nicht immer nur die Maintainer für die Weiterentwicklung des Moduls verantwortlich, ihre Aufgabe liegt also eher in der Koordination der Contributions von anderen. Gefragt sind am Ende also vorausschauendes Handeln und ein starkes Wir-Gefühl.

Collective-Code-Ownership

Fazit

Auch wenn die genannten Maßnahmen für viele Projekte bereits zum Alltag gehören, gibt es doch auch immer wieder welche, in denen das nicht gilt: Gerade das Testen und das Pair Programming gehören zu den oftmals vernachlässigten Kandidaten, wobei als Ursache regelmäßig Zeitmangel genannt wird. Letztlich leidet aber langfristig die Qualität der entwickelten Software, wenn man an diesen Aspekten spart.

Daher ist es ratsam, diese fünf Aspekte umzusetzen. Selbstverständlich bilden sie nur die Grundlage und sind nicht das Ende der Fahnenstange, aber wer diese fünf Punkt konsequent und gewissenhaft umsetzt, hat schon eine gute Ausgangsbasis erreicht.