Code verbessern in wenigen Schritte
Wartung von Projekten kann mit der Zeit sehr aufwändig und kostenintensiv werden. Die folgende Seite soll einen Überblick geben wie man sein Code Stück für Stück besser gestalten kann. Aufgebaut ist dieses in mehrere Stufen, sodass jeder einen guten Einstieg finden kann, egal auf welcher Stufe man sich gerade befindet.
Die hier genannten Punkte sind keine Dogmen und sollten auch hinterfragt werden dürfen, da Softwareentwicklung sich ständig weiterentwickelt und oft von Kosten-Nutzen-Effekten getrieben sind. Auch die Reihenfolge und Stufen können bei jedem Projekt andere sein. Sie sollen nur einen groben Leitfaden geben und basieren auf meinen persönlichen Erfahrungen der letzten 20 Jahre im PHP und Java Umfeld.
Das Ganze erfolgt auf Basis von einer PHP-Anwendung mit dem Symfony Framework, kann aber auch für andere Sprachen adaptiert werden.
Stufe 1 - Basics
Sprechenden Commit Nachrichten
Niemand weiß nach Monaten (teilweise schon nach Tagen) wieso Code so geschrieben ist wie er ist. Deshalb sollte die Nachricht möglichst sprechend, präzise und kurz sein. Eintrage wie "Verschiedene Dinge", "Tests" oder "Stand 01.01.2020" sollten vermieden werden, weil sie keinen Mehrwert bieten. Am besten beschreibt die Nachricht die Fachlichkeit und den Grund warum es zu dieser Änderung kam.
Es sollte immer das Präsens Form verwendet werden und nicht die Vergangenheitsform.
Gibt es weitere Detailinformationen können auch mehrere Zeilen verwendet werden. Durch eine zusätzliche Leerzeile können Details aus dem Normalen GIT Log ausgeblendet werden und erscheinen nur bei der Ansicht eines einzelnen Commits.
Hinzufügen einer Berechtigungsprüfung
Jira Ticket: 0815Review durch: Max Mustermann
Umfang der Commits
Der Umfang einer Änderung sollte eine angenehme Größe haben, sodass man ggf. die Änderung wieder zurückrollen kann und man später auch besser nachvollziehen kann was alles zu der Änderung gehört. Commits mit Veränderung an hunderten von Dateien sollten vermieden werden, das gleiche gilt auch von zu kleinen Änderungen.
Nutzen von Features Branches
Man sollte nie direkt in den Hauptbranch (z.B. master bzw. main) commiten. Wenn hier etwas kaputt ist, dann ist es für alle Entwickler*innen kaputt. Unvollständige Änderungen und Refactorings machen das Leben für alle Entwickler*innen schwer. Da Features in der Regel nicht in wenigen Stunden entwickelt werden können, sollte man am besten für jedes atomare Feature einen eigenen Branch verwenden und diesen später in den Hauptbranch reintegrierend.
Jederzeit Lauffähig
Jeder Commit sollte in sich geschlossen sein und lauffähig sein. Es sollte nie Code eingecheckt werden der zu einen kaputten Projektstand führt. Änderungen können sonst schlechter rückgängig gemacht werden. Außerdem können unvollständige Änderungen zu Problemen in der Entwicklung führen, insbesondere wenn man mit mehreren Entwickler*innen auf einem Feature Branch arbeitet.
Kein Auskommentieren von Code
In vielen Projekten habe ich bisher Entwickler*innen kennen gelernt die Code auskommentiert haben und dann commitet haben. Hierfür gibt es GIT! Wenn man den Code später nochmal braucht, dann kann man mittels GIT Log den Code wieder finden.
Auskommentierter Code hat den großen Nachteil, dass dieser schnell veraltet. Refactorings (z.B. Umbenennen von Variablen) werden in der Regel nicht für auskommentierten Code angewandt. Niemand weiß ob der Code nach einigen Wochen überhaupt noch mit der aktuellen Entwicklung passt. Aus diesem Grund wird auskommentierter Code von vielen Entwickler*innen einfach nie zurück einkommentiert und versauert im Projekt.
Stufe 2 - Gute Qualität
Nutzen von strikter Typisierung
Seit PHP 7.4 können neben Parametern auch Klassenvariablen mit Typen versehen werden. Dieses ist ein sehr mächtiges Feature und bietet zusätzliche Sicherheit in der Entwicklung. Flüchtigkeitsfehler werden so frühzeitig gefunden. Dieses ist insbesondere für die Punkte aus Stufe 4 relevant.
Vermeiden von langen Methoden
Methoden sollten nicht länger 100 Zeichen (manche Entwickler*innen sagen sogar 20 Zeichen sind zu viel) sein. Die Gründe liegen auf der Hand: Lange Methoden machen zu viel, das Verhalten ist schwer zu antizipieren und sehr aufwändig zu testen. Besser ist es kleine Bestandteile aus den Methoden in kleinere Methoden auszulagern die eine Teilaufgabe erledigen. Das kann z.B. eine Validierung der Parameter sein oder das Erstellen von Datenobjekten.
Aufteilen von Verantwortungen
Eine Methode sollte genau eine Aufgabe haben und nicht mehr. Dieses erkannt man z.B. daran, wenn man einen fiktiven Kommentare für die Methode schreiben würde: Diese Methode erstellt eine neue Abrechnung und berechnet einen Wert. Sobald das Wort UND in den Kommentare vorkommt wird dieses Muster verletzte und sollten besser zwei Methoden erstellt werden.
Zusammengehörige Fachlichkeiten sollten außerdem in eigene Komponenten / Klassen ausgelagert werden.
Sprechende Namen
Benenne Dinge (Variablen, Methoden, Klassen) so wie sie sind. Vermeide Abkürzungen oder zu lange Name. Umso länger und kryptischer eine Variable benannt ist, desto länger braucht das Gehirn dieses zu verarbeiten. Sollten Abkürzungen verwendet werden, dann sollte diese für alle gebräuchig sein.
Vermeide Dinge künstliche zu übersetzen, wenn es keine geeignete Übersetzung gibt.
Vermeiden von Kommentaren
Gut strukturierter Code braucht keine Kommentare. Jedem/r Entwickler*in sollte es möglich sein den Code zu verstehen ohne Kommentare lesen zu müssen. Ist dieses nicht der Fall, dann hat die Code Struktur schwächen.
Stufe 3 - Bessere Qualität
Nutzen von automatischen Code Formatter
Es gibt nichts schlimmeres als den persönlichen Code Style eines Entwicklers. Im Projekte sollte man sich auf einen einheitlichen Style einigen. Dieses bietet den großen Vorteil das man nach kurzer Zeit den Code besser wahrnehmen und überfliegen kann. Das Gehirn muss sich nicht für jedes Fragment an einen anderen Stil gewöhnen und Zeile für Zeile einzeln parsen.
Für die Formatierung kann z.B. der PHP-CS-Fixer werden. Diesen kann man entweder regelmäßig selbst ausführen oder sogar in seinem Build Prozess automatisch laufen lassen.
Schreiben von Tests
Um sicher zu sein, dass der Code genau so funktioniert wie gedacht sollte man Unit Tests schreiben. Nur so kann man sicher sein, dass der Code auch nach Umbauten weiter so funktioniert wie er soll. Selbst wenn man glaubt, dass man alles durch Aufruf der echten Anwendung finden kann, muss ich diesem widersprechen. Menschen machen Fehler und werden nach dem x-ten Durchlaufen der Anwendung Betriebsblind - Tests passiert dieses nicht.
Tests sollten nach der Testpyramide erstellt werden: viele kleine Unit Tests, wenige größere Integrationstests.
Aktualisieren von Bibliotheken
Regelmäßig werden Sicherheitslücken in Softwarekomponenten gefunden. Aus diesem Grund sollte man regelmäßig seine verwendeten Fremdbibliotheken aktualisieren. Das gleiche gilt natürlich auch für die PHP-Version, das Betriebssystem und den weiteren Tech Stack.
Viele Bibliotheken bieten LTS Releases an, d.h. eine längerfristige Unterstützung von Versionen. Diese bieten eine zusätzliche Stabilität und verringern den regelmäßigen Wartungsaufwand enorm.
Vor dem Einsatz einer Bibliothek sollte außerdem geprüft werden, ob es überhaupt eine aktive Entwicklung für diese gibt oder ob die letzte Änderung mehr als 5 Jahre her ist. Gerade in der npm Community ist dieses keine seltenheit!
Stufe 4 - Frühzeitiges Finden von Problemen
Nutzen der Entwicklungsumgebung
Viele professionelle Entwicklungsumgebungen wie z.B. IDEA bzw. PhpStorm bringen eine große Menge an Unterstützung mit. Selbst wenn die Software ein paar Euro kostet, das Geld hat man schneller wieder reingeholt als man denkt.
Hier nur ein paar Beispiele was gute Umgebungen bewerkstelligen können
- Finden von Fehlern mit Code Inspection
- Umbenennen von Variablen über Unterstütze Refactorings
- Autovervollständigung
- Syntax Highlighting
- ...
Nutzen von statischen Code Analyse Tools
Seit einigen Jahren gibt es einige Tools die frühzeitig Fehler im Code finden können über statische Code Analyse. Zu nennen wären hier psalm und phpstan. Für Anfänger würde ich phpstan empfehlen da dieses einen einfachen Einstieg hat und selbst auf der niedrigsten Stufe viele gängige Fehler finden kann. Psalm bietet noch weitere Analysen und verzeiht noch weniger Fehler im Code.
Stufe 5 - Vermeiden von Fehlern
Keine Business Logik in Controllern
Viele Frameworks wie Symfony zeigen in ihrer Dokumentation wie einfach es ist Dinge zu tun, allerdings sind die Beispiele oft sehr vereinfacht dargestellt und sollten so nicht von Entwickler*innen direkt verfolgt werden.
Ein Controller sollte z.B. keine größeren fachlichen Entscheidungen treffen. Dieses sollte immer eine eigenständige Komponente tun. Dieses bietet zum einen den Vorteil, dass man die Fachlichkeit losgelöst vom Aufrufer (Konsole, HTTP Request, Message Queue) testen kann und dass man sich nicht zu stark an ein Framework bindet.
Nahezu jedes Framework hat einmal größere Umbauten in der Architektur gehabt, dieses hat zu immensen Aufwänden in Projekten geführt, wenn man das Framework auf eine neue Major Version aktualisieren möchte oder sogar muss (z.B. Symfony 1 zu 2, Angular 1 zu 2, Zend 1 zu 2, ...). Diesen Aufwand kann man minimieren in dem nur wenig Komponenten Abhängigkeiten zu dem Framework haben.
Vermeiden von Arrays
Arrays wurden viele Jahre von Entwickler*innen verwendet, da sie sehr flexibel und einfach zu erweitern sind. Genau das ist der große Nachteil. Arrays sind unsicher, da nicht sichergestellt ist, ob ein Wert überhaupt vorhanden ist und ob der Typ der richtige ist. Besser sind dedizierte Objekte die über einen Konstruktor in einen bestimmten Zustand gebracht werden können.
Neben der Sicherheit sind Arrays auch sehr langsam, da diese deutlich mehr Speicher benötigen als Objekte.
Vermeiden von Settern
Ähnlich wie Arrays haben Setter Aufrufe von Klassen den großen Nachteil, dass man nicht sicher gehen kann, ob ein Wert überhaupt gesetzt wurde. Es ist ein implizites Wissen erforderlich, dass vor dem lesenden Aufruf eine Hand voll Setter aufgerufen wurde.
Besser ist hier das Verwenden von Konstruktoren.