So machen Sie Ihren Code sauberer

Veröffentlicht: 2020-06-24

Entwickler verbringen die meiste Zeit damit, bestehenden Code zu lesen und zu pflegen. Wir versuchen zu verstehen, was es tut und wie es geändert oder erweitert werden kann. Warum sich also nur auf die Leistung konzentrieren?

Lesbarkeit sollte genauso wichtig sein, wenn Sie Code erstellen, der lange hält.

Bei der Entwicklung langlebiger Software ist eine hervorragende Lesbarkeit des Codes von entscheidender Bedeutung. Ob es darum geht, den alten Code zu pflegen, neue Funktionen hinzuzufügen oder den alten zu ändern, lesbarer Code macht die Arbeit viel einfacher .

Ein weiterer Vorteil ist, wenn Sie in der Lage sind, neue Entwickler einzuarbeiten . Ein erfolgreiches Onboarding setzt auf die vorhandene Lesbarkeit des Codes.

Die Realität ist, dass Sie, wenn Sie anfangen, an einem Produkt zu arbeiten, das jahrelang entwickelt wurde, irgendwann mit Legacy-Code zu tun haben werden. Es kommt viel Frustration auf, wenn Sie mit Tausenden von Zeilen unlesbaren Codes überschwemmt werden, die Sie nicht geschrieben haben. Es zu pflegen und darauf aufzubauen, macht das nur noch schlimmer.

Bei Mediatoolkit entwickeln wir unser Produkt langfristig weiter. Das bedeutet einerseits, dass wir kurzfristige Lösungen und riskante Geschäftsentscheidungen vermeiden. Auf der anderen Seite verlangt es vom Engineering-Team, etwas mehr Mühe in die Lesbarkeit des Codes zu stecken.

Die Wahrheit ist, dass wir bis vor ein paar Jahren nicht so viel Wert auf die Lesbarkeit von Code gelegt haben. Sie haben es erraten, das war, als einige ernsthafte Probleme mit dem Onboarding neuer Entwickler und dem Upgrade von Funktionen auftraten.

Hoffentlich wiederholen die anderen unsere Fehler nicht und beginnen stattdessen rechtzeitig mit der Reinigung . Hier sind einige Richtlinien zur Lesbarkeit von Code, an die wir uns bei Mediatoolkit halten.

Lesbarkeit ist wichtig

Bei der Arbeit an einem bestehenden Produkt verbringen Entwickler die meiste Zeit damit, bestehenden Code zu lesen und zu pflegen. Wenn es Jahre gedauert hat, etwas zu entwickeln, ist es selbstverständlich, dass es mehr als eine Minute dauern wird, um herauszufinden, was es tut und wie es geändert werden kann.

Trotz der Menge an Zeit, die Entwickler mit dem Lesen des vorhandenen Codes verbringen, wird mehr Wert auf das Schreiben des neuen, leistungsfähigeren Codes gelegt. Die Zeit, die für die Wartung benötigt wird, wird selten berücksichtigt. Also, worauf sollte man sich stattdessen konzentrieren?

Beginnen Sie mit den Grundlagen . Dies sind die ersten Schritte, die Sie unternehmen können, um Ihren Code zu verbessern und sauberer zu machen:

  • Verwenden Sie aussagekräftige Namen
  • Funktionen verbessern
  • Bevorzugen Sie Unveränderlichkeit
  • Bevorzugen Sie die deklarative Programmierung gegenüber dem Imperativ
  • Keep it simple stupid – KISS
  • Wiederholen Sie sich nicht – DRY
  • Du wirst es nicht brauchen – YAGNI

Verwenden Sie aussagekräftige Namen

Sie benennen alles im Code, also stellen Sie sicher, dass Sie es gut machen. Aussagekräftige Namen geben einen Kontext dessen, was vor sich geht. Namen sollten die Absicht des Codes ausdrücken – den Grund, warum etwas vorhanden ist, was es tut und wie es verwendet wird.

Nehmen Sie sich Zeit, um gute Namen zu finden. Der Versuch, sich mit willkürlichem Kauderwelsch daran zu erinnern, was Sie ausdrücken wollten, verbraucht mehr Zeit.

