Clean Code: allgemeine Grundsätze

Karl Gorman
Clean Code by Chudovo

Wir starten eine Artikelserie über Clean Code-Konzepte, die auf dem berühmten Buch von R.Martin Clean Code basiert: A Handbook of Agile Software Craftsmanship, der Klassifizierung von Urs Enzler von BBV und unseren Erfahrungen. Code mit Beispielen auf die Sprache C# gegeben, aber es könnte für die meisten aller OOP-Sprachen geeignet und übertragbar sein. Wir erklären jedes Prinzip, jede Regel und jede Negativregel; geben unsere eigenen Kommentare, wo es sinnvoll ist, dazu.

Warum sauberer Code

Sauberer Code ist eine Menge positiver und negativer Prinzipien/Regeln, die es erlauben, idealen Code zu produzieren. Das ist natürlich ein Witz. Idealer Code scheint von gestern zu sein, aber heute erfordert ein Teil ein Refactoring. Aber Refactoring ist ein Prozess, der Zeit kostet, die für andere Aufgaben reserviert ist.
Wo ist die Wahrheit? Was ist zu tun?
Idealen Code gibt es nicht. Jeder wertvolle Teil des Codes lässt sich optimieren, refaktorisieren. Jeder neue Tag bringt neue Codeprinzipien und Verbesserungen.
Code ist sauber, wenn er leicht verstanden werden kann – von jedem im Team. Mit der Verständlichkeit kommt die Lesbarkeit, Änderbarkeit, Erweiterbarkeit und Wartbarkeit. All die Dinge, die benötigt werden, um ein Projekt über eine lange Zeit am Laufen zu halten, ohne eine große Menge an technischen Schulden anzuhäufen.

Das Schreiben von sauberem Code von Anfang an in einem Projekt ist eine Investition, um die Kosten für Änderungen während des gesamten Lebenszyklus eines Softwareprodukts so konstant wie möglich zu halten. Daher sind die anfänglichen Kosten für Änderungen beim Schreiben von sauberem Code (graue Linie) etwas höher als bei der schnellen und schmutzigen Programmierung (schwarze Linie), amortisieren sich aber recht bald. Vor allem, wenn man bedenkt, dass der größte Teil der Kosten bei der Wartung der Software anfallen wird. Unsauberer Code führt zu technischen Schulden, die mit der Zeit zunehmen, wenn er nicht in sauberen Code umgewandelt wird. Es gibt noch andere Gründe, die zu Technical Debt führen, wie z.B. schlechte Prozesse und fehlende Dokumentation, aber unsauberer Code ist ein Haupttreiber. Infolgedessen ist Ihre Fähigkeit, auf Änderungen zu reagieren, eingeschränkt (rote Linie).

Kommentar: Aus der Grafik ist ersichtlich, dass es für kurzfristige Projekte keinen Sinn macht, viel Zeit für die Einhaltung der Clean Code Prinzipien aufzuwenden. Typisches Beispiel ist das Prototyping.

In sauberem Code können sich Bugs nicht verstecken

Die meisten Software-Fehler werden beim Ändern von bestehendem Code eingeführt. Der Grund dafür ist, dass der Entwickler, der den Code ändert, die Auswirkungen der vorgenommenen Änderungen nicht vollständig erfassen kann. Sauberer Code minimiert das Risiko der Einführung von Defekten, indem der Code so einfach wie möglich zu verstehen ist.

Kommentar: Eine der Metriken, die wir bei der Bewertung von Entwicklerarbeiten verwenden, ist „Geschwindigkeit der Fehlerbehebung“. Eine hohe Geschwindigkeit zeigt eine korrekt implementierte/eingestellte Aufgabe und umgekehrt.

✓ Standardkonventionen beachten

