Unerwartete schlechte Praktiken

24.08.2022
Libor Huspenina

​​​Einige Programmierpraktiken sind uns so vertraut, dass wir sie automatisch und ohne großes Nachdenken anwenden. Manchmal sind diese Techniken veraltet, manchmal werden sie im falschen Kontext angewendet. Das Ansprechen solch schlecht eingespielter Gewohnheiten stößt oft auf Ablehnung. Vor allem von denjenigen, die sie nutzen und das Thema als nützlich empfinden, also lasst uns genau das tun!

Marks

Programmier-IDEs erkennen oft bestimmte Arten von Kommentaren, um die Navigation in der Codebasis zu erleichtern. Die FIXME-Funktion von Xcode lässt andere Entwickler wissen, dass ein Teil des Codes mehr Aufmerksamkeit verdient. TODO ist hilfreich, wenn etwas, nun ja, erledigt werden soll. MARK unterscheidet sich von den vorherigen Fällen; es dient der Dokumentation. Die gleiche Funktion wird in IntelliJ IDEA/Android Studio als Region bezeichnet.

Markierungen unterteilen den Quellcode in mehrere Teile, indem sie Überschriften verwenden. Das kann dazu führen, dass der Code in logische Einheiten unterteilt erscheint. Wenn Sie ein Leser sind, der mit der früheren Objective-C-Ära der iOS-Entwicklung vertraut ist, wissen Sie, dass dies nur eine aktualisierte #pragma mark-Anweisung ist.

Typisch ist die Verwendung in Dateien mit einer großen Anzahl von Zeilen. Zeichen schaffen die Illusion von Klarheit, indem sie in vermeintlich zusammengehörende Teile zerlegt werden.

Die Verwendung von Zeichen in solchen Fällen ist eine schlechte Praxis. Entwickler missbrauchen sie oft, um eine zu große Datei zu rechtfertigen. Man sollte sich nicht auf Xcode verlassen, um den Code verständlich und lesbar zu machen. Kleine und gut komponierte Klassen sind ohne IDE-Funktionen einfacher zu verstehen und zu navigieren. Vor allem für die Überprüfer von Pull-Anfragen, die die Weboberfläche benutzen, wo diese Funktionen nicht vorhanden sind.

Erweiterungen

Moderne Programmiersprachen wie Kotlin oder Swift ermöglichen die Erweiterung von Klassen, Schnittstellen/Protokollen, Structs oder Enums, um eine zusätzliche Implementierung bereitzustellen. Sie können Ihren Code in mehrere Teile aufteilen, indem Sie Erweiterungen verwenden, um festzulegen, was näher zusammengehört. Eine andere Möglichkeit besteht darin, eine Erweiterung für einen anderen Typ zu erstellen, den Sie vielleicht nicht einmal besitzen, um seine Verwendung intuitiver zu gestalten. Die Möglichkeiten sind nahezu grenzenlos. Das ist nicht immer eine gute Sache, aber zunächst ein Blick in die Geschichte. 

Erweiterungen gab es auch schon in Objective-C. Wenn Sie keine Erfahrung mit der Programmierung in einer solchen Sprache haben und den Namen für Erweiterungen erraten müssten, würden Sie wahrscheinlich überrascht sein. Es sind Kategorien! Eine weitere Überraschung ist, dass es auch in Objective-C Erweiterungen gibt, die jedoch anderen Zwecken dienen. Interessant ist der Unterschied zwischen den beiden Sprachen. Kategorien in Objective-C zwangen den Entwickler, sich den Namen auszudenken. Aus diesem Grund werden Dateien mit dem Namen Class+CategoryName.swift oft auch für Swift-Erweiterungen verwendet. Und noch wichtiger: Um Kategorien zu verwenden, mussten sie explizit importiert werden.

Erweiterungen in Swift sind ein unbenannter Teil des Codes. Ein solcher Code kann für den Leser komplizierter zu verstehen sein. Wenn es mehrere Erweiterungen desselben Typs gibt, kann das Hinzufügen eines Namens zum Code und die Einbettung in einen Typ die Lesbarkeit erheblich verbessern.

Die unsachgemäße Erweiterung von weit verbreiteten Typen führt zu einer Verschmutzung des Namensraums. Es ist wichtig, sich vor der Erstellung von Erweiterungen zu fragen, ob alle Instanzen des Typs eine solche Fähigkeit haben sollen. Sollten alle UIViews Zugang zu einer Blinkmethode haben? Macht eine bestimmte Unterklasse von UIView mehr Sinn?

Einige Entwickler verwenden Erweiterungen, um die Implementierung mehrerer Protokolle aufzuschlüsseln, was ebenfalls ein Warnzeichen sein kann. Wenn eine Klasse viele Protokolle implementiert, ist es vielleicht an der Zeit, sie in kleinere Klassen aufzuteilen.

Für Trolle da draußen: Sie können Ihre Kollegen wütend machen, indem Sie UIView mit translatesAutoresizingMasksIntoConstraints erweitern und beobachten, wie sie es mit translatesAutoresizingMaskIntoConstraints vergleichen.

Aber tun Sie es nicht.

Kommentare

Die Möglichkeit, Kommentare zu schreiben, könnte undisziplinierte Programmierer dazu verleiten, einen Code von schlechter Qualität zu erstellen. Leider ist es einfacher, eine Variable nicht zu benennen und mit einem komplizierten, aber nicht so klaren Kommentar zu beschreiben, was in meinem Kopf vor sich geht. Leichtigkeit sollte nicht unser Ziel sein. Knappheit und Klarheit sollten es sein.