Code ohne aussagekräftige Namen

Code mit aussagekräftigen Namen

Namen sollten nach Möglichkeit aus einer problematischen Domain stammen . Verwenden Sie keine technischen Namen, wenn es einen besseren Namen in der Domäne gibt.

Klassen und Objekte sollten Substantive oder Nominalphrasen als Namen haben, wie beispielsweise customer , emailSender oder htmlParser .

Versuchen Sie jedoch, während Sie diese Regel befolgen, Namen zu vermeiden, die allgemeine Substantive wie data , info , manager , processor , controller … enthalten, da diese meistens nicht viel Bedeutung haben.

Der allgemeine Begriff sagt Ihnen nicht, was seine Domäne ist. Es sagt Ihnen nur, was es im weitesten Sinne enthält .

Beispielsweise ist ein Objekt mit dem Namen info viel schwieriger zu lesen als userInfo . Außerdem können Sie im gesamten Code mehrere infos , controllers oder managers verwenden, sodass es einfacher ist, der Logik des Codes zu folgen, wenn diese Namen domänenspezifisch sind.

Methoden sollten mit Verben oder Verbausdrücken wie postPayment , deletePage oder insert benannt werden. Es ist gut, abgekürzte Namen zu vermeiden, da sie oft nur während der anfänglichen Entwicklung verwirrend und intuitiv sind.

Verwenden Sie stattdessen aussprechbare Namen . Diese sind beim Lesen und Verstehen von Code leichter zu verstehen und zu merken.

Ein schlechter Name ohne Kommentar ergibt keine Bedeutung:

Ein besserer selbsterklärender Name:

Wählen Sie einen Begriff pro Konzept und bleiben Sie dabei

Es ist verwirrend, fetch , retrieve und get als äquivalente Methoden verschiedener Klassen zu haben.

Die Verwendung unterschiedlicher Namen für dieselben oder ähnliche Dinge verwirrt den Leser und wirft Fragen auf, wie sich diese benannten Dinge unterscheiden, da sie unterschiedlich benannt sind.

Funktionen verbessern

Das Erste, was Sie tun können, um Funktionen zu verbessern, ist , sie klein zu machen . Wenn ich klein sage, meine ich unter 50 Zeilen, vorzugsweise unter 10-15. Indem Sie Funktionen klein halten, reduzieren Sie den Kontext , an den Sie beim Lesen denken müssen.

Einzüge in Funktionen erhöhen auch die Kontexterhaltung . Sie sollten eine gute Erklärung dafür haben, warum Sie 2 (oder mehr) Einrückungsebenen in einer Funktion haben.

Ersetzen Sie nach Möglichkeit verschachtelte Einrückungsblöcke durch Funktionsaufrufe .

Eine weniger eingerückte Version von `calculateEmployeeSalary` von oben:

Um die Lesbarkeit zu verbessern, sollte eine Funktion nur eines tun .

Lange Funktionen erledigen oft mehrere Dinge, was sie lang macht. Um mehrere Dinge zu tun, während der Code lesbar bleibt, sollten Sie lange Funktionen in mehrere kleinere Funktionen aufteilen.

Funktionen, die andere Funktionen aufrufen, sollten immer noch nur eine Sache tun, nur etwas abstrakter.

Führen Sie mehrere Abstraktionsebenen ein

Extrahieren Sie Codeschnipsel in Funktionen, wenn Sie Verhalten mit einer höheren Abstraktionsebene beschreiben können.

Viele Funktionen auf höherer Abstraktionsebene sind im Vergleich zu einigen wenigen Funktionen auf niedriger Ebene leichter zu verstehen. Abstraktionen sollten auf Domänenverhalten basieren.

Willkürliche Abstraktionen, die zum Zwecke der Wiederverwendung von Code eingeführt werden, machen die Dinge meistens noch schlimmer. Versuchen Sie , Funktionsaufrufe einer Funktion auf derselben Abstraktionsebene zu halten . Das erfordert manchmal einzeilige Funktionen, nur um die Abstraktion zu nivellieren, was in Ordnung ist (die Leistung sollte kein Problem sein, da moderne Sprachen durch Inlining dafür optimiert werden).