Kodier-, Architektur-, Designrichtlinien (überprüfen Sie diese mit Tools).
Haben Sie Code-Konventionen? Befolgen Sie Architekturprinzipien? Haben Sie objektorientierte Designrichtlinien? Benutzen Sie Werkzeuge wie Resharper? Wie bereits erwähnt, sollte der Code leicht verständlich sein. Das geschieht, wenn jeder Entwickler Code-Konventionen befolgt.

Beispiel 1: Namen von Klassen, Eigenschaften, Methoden – Pascal-Gehäuse; Namen von Parametern, internen Methodenvariablen – Kamelgehäuse. Wenn ein Entwickler sich entscheidet, einen eigenen Stil zu verwenden oder keinem Stil zu folgen, wird die Geschwindigkeit der Implementierung/Wartbarkeit abnehmen.

Beispiel 2: Der Name der Spalten, die auf dem Fremdschlüssel basieren, ist typischerweise „Name der verknüpften Tabelle>ID (ContactID, CustomerID, etc). Wenn der Entwickler beschließt, sie mit dem letzten kleinen Zeichen als ContactId für die DB zu benennen, ist das in Ordnung. Aber wenn der Code den Mechanismus der automatischen Generierung von Entitäten mit der Regel „für alle Entitäten, die die Spalte ContactID haben -> etwas generieren“ verwendet, erhalten wir einen logischen Fehler.

Beispiel 3: Es ist sehr wichtig, Klassen, Methoden und Eigenschaften korrekt zu benennen. Falsche Benennungen können andere Entwickler irritieren, die Geschwindigkeit der Implementierung verringern und Bugs verursachen. Wenn die Klasse File existiert, sind die Eigenschaften Size oder Path nicht korrekt. File.Size und File.Path haben eine Vielzahl von Interpretationen. Die Dateigröße kann in Bits, Bytes, KB… angegeben werden, der Dateipfad kann absolut oder relativ sein. Viel bessere Namen sind File.SizeInBytes und File.AbsolutePath.

Auf der Suche nach einer neuen Herausforderung?

Interessante Projekte

Zuverlässige Arbeitgeber

Faire Vergütung


Beispiel 4: Manchmal erhalten wir Projekte/Code, bei denen wir feststellen, dass die Anzahl der Zeilen mit Kommentaren mit der Anzahl der Codezeilen vergleichbar ist.

///lt;/summary gt;
/// Ermittelt das gebundene Objekt.
///lt;/summary gt;
public object BoundObject { get; set; }

Offensichtlich haben wir die Eigenschaft BoundObject und konnten erkennen, dass das Objekt ein gebundenes Objekt im System darstellt. Wir brauchen in diesem Fall keinen zusätzlichen Kommentar. Der Code selbst ist der beste Kommentar, schreiben Sie nur Kommentare, wenn es wirklich nötig ist, lassen Sie keine kommentierten Codezeilen stehen.
Beispiel 5: Einige Beispiele, wo es Sinn macht, Kommentare zu hinterlassen:

  • TODO etwas
  • Seltsame Geschäftslogik, die für das Verständnis unklar ist
  • Öffentliche API mit der Anforderung, Dokumentation aus Kommentaren zu generieren

Kommentar: Code-Stil-Konventionen sind eher statisch im Vergleich zu Architektur-Prinzipien, die von Projekt zu Projekt typischerweise unterschiedlich sind. Wir könnten einige Beispiele für Code-Konventionen austauschen

✓ Keep it Simple, Stupid (KISS)

KISS ist ein Akronym für „Keep it simple, stupid“ (Halte es einfach, dumm), ein Konstruktionsprinzip, das von der US-Marine im Jahr 1960 aufgestellt wurde. Das KISS-Prinzip besagt, dass die meisten Systeme am besten funktionieren, wenn sie einfach gehalten werden, anstatt sie kompliziert zu machen; daher sollte Einfachheit ein Hauptziel bei der Konstruktion sein und unnötige Komplexität sollte vermieden werden. Der Satz wurde mit dem Flugzeugingenieur Kelly Johnson in Verbindung gebracht. Variationen des Satzes beinhalten „keep it short and simple“, „keep it simple and straightforward“ und „keep it small and simple“.