Ein guter Kommentar für einen schlecht geschriebenen Code ist immer noch ein Codegeruch. Nehmen Sie nicht nur mich beim Wort. Robert Martin erklärt: „Ein Kommentar ist ein Versäumnis, sich in einem Code auszudrücken. Wenn Sie scheitern, dann schreiben Sie einen Kommentar; aber versuchen Sie nicht zu scheitern.“

Ein weiterer Grund ist, dass sich das Verhalten des Codes ändern kann, wenn er im Repository lebt, geändert und überarbeitet wird, und dass sein Name dies überall, wo er aufgerufen wird, zum Ausdruck bringen kann. Der Kommentar wird jedoch selten aktualisiert und kann mehr verwirren als helfen.

Dokumentationskommentare erfüllen ihren Zweck sehr gut, wenn Sie eine API entwerfen, die andere verwenden sollen. Denken Sie daran, dass die API für sich selbst stehen muss, und dass Klarheit Vorrang hat. Benutzen Sie die Kommentare in der Dokumentation nicht als Entschuldigung für ein miserables Design.

Struktur

Die Struktur eines Projekts ist eines der ersten Dinge, die man sieht, wenn man sich eine Codebasis ansieht, und sie sollte auf den ersten Blick den Zweck der Anwendung umreißen. Es ist jedoch keine Ausnahme, dass einige Projekte Ordnerstrukturen haben, die von den Schichten der Architektur inspiriert sind, z. B. View, ViewModel, Model.

Eine Projektstruktur auf der Grundlage von Architekturschichten ist eine schlechte Praxis. Dies macht die Wiederverwendbarkeit praktisch unmöglich. Die Navigation durch eine solche Struktur ist unnötig kompliziert und wird mit zunehmendem Umfang immer schwieriger zu pflegen. Sie ist nicht skalierbar. Von der Architektur inspirierte Ordner könnten ihren Platz haben, nicht nur auf der obersten Ebene. Es sollte nicht das erste sein, was Sie sehen.


Überzeugen Sie sich selbst, welche Struktur Ihnen mehr über die Anwendung verrät

Abhängigkeiten

Open Source bietet viele Bibliotheken, die das Leben vereinfachen, von UI-Komponenten über Netzwerke bis hin zu Lösungen für die Injektion von Abhängigkeiten. Dies kann oft viel Zeit und Mühe sparen. Andererseits birgt dies verschiedene Gefahren und Einschränkungen; die Verwendung von Bibliotheken Dritter erfordert Disziplin, Ordnung und Ausgewogenheit.

Zufällig verstreute Abhängigkeiten von Dritten verringern die Robustheit erheblich. Die Abschirmung des Kerns der Anwendung und die Verwendung der Bibliotheken von den äußeren Teilen der Architektur helfen, das Risiko zu mindern. Die Abstraktion erleichtert den Prozess des Ersetzens einer Bibliothek durch eine andere.

Es ist in Ordnung, Abhängigkeiten von Drittanbietern zu verwenden, aber mit Vorsicht. Fragen Sie sich selbst: Wie viel Zeit werde ich dadurch sparen? Wie hoch ist der Aufwand für den Ersatz? Kann ich genügend Schutzmechanismen installieren, um die Anwendung zu schützen? 

Der Königsweg zum Schutz Ihrer Anwendung, auch wenn dies manchmal schwierig oder unpraktisch ist, besteht darin, die Abhängigkeit nur an einer Stelle zu importieren.

Wir hatten bereits das Vergnügen, mehrere Anwendungen zu übernehmen, die aufgrund dieses Problems nicht mehr zu warten waren. Ohne Abstraktion, nicht mehr unterstützte (oder Closed-Source-) Bibliotheken haben die Codebase zersetzt. Externe Abhängigkeiten sollten Ihr Produkt niemals als Geisel halten.

Tests

Testgetriebene Entwicklung ist die gute Kinderstube eines Programmierers, eine Disziplin mit vielen Vorteilen. Die technischen Auswirkungen sind ein eigener Blogbeitrag, wenn nicht sogar eine ganze Serie. Nicht-technische Auswirkungen wie etwa die einfache Einarbeitung neuer Teammitglieder und eine ausführbare Dokumentation, die nicht veralten kann, sprechen für sich.

Dennoch werden sie oft vernachlässigt. Das völlige Fehlen von Tests ist der offensichtlich erste und häufigste Verstoß, gefolgt vom Schreiben von Tests nach dem Produktionscode, was alle Vorteile abschwächt und andere Hindernisse mit sich bringt.

Sie müssen zuerst Unit-Tests schreiben - vor dem Produktionscode. Wenn Sie zuerst testen, verhindern Sie, dass Sie einen zu komplexen Code erstellen. Er führt Sie durch den Bau von Bauteilen in der richtigen Größe. Die großen Klassen sind schwierig zu testen, und die Tests werden Sie anweisen, sie in kleinere Klassen zu zerlegen.

Tests, die nach dem Produktionscode geschrieben werden, sind von Natur aus von geringerer Qualität und können sogar irreführend sein. Solange Sie den Produktionscode nicht als Beweis für den ersten fehlgeschlagenen Test schreiben, ist es unmöglich zu sagen, ob die Tests das behaupten, was sie deklarieren. Es ist dann fraglich, wie gut solche Tests das zu prüfende System schützen.

Wenn Sie Tests erst nach der Implementierung schreiben, kann es sein, dass eine Komponente schwierig zu testen ist, was bei einem Test-first-Ansatz unmöglich ist. Sie können keinen untestbaren Code erstellen!

Der Teufel steckt im Detail

Selbst das Alltägliche kann schädlich sein, wenn wir etwas zu automatisch und mit weniger Aufmerksamkeit tun. Stellen Sie das Gewöhnliche in Frage und suchen Sie nach schlechten Praktiken, die Sie nicht erwarten würden.​​

#development