Durch die hierarchische Organisation von Funktionen nach Ebenen erzählt der Code eine Verhaltensgeschichte statt eines schrittweisen Rezepts.

Funktionen sollten so wenig Argumente wie möglich haben. Mehr als 2 sollten selten verwendet werden.

Viele Argumente erschweren das Verständnis des Codes , insbesondere wenn Argumente als Ausgabe verwendet werden (Argumente, die nach Abschluss der Funktion geändert werden).

Ausgabeargumente sind oft unerwartet und unnatürlich, also vermeiden Sie sie.

HINWEIS : Das vorherige Beispiel verwendet drei Argumente, um einem verwandten Beispiel so ähnlich wie möglich zu bleiben, sollte aber vermieden werden.

Eine Funktion sollte entweder ein Befehl sein, der eine Aktion ausführt (Nebenwirkung), oder eine Abfrage, die Daten an den Aufrufer zurückgibt , aber nicht beides.

Ihre „Rolle“ sollte durch ihren Namen betont werden. Es sollte niemals ein Missverständnis darüber geben, ob eine Funktion ein Befehl oder eine Abfrage nach ihrem Namen ist.

Nennen Sie ein Beispiel für einen Befehl, der seinen Argumentstatus ändert:

Namensbeispiel für eine Abfrage, die Werte extrahiert, ohne den Zustand der Argumente zu ändern:

Nebenwirkungen müssen immer explizit angegeben werden. Sie sollten niemals unerwartet sein. Wenn unnötig, vermeiden Sie sie. Versuchen Sie, Funktionen „rein“, dh zustandslos zu halten.

Bevorzugen Sie Unveränderlichkeit

Was bedeutet eigentlich „Unveränderlichkeit“ beim Programmieren?

Es ist eine Eigenschaft, die besagt, dass ein Objekt/eine Variable nicht geändert werden kann, nachdem es vollständig erstellt wurde . Unveränderlicher Code erstellt anstelle eines mutierenden Zustands eines Objekts eine Kopie mit einem modifizierten Zustand.

Veränderbarer Code:

Gleiches Verhalten in unveränderlichem Code:

Unveränderlichkeit macht Code einfacher und leichter zu verfolgen .

Da Sie wissen, dass ein Objekt unveränderlich ist, müssen Sie sich keine Sorgen machen, ob die Funktion, an die Sie das Objekt übergeben, es ändern wird. Gleichzeitige Programmierung wird auch einfacher und sauberer.

Unveränderliche Objekte sind von Natur aus Thread-sicher , was bedeutet, dass keine Synchronisierungsblöcke erforderlich sind, da Sie immer mit einem unveränderlichen konsistenten Zustand arbeiten.

Natürlich kann Logik manchmal auch unnatürlich, kompliziert oder leistungsschwach sein und dennoch als unveränderlich ausgedrückt werden. In diesem Fall können Sie immer noch Mutabilität verwenden, aber versuchen Sie, ihren Umfang so weit wie möglich zu reduzieren .

Bevorzugen Sie die deklarative Programmierung gegenüber dem Imperativ

Ein wichtiger Teil beim Schreiben von Code, den Ihre Erben leicht lesen können, ist die Wahl der deklarativen Programmierung gegenüber dem Imperativ . Was ist imperative und was deklarative Programmierung?

Imperative Programmierung sagt , wie Dinge zu tun sind . Der Schwerpunkt liegt auf Schritt-für-Schritt-Mechaniken wie Bedingungsanweisungen, Schleifen, Mutationen.

Deklarative Programmierung hingegen sagt, was zu tun ist, anstatt wie es zu tun ist . Der Fokus liegt auf dem, was getan werden muss, also ganze Handlungen statt einer schrittweisen Erklärung. Die deklarative Programmierung baut auf einer imperativen Schritt-für-Schritt-Programmierung auf.

Versuchen Sie, imperativen Code in declarative zu abstrahieren .