Jedes größere System wird eine große Menge an Logik und Komplexität enthalten. Das primäre Ziel bei der Verwaltung dieser Komplexität ist es, sie so zu organisieren, dass ein Entwickler weiß, wo er suchen muss, um Dinge zu finden, und nur die direkt betroffene Komplexität zu einem bestimmten Zeitpunkt verstehen muss. Im Gegensatz dazu behindert uns ein System mit größeren, vielseitigen Klassen immer, indem es darauf besteht, dass wir uns durch viele Dinge durchwühlen, die wir im Moment nicht wissen müssen.

Visual Studio kann Code-Metriken berechnen. Menü ANALYZE -> Code-Metriken berechnen. Um festzustellen, ob der Quellcode einfach ist oder nicht, müssen Sie die zyklomatische Komplexität prüfen. Ein niedriger Wert ist besser.
Zyklomatische Komplexität – Misst die strukturelle Komplexität des Codes. Sie wird durch die Berechnung der Anzahl der verschiedenen Codepfade im Programmfluss erstellt. Ein Programm mit komplexem Kontrollfluss erfordert mehr Tests, um eine gute Codeabdeckung zu erreichen, und ist weniger wartbar.

Möglichkeiten zur Verringerung der zyklomatischen Komplexität
:- Befolgen Sie die Code-Konvention mit der Regel, nicht mehr als 50 Zeilen in einer Methode zu haben-
Teilen Sie große Methoden in Untermethoden auf und verlagern Sie den Code dorthin-
Verringern Sie die Anzahl der bedingten Anweisungen (if else if
)- Verwenden Sie Attribute, verwenden Sie AOP (aspektorientierte Programmierung)

Kommentar: Das ist wirklich wahr! Wenn es eine Aufgabe gibt: Das Projekt sollte in einem Monat mit einer Verbindung zu MS SQL Server implementiert werden, es macht keinen Sinn, zusätzliche x Tage für die Erstellung der Kommunikation mit anderen RDBMS zu verwenden. Es macht keinen Sinn, Flexibilität zu implementieren, wenn der Kunde/Produkteigentümer nicht danach fragt. Der aktuelle Trend ist, dass NOSQL DB wirklich in 1% aller Projekte benötigt wird (Facebook, Google, etc.). Die anderen 99% könnten über Standard-RDBMS-Server abgedeckt werden. Die Entwicklungsmöglichkeiten und die Implementierungsgeschwindigkeit sind mit NOSQL viel geringer. An diesem Punkt beginnen viele neue Projekte mit NOSQL. Warum eigentlich? Wenn das Projekt aus geschäftlicher Sicht erfolgreich ist, erscheint das Budget und die Aufgabe für die Adaption auf neue Herausforderungen in der Zukunft. Aber wenn große Herausforderungen von Anfang an gelöst werden und doppelt so viel Zeit darauf verwendet wird – ist die Wahrscheinlichkeit groß, dass das Projekt von Anfang an scheitert.

✓ Pfadfinder-Regel

Es reicht nicht aus, den Code gut zu schreiben. Der Code muss mit der Zeit sauber gehalten werden. Wir alle haben schon gesehen, wie Code mit der Zeit verrottet und degradiert. Wir müssen also eine aktive Rolle bei der Verhinderung dieser Verschlechterung übernehmen.

Die Boy Scouts of America haben eine einfache Regel, die wir auf unseren Beruf anwenden können.
Verlasse den Zeltplatz sauberer als du ihn vorgefunden hast.

