In diesem Artikel werden 9 Erfahrungswerte aus der Praxis aufgeführt, die wirklich jeden Programmierer angehen.

Legostein um Legostein zum großen System

Beginnt man mit der praktischen Arbeit, dann geht es nicht mehr um kleine, überschaubare Systeme. Man hantiert dann mit einer unübersichtlichen Anzahl an Servern, Dateien und Quellcodezeilen. Wie kann eine moderne Software mit Millionen von Codezeilen überhaupt funktionieren?

Die System-Wissenschaft hat eine Antwort auf diese Frage im Gall´schen Gesetz gefunden: Ein funktionierendes komplexes System entwickelte sich aus funktionierenden einfacheren Systemen. Wenn du ein funktionierendes komplexes System bauen willst, baue erst ein einfacheres System und erweitere es mit der Zeit.

Eine Erschaffung eines komplexen, modernen Softwaresystems aus dem Nichts in einem Schritt ist also nicht möglich.

Und wie geht man nun im Einzelnen vor? Am besten funktioniert es mit der Teile-und-Herrsche Strategie. Teile das Gesamtsystem, also das Gesamtproblem, in kleinere Systeme/Probleme auf. Diese kann man dann rekursiv mit derselben Strategie behandeln. Ist ein System/Problem klein genug, kann man dafür einen funktionierenden Baustein, einen Legostein, entwickeln.

Aus den einzelnen Bausteinen, die einem mit der Zeit zur Verfügung stehen, kann man dann immer größere Systeme bauen, um größere Probleme zu lösen.

Das Schöne daran ist nun, dass ein Baustein sein Problem in der Art kapselt, dass der Verwender des Bausteins nur seine Schnittstelle kennen muss. Die Implementation der Problemlösung wird abstrahiert.

Mit der Zeit baut man sich so eine Anwendungsgebiet-spezifische Bauteilkollektion. Dann kann man seine Probleme mit einem angemessenen Vokabular lösen.

Technik und Wissenschaft im Allgemeinen funktionieren auch so. Man baut auf kleinen Lösungen auf und löst immer komplexere Probleme.

Schön ist es, wenn man die Bauteile so allgemein gestalten kann, dass Wiederverwendung möglich wird: entweder in demselben System oder als Lösung für eine ganze Branche.

So ein Baustein kann vielfältige Formen annehmen: eine Bibliothek, eine Klasse, einen Mikroservice, eine SaaS-Dienstleistung usw.

Baue zunächst Legosteine und dann daraus erst das Gesamtsystem.

Investiere in eigene Werkzeuge

Der Mensch ist ein werkzeugschaffendes Tier. Nur wenige andere Tierarten auf diesem Planeten verwenden Werkzeuge. Das Bauen von Werkzeugen hat unsere Zivilisation erst möglich gemacht.

Investiert man in der Praxis Entwicklungszeit in gute Tools, dann hebelt das die eigene Arbeitskraft und Arbeitszeit. Die Investition amortisiert sich bald.

Gerade manuelle, repetitive Arbeitsvorgänge sind sehr ermüdend und dazu noch eine große Fehlerquelle. Automatisiert man sie mit dem richtigen Programm, kann man daraus guten Gewinn schöpfen.

Und seien wir mal ehrlich: Das Programmieren von Tools, die uns ermüdende Arbeit abnehmen, ist äußerst befriedigend.

Sei ein Mensch und baue dir Werkzeuge, so dass du mehr hebeln kannst.

Refaktoriere in kleinen Schritten

Die übliche Literatur zum Refaktorieren propagiert, eine saubere Testabdeckung herzustellen, bevor man beginnt. In der Praxis sieht es leider anders aus.

In wie vielen Projekten habe ich schon gearbeitet, in denen aus Budgetbeschränkungen und Entwicklerzeitmangel das automatisierte Testen hintenangestellt wurde?

Wie geht man also vor, wenn man ein System refaktorisieren darf oder muss, für das es gar keine oder keine ausreichende Testabdeckung gibt?

Man geht das Umstellen des Codes langsam und gezielt an, wie eine Operation am Herzen. Es werden kleine Schritte unter Versionskontrolle gemacht, die dann abgeschlossen und einzeln getestet werden.

So tastet man sich von funktionierender Version zu funktionierender Version.

Große, augenblickliche Änderungen sind nicht überschaubar, nicht testbar und laden Fehler geradezu ein.

Die globale Strategie ist also, Teile des Systems zu isolieren und dann erst zu verbessern.

Refaktoriere wie eine Serie von Operationen am offenen Herzen.

Nahtstellen im Code

An den Stellen im Code, an denen die einzelnen Bausteine zusammenkommen, bilden sich die Nahtstellen des Programms. An diesen kann man das System auftrennen und die Einzelteile separat testen oder verändern.