Mit dem deklarativen Stil wird die Geschäftslogik/-absicht viel offensichtlicher . Die Absicht des Codes auf den ersten Blick zu erkennen, macht die Wartung und Reparatur des Codes viel einfacher zu verstehen. Es ermöglicht uns, den problematischen Teil genau zu lokalisieren und dann in die Details einzutauchen, anstatt zuerst den gesamten Code zu lesen, nur um seine Absicht zu entschlüsseln.

Das Verhalten ist im deklarativen Stil offensichtlicher, wie unten zu sehen ist:

Keep it simple stupid – KISS

Entwickler finden oft „ kluge “ Lösungen für ein bestimmtes Problem.

Diese Lösungen sehen zwar in bestimmten Momenten wie ein Geschenk Gottes aus, sollten aber trotz der Ego-Boosts, die sie uns geben können, nicht bevorzugt werden. Meistens sind sie schwer zu verstehen und zu befolgen, und dadurch noch schwieriger zu warten oder zu aktualisieren.

Einfachheit sollte ein Hauptziel im Design sein . Vermeiden Sie unnötige Komplexität.

Wiederholen Sie sich nicht – DRY

Duplizieren ist schlecht, besonders Duplizieren von Wissen. Sie sollten darauf abzielen, eine einzige Repräsentation des Wissens in Ihrem System zu haben. Wenn sich Wissen ändert, müssen Sie es an jeder Stelle ändern, an der es dupliziert wird.

Wenn Sie ein Duplikat übersehen, besteht eine gute Chance , dass es sich in einen Fehler verwandelt .

Die Wartung wird mit vielen Duplikaten leicht zum Albtraum.

Es ist eine gängige Praxis, beim Prototyping zu kopieren und einzufügen, wenn man an einem komplexen Problem arbeitet. Daran ist nichts auszusetzen, da es uns hilft, die richtigen Wissensabstraktionen zu erkennen, was dazu führt, dass die Absicht besser ausgedrückt wird.

Aber so wie es uns als Kinder beigebracht wurde, ist es nach der Spielzeit Zeit aufzuräumen. Diese Duplikate erfüllten ihren Zweck. Jetzt ist es Zeit, sie zu entfernen.

Du wirst es nicht brauchen – YAGNI

Implementieren Sie nicht etwas, das Sie in Zukunft „möglicherweise“ benötigen, implementieren Sie nur das, was Sie benötigen . Was wir sehr oft brauchen „können“, ist nicht das, was wir später wirklich brauchen.

Die vorzeitige Umsetzung von Dingen, die wir „vielleicht“ brauchen, verschließt viele Wege, in die unser Design gehen könnte. Es zwingt uns zu einem Design, das nicht für die tatsächlichen Bedürfnisse unseres Produkts optimiert ist , was uns wiederum zu einem schlechteren und komplexeren Gesamtdesign führt, das schwer zu warten ist.

Nächste Schritte

Die nächsten Schritte, die Sie unternehmen können, um sich zu verbessern, sind SOLID-Prinzipien .

Es sind fünf Designprinzipien , die Softwaredesigns noch verständlicher, flexibler und wartbarer machen sollen:

  1. Grundsatz der Einzelverantwortung – SRP
  2. Auf-Zu-Prinzip – OCP
  3. Substitutionsprinzip von Liskov – LSP
  4. Prinzip der Schnittstellentrennung – ISP
  5. Abhängigkeitsinversionsprinzip – DIP

Indem wir diese (neben einigen anderen) Prinzipien befolgt haben, ist es uns gelungen, den größten Teil des Code- und Domänendesigns lesbar, leicht zu warten und erweiterbar zu halten .

Der Grund, warum wir das getan haben, ist, uns auf die Aufnahme neuer Mitglieder unseres Teams vorzubereiten, in erster Linie, indem wir den Onboarding-Prozess effizienter und so angenehm wie möglich gestalten.

Apropos neue Mitglieder, sehen Sie sich unsere offene Stelle für Senior Java Developer oder andere Stellenangebote auf unserer Karriereseite an.