Wenn wir alle unseren Code etwas sauberer einchecken würden als beim Auschecken, könnte der Code einfach nicht verrotten. Die Aufräumarbeiten müssen nicht etwas Großes sein. Ändern Sie einen Variablennamen zum Besseren, lösen Sie eine Funktion auf, die ein wenig zu groß ist, beseitigen Sie ein kleines bisschen Duplikation, bereinigen Sie eine zusammengesetzte if-Anweisung. Können Sie sich vorstellen, an einem Projekt zu arbeiten, bei dem der Code mit der Zeit einfach besser wurde? Glauben Sie, dass irgendeine andere Option professionell ist? Ist die kontinuierliche Verbesserung nicht sogar ein wesentlicher Bestandteil der Professionalität?

Kommentar: eines der Merkmale der Entwickler-Senioritätsstufe ist „der Code sollte nach einer Code-Änderung besser werden (Fehlerbehebung, Hinzufügen neuer Zukunft, etc.).“

✓ Fehlerursachenanalyse

Suchen Sie immer nach der Ursache für ein Problem. Andernfalls wird es Sie wieder erwischen. Die Root-Cause-Analyse ist eine Methode der Problemlösung, die zur Identifizierung der Grundursachen von Fehlern oder Problemen verwendet wird

Ein Faktor wird als Grundursache betrachtet, wenn seine Beseitigung aus der Fehlerfolge des Problems die Wiederholung des unerwünschten Ereignisses verhindert; ein kausaler Faktor ist dagegen ein Faktor, der das Ergebnis eines Ereignisses beeinflusst, aber keine Grundursache ist. Obwohl die Beseitigung eines kausalen Faktors ein Ergebnis begünstigen kann, verhindert sie nicht mit Sicherheit sein erneutes Auftreten.

Allgemeine Prinzipien der Ursachenanalyse:

  • Das Hauptziel der Ursachenanalyse ist: die Faktoren zu identifizieren, die zu Art, Ausmaß, Ort und Zeitpunkt der schädlichen Folgen (Konsequenzen) eines oder mehrerer vergangener Ereignisse geführt haben; zu bestimmen, welche Verhaltensweisen, Handlungen, Unterlassungen oder Bedingungen geändert werden müssen; das erneute Auftreten ähnlicher schädlicher Folgen zu verhindern; und Lehren zu ziehen, die das Erreichen besserer Folgen fördern können. („Erfolg“ ist definiert als die nahezu sichere Verhinderung einer Wiederholung.)
  • Um effektiv zu sein, muss die Ursachenanalyse systematisch durchgeführt werden, normalerweise als Teil einer Untersuchung, mit Schlussfolgerungen und identifizierten Ursachen, die durch dokumentierte Beweise gestützt werden. In der Regel ist eine Teamarbeit erforderlich.
  • Es kann mehr als eine Grundursache für ein Ereignis oder ein Problem geben, weshalb der schwierige Teil darin besteht, die Ausdauer zu zeigen und die Bemühungen aufrechtzuerhalten, die erforderlich sind, um sie zu ermitteln
  • Der Zweck der Identifizierung aller Lösungen für ein Problem besteht darin, ein erneutes Auftreten zu den geringsten Kosten auf die einfachste Weise zu verhindern. Wenn es Alternativen gibt, die gleichermaßen wirksam sind, wird der einfachste oder kostengünstigste Ansatz bevorzugt.
  • Welche Ursachen identifiziert werden, hängt von der Art und Weise ab, wie das Problem oder Ereignis definiert wird. Effektive Problemaussagen und Ereignisbeschreibungen (z. B. als Ausfälle) sind hilfreich und in der Regel erforderlich, um die Durchführung entsprechender Analysen zu gewährleisten.

Kommentar: Der Erfolg der Task-Ausführung hängt vollständig davon ab, wie die Task eingerichtet wurde.

Der nächste Artikel handelt von Gerüchen: Starrheit, Zerbrechlichkeit, Unbeweglichkeit, Viskosität des Designs, Viskosität der Umgebung, unnötige Komplexität, unnötige Wiederholungen und Opazität.

Quellen:
R.Martin Clean Code: A Handbook of Agile Software Craftsmanship
BBV.ch
MSDN
Wikipedia