Ist die Strukturierung ordentlich gemacht, kann man den Rest des Systems optimalerweise dabei ignorieren. Das ist der Vorteil von sauberer Kapselung.

Zusammengefügte kleinere Bausteine bilden natürliche Nähte per Konstruktion.

Warten, Testen und Refaktorisieren eines gut strukturierten Systems dieser Art ist viel einfacher möglich.

Baue bewusst Nahtstellen in das zu entwickelnde System ein, so dass du von diesen Vorteilen profitieren kannst.

Nur ausprobierter Code ist verlässlich

Wie oft geht man beim Programmieren eines Codestücks davon aus, dass die erträumte Lösung sofort funktioniert? Die Praxis sieht leider anders aus. Meist hat man Randfälle nicht bedacht oder die Schnittstelle sieht doch anders aus als in der Erinnerung.

Vertraue nur ausprobiertem Code. Die ersten Testläufe bringen die Wahrheit ans Tageslicht. Ungeprüfter Code sollte stets als fehlerhaft angesehen werden.

Auch vor False-Positives muss man sich in Acht nehmen: Code, der anscheinend funktioniert, in Wirklichkeit aber gar nicht ausgeführt wird. Besonders bei Tests sollte man sich immer an die übliche Reihenfolge von rot – grün – Refaktorierung halten. Insbesondere der Check, dass der initiale Test fehlschlägt, ist wichtig, so wird er also zumindest ausgeführt.

Teste deinen Code, ehe du ihm vertraust.

Benennung ist mühsam, aber wertvoll

Benennung ist eine anstrengende Sache. Einen guten Namen für einen Codebestandteil zu finden, der dessen Zweck ausreichend ausdrückt, ist nicht immer ganz einfach. Bedenke, wie viel Zeit sich Eltern für die Benennung ihres ersten Kindes nehmen. Geschenkt –das ist wichtiger. Aber dieser Vergleich gibt einen gewissen Eindruck meiner Meinung, dass Benennung wirklich essentiell für gute Programmierung ist.

Gute Namen transportieren viel: Informationen über den intendierten Algorithmus, die Aufteilung der Probleme, eine klare Datenstruktur etc.

Gute Namen sind unverzichtbar, insbesondere wenn Dritte den Code warten sollen.

Investiere anständig Zeit in die Namensfindung, das ist gut angelegte Zeit, die Früchte tragen wird.

Es gibt keinen perfekten Code

Als professioneller Programmierer strebt man nach gutem Code. Perfekten Code dagegen gibt es nicht. Im Geschäftsalltag ändern sich die Anforderungen an die Software meist so schnell, dass man mit der Programmierung so schon nicht hinterherkommt. Wenn man nun auch noch einem Perfektionierungsdrang nachkommen muss, dann kommt man in Teufels Küche.

Was nutzt hochfrisierter Code für Problem A, wenn die Unternehmung nun vor Problem B steht und dieses lösen muss?

Im Alltag ist Pragmatismus gefragt. Ist der Nutzen einer weiteren Refaktorierungsrunde wirklich so groß, dass der Aufwand gerechtfertigt ist? Was ist der Return-on-Investment der letzten Refaktorierungsstunde? Behalte immer den Grenznutzen im Auge.

Sei pragmatisch und denke auch wirtschaftlich bei der Allokation deiner Programmierzeit.

Gutes Logging ist Gold wert

Wie oft steht man in der Praxis vor dem Problem, einen Bug nachvollziehen zu müssen? Erfreulich sind dann gut gestaltete Logs, die den Programmablauf protokollieren und jeglichen Fehlerfall nachzeichnen.

Das gilt in der Produktiv-Umgebung, aber auch beim Debuggen beim Programmieren selbst kann das Protokoll des Programmablaufs helfen, zeigt es doch genau, wo und wann ein Fehler auftritt und der Ablauf vom Normalverlauf abweicht.

Investiere angemessen Zeit in das Logging des Programmablaufs.

Metriken statt Tests

Hat man keine vernünftige Testabdeckung, vielleicht aus wirtschaftlichen Gründen, so hilft es, eine weniger kostenintensive Technik zu nutzen: Metriken. An den rechten Stellen im Programm hält man Ereignisse fest, die sich dann zu Metriken aggregieren. Datenbank-Abfrage-Zeiten, Logins, Pageviews etc. sind Beispiele für solche Metriken.

Ändert sich eine Metrik in der ausgelieferten Applikation plötzlich, so weist das stark auf einen Bug hin. Die Metriken helfen also, Bugs zu detektieren, kann man ihnen dann doch nachgehen.

Metriken bieten eine gangbare, pragmatische Alternative zu einer aufwendigen Testabdeckung der gesamten Software.

 

Titelbild: Gratisography.com