The Pragmatic Programmer ist ein geschickter und praktischer Leitfaden, der Ihnen Einblicke und Grundlagen vermittelt, um ein guter Programmierer zu sein, und der Ihre Spezialisierung und die technischen Aspekte der modernen Softwareentwicklung erweitert. Das Buch wurde im Oktober 1999 veröffentlicht. Andrew Hunt behandelt Themen wie persönliche Verantwortung und Karriereentwicklung bis hin zu architektonischen Techniken, mit denen Sie Ihren Code flexibel halten und leicht anpassen und wiederverwenden können. In dem Buch lernen Sie, wie Sie Softwarefäulnis bekämpfen, die Falle der Wissensverdoppelung vermeiden und Ihre Entwicklungen durch Automatisierung präziser gestalten können.
WIE DIESES BUCH UNS GEHOLFEN HAT?
Dieses Buch hat uns geholfen zu verstehen, wie Entwickler bessere Programmierer werden können, indem sie die wichtigsten Grundlagen verstehen, die für den Erfolg der Softwareentwicklung erforderlich sind. Es zeigt, dass es bei einem guten Programmierer nicht nur um technische Fähigkeiten geht. Es konzentriert sich auf praktische Themen wie Entkopplung, Power Editing, Debugging und Testen, die uns helfen, Entwicklern dabei zu helfen, besseren Code für unsere Kunden zu erstellen und Berater und Mitglieder großer Projektteams zu werden. Das Buch hat uns geholfen zu verstehen, wie wir unsere Erfahrungen nutzen können, um sowohl in unserem beruflichen als auch in unserem privaten Leben fundiertere Entscheidungen zu treffen.
DAS BUCH IN WENIGER ALS 60 SEKUNDEN ERKLÄRT
Das Buch verwendet Analogien und kurze Geschichten, um Entwicklungsmethoden und Warnungen vorzustellen, z.B. die Broken-Windows-Theorie, die Geschichte von der Steinsuppe oder dem kochenden Frosch. Außerdem enthält es kleine Übungen, mit denen Sie Ihre Programmierkenntnisse trainieren können.
Der pragmatische Programmierer erklärt, dass sich Softwarefehler auf verschiedene Weise manifestieren, von missverstandenen Anforderungen bis hin zu Programmierfehlern. Leider sind moderne Computersysteme immer noch darauf beschränkt, das zu tun, was Sie ihnen sagen, und nicht das, was Sie von ihnen wollen.
Dem Buch zufolge schreibt niemand perfekte Software. Es ist also klar, dass die Fehlersuche einen großen Teil Ihres Tages in Anspruch nehmen wird. Die Fehlersuche ist für viele Entwickler ein sensibles, emotionales Thema.
DIE DREI BESTEN ZITATE
„Werkzeuge verstärken Ihr Talent. Je besser Ihre Werkzeuge sind und je besser Sie sie zu nutzen wissen, desto produktiver können Sie sein.“
„Die größte aller Schwächen ist die Angst, schwach zu erscheinen“.
„Der Editor wird zu einer Verlängerung Ihrer Hand; die Tasten singen, während sie sich durch Text und Gedanken schneiden.
BUCHZUSAMMENFASSUNG UND ANMERKUNGEN
Kapitel Eins: Eine pragmatische Philosophie
- Die Katze hat meinen Quellcode gefressen
Übernehmen Sie Verantwortung: Verantwortung ist etwas, dem Sie aktiv zustimmen; Sie setzen sich dafür ein, dass etwas richtig ausgeführt wird. Allerdings haben Sie nicht unbedingt die direkte Kontrolle über jeden Aspekt. Sie haben das Recht, die Verantwortung für eine unvorstellbare oder mit hohen Risiken behaftete Situation nicht zu übernehmen. Wenn Sie die Verantwortung für eine Situation übernehmen, sollten Sie die Demut haben, dafür zur Rechenschaft gezogen zu werden. Jeder macht Fehler. Wenn Sie also einen Fehler machen, geben Sie ihn ehrlich zu und versuchen Sie, Alternativen zu finden.
- Software-Entropie
Ein zerbrochenes Fenster, das über einen längeren Zeitraum nicht repariert wurde, vermittelt den Bewohnern des Hauses das Gefühl, dass sie sich selbst überlassen sind, was schließlich zu weiteren Schäden und Ruinen führt. Das Gleiche gilt für Software. Ein mieser Code kann zu noch mehr Schaden führen, wenn er nicht bald repariert wird. Leben Sie daher nicht mit destruktivem Code. Reparieren Sie ihn, sobald ein Fehler entdeckt wird. Ergreifen Sie Maßnahmen und zeigen Sie Initiative, um weiteren Schaden zu verhindern und um zu zeigen, dass Sie die Situation im Griff haben.
- Ausreichend gute Software
Gut genug bedeutet nicht, dass der Code schlampig oder schlecht produziert ist. Alle Systeme müssen die Anforderungen ihrer Benutzer erfüllen, um erfolgreich zu sein. Sie können sich selbst dazu verpflichten, Software zu schreiben, die gut genug ist – für Ihre Benutzer, für künftige Betreuer und für Ihren Seelenfrieden. Wenn Sie Software schreiben, die gut genug ist, werden Sie produktiver und Ihre Benutzer zufriedener. Die Erstellung von ausreichend gutem Code setzt einfach voraus, dass die Benutzer mitentscheiden können, ob das, was Sie produziert haben, gut genug ist.
Wenn Sie einen ausreichend guten Code entwickeln, sollten Sie wissen, wann Sie aufhören müssen. Ruinieren Sie ein hervorragendes Programm nicht durch übermäßige Ausschmückung und Verfeinerung. Machen Sie weiter und lassen Sie Ihren Code eine Zeit lang für sich stehen. Er mag nicht perfekt sein. Machen Sie sich keine Sorgen: Er könnte nie perfekt sein. Gute Software jetzt ist besser als ideale Software in einem Jahr.
Mein Lieblingszitat aus dem Kapitel: „ Tolle Software heute ist oft besser als perfekte Software morgen.“
Kapitel Zwei: Ein pragmatischer Ansatz
- Das Übel der Duplikation
Als Programmierer sammeln, organisieren, pflegen und nutzen Sie Wissen. Unglücklicherweise ist das Lernen nicht konstant oder fest und ändert sich schnell. Ihr Verständnis einer Anforderung kann sich nach einem Treffen mit dem Kunden ändern. Das bedeutet, dass Sie den größten Teil Ihrer Zeit damit verbringen, den Code täglich zu pflegen. Die meisten Leute denken, dass die Wartung mit dem Start einer Anwendung beginnt, dass Wartung bedeutet, Fehler zu beheben und Funktionen zu verbessern. Diese Leute irren sich. Die Wartung ist keine separate Aktivität, sondern ein Teil des Entwicklungsprozesses, denn neue Anforderungen kommen hinzu, während Sie den Code entwerfen.
Die einzige Möglichkeit, Software zuverlässig zu entwickeln und Ihre Entwicklungen leichter verständlich und wartbar zu machen, besteht darin, dem DRY-Prinzip (Don’t Repeat Yourself) zu folgen: Jedes Wissen muss in einem System eine einzige, eindeutige und verbindliche Darstellung haben.
Die meisten der Duplikate, die wir sehen, fallen in eine der folgenden Kategorien:
- Aufgezwungene Vervielfältigung. Sie haben das Gefühl, dass Sie keine andere Wahl haben – die Umwelt scheint eine Replikation zu verlangen.
- Unbeabsichtigte Duplikation. Sie sind sich nicht bewusst, dass Sie Informationen duplizieren.
- Ungeduldige Vervielfältigung. Man wird faul und dupliziert, weil es einfacher erscheint.
- Duplizierung zwischen Entwicklern. Mehrere Personen in einem Team duplizieren eine Information und merken es nicht.
- Umkehrbarkeit.
Die Änderungen müssen nicht extrem sein oder sogar sofort erfolgen. Aber wenn die Zeit vergeht und Ihr Projekt voranschreitet, kann es sein, dass Sie sich in einer unhaltbaren Lage befinden. Das Projektteam legt sich bei kritischen Entscheidungen auf ein kleineres Ziel fest und schafft so eine Version der Realität mit weniger Optionen. Das Problem ist, dass kritische Entscheidungen nicht leicht rückgängig zu machen sind.
Nichts ist für immer, und wenn Sie sich auf eine Tatsache verlassen, können Sie fast sicher sein, dass sie sich ändern wird. Planen Sie die Umkehrbarkeit ein, denn keine Entscheidung ist endgültig. Streben Sie nach flexiblem Code, Architektur und Anbieterintegration.
Wenn Sie sich an Empfehlungen wie das DRY-Prinzip, die Entkopplung und die Verwendung von Metadaten halten, müssen Sie nicht viele kritische und unumkehrbare Entscheidungen treffen.
- Leuchtspurgeschosse
Tracer-Bullets helfen Ihnen, Ihr Ziel unter realen Bedingungen schnell zu finden und geben Ihnen und Ihren Benutzern Feedback. Ein Tracer-Code könnte eine einzelne Funktion sein, die durchgängig auf allen Ebenen implementiert ist. Ein Tracer-Code führt schnell zum Ziel und liefert sofortiges Feedback. Und vom praktischen Standpunkt aus betrachtet, sind sie eine relativ billige Lösung. Um denselben Effekt im Code zu erzielen, suchen wir nach etwas, das uns schnell, sichtbar und wiederholt von einer Anforderung zu einem Aspekt des endgültigen Systems bringt.
Vorteile
- Sie haben eine Integrationsplattform.
- Sie haben ein besseres Gefühl für den Fortschritt.
- Die Benutzer können frühzeitig sehen, dass etwas funktioniert.
- Helfen Sie beim Aufbau einer Struktur, in der Sie arbeiten können.
Tracer Code versus Prototyping
Sie könnten denken, dass das Konzept des Tracer Codes nichts anderes als Prototyping ist; es gibt einen Unterschied. Beim Prototyping geht es darum, bestimmte Aspekte des endgültigen Systems zu erforschen. Bei einem echten Prototyp werfen Sie alles weg, was Sie beim Ausprobieren des Konzepts zusammengezimmert haben, und programmieren es anhand der gewonnenen Erkenntnisse neu. Der Tracer-Code-Ansatz befasst sich mit einem anderen Problem. Sie müssen wissen, wie die Anwendung als Ganzes zusammenhängt. Sie wollen Ihren Benutzern zeigen, wie die Interaktionen in der Praxis funktionieren und Ihren Entwicklern ein architektonisches Gerüst an die Hand geben, an dem sie den Code aufhängen können.
Lieblingszitat des Kapitels: „Nichts ist gefährlicher als eine Idee, wenn es die einzige ist, die man hat.“
Kapitel 3: Die grundlegenden Werkzeuge
- Die Macht des einfachen Textes
Als Pragmatischer Programmierer ist Ihr Basismaterial Wissen. Sie sammeln Anforderungen wie Wissen und drücken es dann in Ihren Entwürfen, Implementierungen, Tests und Dokumenten aus. Das beste Format für die Speicherung von Wissen ist einfacher Text.
Was ist Klartext?
Der einfache Text besteht aus druckbaren Zeichen, so dass Menschen ihn direkt lesen und verstehen können. Mit einfachem Text geben Sie sich die Möglichkeit, Wissen sowohl manuell als auch programmgesteuert mit praktisch jedem Ihnen zur Verfügung stehenden Werkzeug zu manipulieren.
- Muschelspiele
Jeder Holzarbeiter braucht eine geeignete, solide und zuverlässige Werkbank, auf der er seine Werkstücke in bequemer Höhe aufbewahren kann, während er sie bearbeitet.
Als Programmierer, der mit Textdateien arbeitet, ist Ihre Werkbank die Befehlsshell. Über die Shell-Eingabeaufforderung können Sie Ihr gesamtes Repertoire an Tools aufrufen. Mit der Shell-Eingabeaufforderung können Sie Anwendungen, Debugger, Browser, Editoren und Dienstprogramme starten. Sie können nach Dateien suchen, den Status des Systems abfragen und Ausgaben filtern. Durch die Programmierung der Shell können Sie Multiplex-Makrobefehle für Aktivitäten erstellen, die Sie häufig ausführen.
Shell-Dienstprogramme und Windows-Systeme
Obwohl sich die von Windows-Systemen bereitgestellten Befehlsshells allmählich verbessern, sind die Windows-Befehlszeilen-Dienstprogramme ihren Linux/Unix-Pendants immer noch unterlegen. Es ist jedoch noch nicht alles verloren.
Cygnus Solutions bietet ein Paket namens Cygwin an. Cygwin bietet nicht nur eine Linux/Unix-Kompatibilitätsschicht für Windows, sondern enthält auch eine Sammlung von mehr als 120 Unix-Dienstprogrammen, darunter beliebte Programme wie 1s, grep und find. Die Dienstprogramme und Bibliotheken können kostenlos heruntergeladen und verwendet werden, aber lesen Sie die Lizenz. Die Cygwin-Distribution wird mit der Bash-Shell geliefert.
- Power Editing
Werkzeuge sind eine Verlängerung Ihrer Hand. Das gilt für Editoren mehr als für jedes andere Softwaretool. Wählen Sie einen Editor, mit dem Sie Text so mühelos wie möglich bearbeiten können, denn der Text ist das wichtigste Rohmaterial für die Programmierung.
Ein Redakteur
Es ist besser, einen Editor sehr gut zu kennen und ihn für alle Bearbeitungsaufgaben zu verwenden: Code, Dokumentation, Memos, Systemverwaltung und so weiter. Bei mehreren Editoren droht Ihnen ein modernes Chaos der Verwirrung. Sie müssen vielleicht den integrierten Editor in der IDE jeder Sprache für die Programmierung verwenden, ein All-in-One-Office-Produkt für die Dokumentation und vielleicht einen anderen integrierten Editor für das Versenden von E-Mails. Selbst die Tastenkombinationen, mit denen Sie Befehlszeilen in der Shell bearbeiten, können sich unterscheiden. Es ist schwierig, sich in jeder dieser Umgebungen zurechtzufinden, wenn Sie in jeder einen anderen Satz von Bearbeitungskonventionen und Befehlen verwenden.
Wählen Sie daher einen einzigen Editor, kennen Sie ihn genau und verwenden Sie ihn für alle Bearbeitungsaufgaben. Verwenden Sie einen einzigen Editor oder eine Reihe von Tastenkombinationen für alle Textbearbeitungsvorgänge. Sie müssen nicht mehr innehalten und nachdenken, um einen Text zu bearbeiten: Die notwendigen Tastenanschläge werden ein Reflex sein. Der Editor wird zu einer Verlängerung Ihrer Hand, die Tasten werden singen, wenn sie sich ihren Weg durch Text und Gedanken bahnen. Stellen Sie sicher, dass der von Ihnen gewählte Editor auf allen von Ihnen verwendeten Plattformen verfügbar ist. Emacs, vi, CRiSP, Brief und andere sind auf mehreren Plattformen verfügbar, oft in GUI- und Nicht-GUI-Versionen (Textbildschirm).
Editor-Funktionen:
- Konfigurierbar
- Erweiterbar
- Programmierbar
Mein Lieblingszitat aus dem Kapitel: „Werkzeuge verstärken Ihr Talent. Je besser Ihre Werkzeuge sind und je besser Sie wissen, wie man sie benutzt, desto produktiver können Sie sein.“
Viertes Kapitel: Pragmatische Paranoia
- Design im Auftrag
Das Konzept des Design by Contract ist eine einfache, aber wirkungsvolle Technik, die sich auf die Dokumentation und Vereinbarung der Rechte und Pflichten von Softwaremodulen konzentriert, um die Korrektheit des Programms sicherzustellen. Ein korrektes Programm tut nicht mehr und nicht weniger, als es zu tun behauptet. Die Dokumentation und Überprüfung dieses Anspruchs ist das Herzstück von Design by Contract. Jede Funktion und Methode in einem Softwaresystem tut etwas. Bevor sie startet, kann die Routine einige Erwartungen an den Zustand der Welt haben. Zu den Erwartungen und Ansprüchen gehören;
- Vorbedingungen: Was muss erfüllt sein, damit die Routine aufgerufen werden kann?
- Invariante der Klasse: Eine Klasse stellt sicher, dass diese Bedingung aus Sicht des Aufrufers immer aktuell ist.
- Nach-Bedingungen: Die Tatsache, dass die Routine eine Nachbedingung hat, bedeutet, dass sie zu einem Ergebnis kommt: Endlosschleifen sind nicht erlaubt.
Implementierung von Design by Contract
Der Hauptvorteil der DBC ist, dass sie die Frage der Anforderungen und Garantien in den Vordergrund rückt. Die einfache Aufzählung des Eingabebereichs, der Randbedingungen und dessen, was die Routine zu liefern verspricht bzw. was sie nicht zu liefern verspricht, ist ein großer Fortschritt beim Schreiben besserer Software. Wenn Sie diese Dinge nicht angeben, programmieren Sie nach dem Zufallsprinzip und genau hier beginnen viele Projekte, enden und scheitern. In Sprachen, die DBC im Code nicht unterstützen, können Sie nur so weit gehen, was gar nicht so schlecht ist. Schließlich ist DBC eine Entwurfstechnik.
- Tote Programme lügen nicht
Es ist leicht, in die Denkweise „das kann nicht passieren“ zu verfallen. Die meisten von Ihnen haben schon Code geschrieben, der nicht überprüft hat, ob eine Datei erfolgreich geschlossen wurde oder ob eine Trace-Anweisung wie erwartet geschrieben wurde. Wahrscheinlich war das auch gar nicht nötig, denn der betreffende Code würde unter normalen Bedingungen nicht fehlschlagen. Aber Sie programmieren defensiv. Sie suchen nach fehlerhaften Zeigern in anderen Teilen Ihres Programms und löschen den Stack. Sie überprüfen, ob die geladenen Versionen der gemeinsam genutzten Bibliotheken korrekt sind.
Alle Fehler geben Ihnen Informationen. Sie könnten sich selbst davon überzeugen, dass der Fehler nicht passieren kann und ihn einfach ignorieren. Stattdessen sagt sich ein pragmatischer Programmierer, dass etwas sehr, sehr Schlimmes passiert ist, wenn ein Fehler auftritt.
Abstürzen, nicht ausmisten
Einer der Vorteile, Probleme so früh wie möglich zu erkennen, ist, dass Sie früher abstürzen können.
Und in vielen Fällen ist ein Absturz Ihres Programms das Beste, was Sie tun können. Die Alternative wäre vielleicht, weiterhin beschädigte Daten in eine robuste Datenbank zu schreiben oder die Waschmaschine in den zwanzigsten aufeinanderfolgenden Schleudergang zu schicken.
- Selbstbewusste Programmierung
Der Zählerstand kann nicht negativ sein, der Druck kann nicht fehlschlagen, die Protokollierung kann nicht fehlschlagen oder dies kann niemals passieren. Üben Sie diese Art von Täuschung nicht aus, vor allem nicht beim Programmieren. Wenn es nicht passieren kann, wenden Sie Assertions an, um sicherzustellen, dass es nicht passieren wird.
Wann immer Sie denken, dass „das niemals passieren könnte“, fügen Sie Code hinzu, um dies zu überprüfen. Der einfachste Weg, dies zu tun, ist die Verwendung von Assertions. In den meisten C- und C++-Implementierungen finden Sie eine Form von assert oder assert-Makros, die eine boolesche Bedingung überprüfen. Diese Makros können von unschätzbarem Wert sein. Assertions helfen auch bei der Überprüfung der Funktionsweise eines Algorithmus. Angenommen, Sie haben einen ausgeklügelten Algorithmus geschrieben; Assertions prüfen, ob er funktioniert.
Verwenden Sie keine Assertions anstelle der eigentlichen Fehlerbehandlung. Assertions prüfen auf Dinge, die niemals passieren sollten, und stellen Sie sicher, dass die von Ihnen verwendete Assertion-Methode keine Nebeneffekte hat, die neue Fehler verursachen könnten.
Lassen Sie Behauptungen auf
Bei Menschen, die Compiler und Sprachumgebungen schreiben, ist eine weit verbreitete Fehlinterpretation von Assertions weit verbreitet. Sie lautet: „Assertions fügen dem Code einen gewissen Overhead hinzu. Da sie Dinge überprüfen, die nie passieren sollten, werden sie nur durch einen Fehler im Code ausgelöst. Sobald der Code getestet und ausgeliefert wurde, schalten Sie die Assertions aus, damit der Code schneller läuft.“
Mein Lieblingszitat aus dem Kapitel: „Assertions fügen dem Code einen gewissen Overhead hinzu. Da sie Dinge überprüfen, die niemals passieren sollten, werden sie nur durch einen Fehler im Code ausgelöst. Sobald der Code getestet und ausgeliefert wurde, werden sie nicht mehr benötigt und sollten abgeschaltet werden, damit der Code schneller läuft.“
Kapitel Fünf: Biegen oder Brechen
- Entkopplung und das Gesetz von Demeter
Organisieren Sie Ihren Code in Module und kontrollieren Sie die Interaktion zwischen ihnen. Wenn ein Modul beschädigt wird und ersetzt werden muss, sollten die anderen Module weiterarbeiten.
Kopplung minimieren
Es ist nichts dagegen einzuwenden, wenn Module voneinander wissen. Allerdings müssen Sie darauf achten, mit wie vielen anderen Modulen Sie interagieren und, noch wichtiger, wie Sie dazu gekommen sind, mit ihnen zu interagieren. Wenn Sie ein Objekt um einen bestimmten Dienst bitten, möchten Sie, dass dieser Dienst in Ihrem Namen ausgeführt wird. Sie möchten nicht, dass das Objekt Ihnen eine fremde Entität liefert, mit der Sie sich auseinandersetzen müssen, um den gewünschten Dienst zu erhalten.
Das Gesetz der Demeter
Das Demeter-Gesetz für Funktionen besagt, dass jede Methode eines Objekts nur Prozesse aufrufen sollte, die zu ihr selbst gehören, alle an die Methode übergebenen Parameter, alle von ihr erstellten Objekte und alle direkt gehaltenen zusammengesetzten Objekte. Das Demeter-Gesetz zielt darauf ab, die Kopplung zwischen Modulen in einem bestimmten Programm zu minimieren. Es verhindert, dass Sie in ein Objekt eingreifen, um Zugriff auf die Methoden eines dritten Objekts zu erhalten. Das Schreiben von „scheuem“ Code ehrt das Gesetz der Demeter und erreicht Ihr Ziel, die Kopplung zwischen den Modulen zu minimieren.
- Metaprogrammierung
Details bringen Ihren perfekten Code erheblich durcheinander, wenn sie sich häufig ändern. Jedes Mal, wenn Sie den Code ändern müssen, um ihn an eine Änderung der Geschäftslogik, des Gesetzes oder des Zeitgeschmacks des Managements anzupassen, laufen Sie Gefahr, das System durch die Einführung eines neuen Fehlers zu zerstören.
Genug mit den Details! Holen Sie sie aus dem Code heraus. Und wenn Sie schon dabei sind, können Sie Ihren Code hochgradig konfigurierbar und „weich“ machen. Das heißt, er lässt sich leicht an Änderungen anpassen.
Dynamische Konfiguration
Konfigurieren Sie, integrieren Sie nicht. Sie sollten Ihre Systeme in hohem Maße konfigurierbar machen. Dabei geht es nicht nur um Bildschirmfarben oder Eingabeaufforderungstexte, sondern auch um tief verwurzelte Dinge wie die Auswahl von Algorithmen, Datenbankprodukten, Middleware-Technologien und die Gestaltung der Benutzeroberfläche, die Sie als Konfigurationsoptionen und nicht durch Integration oder Engineering anwenden sollten.
Wann Sie konfigurieren müssen
Stellen Sie Ihre Konfigurations-Metadaten im Klartext dar; das macht das Leben viel einfacher. Aber wann sollte ein Programm diese Konfiguration lesen? Viele Programme scannen solche Dinge nur beim Start. Das ist unglücklich, weil Sie dann gezwungen sind, die Anwendung neu zu starten, wenn Sie das Layout ändern müssen. Ein flexiblerer Ansatz ist es, Programme zu schreiben, die ihre Konfiguration während der Ausführung neu laden. Diese Flexibilität hat zwar ihren Preis, ist aber auch komplexer zu implementieren.
Überlegen Sie also, wie die Benutzer Ihre Anwendung verwenden werden: Wenn es sich um einen lang laufenden Serverprozess handelt, sollten Sie eine Möglichkeit vorsehen, die Metadaten während der Ausführung des Programms erneut zu lesen und anzuwenden. Bei einer kleinen Client-GUI-Anwendung, die schnell neu gestartet wird, brauchen Sie das vielleicht nicht.
- Zeitliche Kopplung
Hier spricht der Autor über Zeit als Gestaltungselement der Software selbst. Es gibt zwei Aspekte der Zeit, die für Sie wichtig sind: Gleichzeitigkeit (Dinge, die gleichzeitig geschehen) und Ordnung (die relativen Positionen der Dinge in der Zeit). Bei der zeitlichen Kopplung wird die Gleichzeitigkeit immer vor der Ordnung aufgerufen; Sie können immer nur einen Bericht gleichzeitig ausführen; danach warten Sie, bis der Bildschirm neu gezeichnet ist, bevor Sie auf eine Schaltfläche klicken.
Lieblingszitat aus diesem Kapitel: „Keine noch so große Genialität kann die Beschäftigung mit Details überwinden.“
Sechstes Kapitel: Während Sie programmieren
- Programmieren nach dem Zufallsprinzip
Es gibt Hunderte von Fallen, die jeden Tag darauf warten, Sie als Entwickler zu erwischen. Seien Sie vorsichtig damit, falsche Schlüsse zu ziehen. Vermeiden Sie es, nach dem Zufallsprinzip zu programmieren, sich auf Glück und zufällige Erfolge zu verlassen und stattdessen bewusst zu programmieren. Wenn Sie nach dem Zufallsprinzip programmieren und Ihr Code scheitert, werden Sie nicht wissen, warum er scheitert, weil Sie bei den wenigen Tests, die Sie durchgeführt haben, nicht wussten, warum er überhaupt funktioniert hat.
Unfälle bei der Umsetzung
Unfälle bei der Implementierung sind Dinge, die einfach deshalb passieren, weil der Code so geschrieben ist, wie er ist. Am Ende verlassen Sie sich auf undokumentierte Fehler oder Randbedingungen. Die Randbedingung, auf die Sie sich verlassen, ist vielleicht nur ein Zufall. Unter anderen Umständen (z.B. bei einer anderen Bildschirmauflösung) könnte sie sich anders verhalten.
Das undokumentierte Verhalten kann sich mit der nächsten Version der Bibliothek ändern.
Wie Sie bewusst programmieren
- Programmieren Sie nicht mit verbundenen Augen.
- Verlassen Sie sich nur auf zuverlässige Dinge. Verlassen Sie sich nicht auf Zufälle oder Annahmen.
- Dokumentieren Sie Ihre Annahmen.
- Algorithmus Geschwindigkeit
Als pragmatischer Programmierer schätzen Sie die Ressourcen von Algorithmen, wie Zeit, Prozessor und Speicher. Die meisten nicht trivialen Algorithmen verarbeiten irgendeine Art von variabler Eingabe; die Größe dieser Eingabe hat Auswirkungen auf den Algorithmus. Je größer die Informationen sind, desto länger ist die Laufzeit oder desto mehr Speicher wird verbraucht.
Sie werden feststellen, dass Sie unbewusst die Laufzeit- und Speicheranforderungen überprüfen, wenn Sie etwas schreiben, das Schleifen oder rekursive Aufrufe enthält. Dieser Prozess ist eine schnelle Bestätigung dafür, dass Ihre Aktionen unter den gegebenen Umständen sinnvoll sind. Deshalb führen Sie eine detailliertere Analyse durch. Hier kommt die O()-Notation ins Spiel. Die O()-Notation ist eine mathematische Methode, um mit Näherungen umzugehen.
- Refactoring
Code muss sich weiterentwickeln, er ist nicht statisch. Wenn sich ein Programm weiterentwickelt, wird es notwendig, frühere Entscheidungen zu überdenken und Teile des Codes zu überarbeiten. Das Umschreiben, Umstrukturieren und Umgestalten eines bestehenden Codes, das Ändern seiner internen Struktur, ohne sein äußeres Verhalten zu verändern, wird als Refactoring bezeichnet.
Wann sollten Sie refaktorisieren?
Angenommen, Sie stoßen auf einen Stolperstein, weil der Code nicht mehr ganz passt oder Ihnen zwei Dinge auffallen, die Sie zusammenführen sollten, dann zögern Sie nicht, ihn zu ändern. Hier sind einige Dinge, die dazu führen können, dass der Code für ein Refactoring in Frage kommt:
Duplikation. Sie haben einen Verstoß gegen das DRY-Prinzip (Don’t Repeat Yourself) entdeckt.
Nicht-orthogonaler Entwurf. Sie haben einen Code oder ein Verfahren entdeckt, das noch orthogonaler gestaltet werden könnte (Orthogonalität).
Veraltetes Wissen. Die Dinge ändern sich, die Anforderungen ändern sich, und Ihr Verständnis des Problems wächst. Der Code muss damit Schritt halten.
Leistung. Sie müssen Funktionen aus einem Bereich des Systems in einen anderen verschieben, um die Leistung zu verbessern.
- Code, der leicht zu testen ist
Bei komplexeren Chips und Systemen integrieren die Hardwareentwickler Funktionen wie einen vollständigen BIST (Build-In Self Test), der intern Diagnosen auf der Basisebene durchführt, und einen TAM (Test Access Mechanism), der ein Test-Kabelbaum bereitstellt, der es der externen Umgebung ermöglicht, Stimuli zu liefern und Antworten des Chips zu sammeln. Sie können dasselbe in der Software tun, die Testbarkeit von Anfang an in die Software einbauen und jedes Teil im Detail testen, bevor Sie versuchen, es miteinander zu verbinden.
Einheitstest
Das Testen auf Chipebene bei Hardware entspricht dem Unit-Test bei Software. Jedes Modul wird isoliert getestet, um sein Verhalten zu überprüfen. Sie können besser verstehen, wie ein Modul in der großen weiten Welt reagieren wird, wenn Sie es unter kontrollierten Bedingungen gründlich getestet haben.
Ein Software-Einheitstest ist ein Code, der ein Modul testet. Normalerweise richtet der Unit-Test eine künstliche Umgebung ein und ruft dann Routinen in dem zu testenden Modul auf. Der Unit-Test prüft dann die zurückgegebenen Ergebnisse, entweder gegen bekannte Werte oder gegen die Ergebnisse der vorherigen Durchläufe desselben Tests.
Wenn Sie später Ihre „integrierten Software-Schaltkreise“ zu einem kompletten System zusammenfügen, können Sie sich darauf verlassen, dass die einzelnen Teile wie erwartet funktionieren. Dann können Sie die gleichen Unit-Tests verwenden, um das System als Ganzes zu testen.
Lieblingszitat des Kapitels: „Jede Software, die Sie schreiben, wird getestet – wenn nicht von Ihnen und Ihrem Team, dann von den späteren Benutzern – also sollten Sie sie gründlich testen.
Kapitel Sieben: Vor dem Projekt
- Die Anforderungsgrube
Das Sammeln von Anforderungen ist eine frühe Phase des Projekts. Sammeln bedeutet, dass die Anforderungen bereits vorhanden sind, Sie müssen sie nur noch finden. Legen Sie sie in Ihren Korb und machen Sie sich auf den Weg. Obwohl es so nicht funktioniert, tauchen die Anforderungen nur selten auf. Sie sind tief unter Schichten von Annahmen, Missverständnissen und Politik begraben.
Auf der Suche nach Anforderungen
Wie erkennen Sie einen echten Bedarf, wenn Sie sich durch den Schmutz der Umgebung wühlen? Die Antwort ist sowohl komplex als auch einfach. Die einfache Antwort ist eine Aussage über etwas, das erfüllt werden muss. Andererseits sind nur sehr wenige Anforderungen eindeutig, was die Anforderungsanalyse komplex macht.
Wenn die Anforderung lautet: „Nur Mitarbeiter können einen Mitarbeiterdatensatz einsehen“, dann werden Sie jedes Mal, wenn die Anwendung auf diese Dateien zugreift, einen einfachen Test programmieren müssen. Lautet die Anweisung jedoch „Nur autorisierte Benutzer können auf einen Mitarbeiterdatensatz zugreifen“, wird der Entwickler wahrscheinlich eine Art von Zugriffskontrollsystem entwerfen und implementieren. Wenn sich die Richtlinien ändern, müssen nur die Metadaten für dieses System aktualisiert werden. Wenn Sie die Anforderungen auf diese Weise sammeln, erhalten Sie ein System, das die Metadaten gut unterstützt.
- Lösen Sie unmögliche Rätsel
Denken Sie an Puzzles aus der realen Welt, die als Weihnachtsgeschenke oder auf Flohmärkten auftauchen. Sie müssen den Ring entfernen oder die T-förmigen Teile in die Schachtel legen. Sie ziehen den Ring ab und stellen schnell fest, dass die scheinbaren Lösungen das Rätsel nicht lösen. Die Antwort liegt ganz woanders. Das Geheimnis zur Lösung des Rätsels besteht darin, die wirklichen Zwänge zu erkennen und darin eine Lösung zu finden. Einige Zwänge sind absolut, andere sind lediglich vorgefasste Meinungen. Grundlegende Zwänge müssen beachtet werden, auch wenn sie noch so geschmacklos oder dumm erscheinen mögen. Auf der anderen Seite sind einige scheinbare Zwänge möglicherweise nicht korrekt.
Denken Sie nicht außerhalb der Box – finden Sie die Box
Wenn Sie vor einem unlösbaren Problem stehen, zählen Sie alle möglichen Wege auf, die Sie vor sich haben.
Lehnen Sie nichts ab, auch wenn es noch so unbrauchbar oder dumm klingt. Gehen Sie die Liste durch und erklären Sie, warum Sie einen bestimmten Weg nicht einschlagen können. Können Sie es beweisen?
- Nicht bevor Sie bereit sind
Wenn Sie bei einer Aufgabe anhaltende Zweifel oder Widerwillen verspüren, sollten Sie darauf achten. Vielleicht können Sie nicht herausfinden, was genau falsch ist. Geben Sie dem Ganzen Zeit und Ihre Zweifel werden sich wahrscheinlich zu etwas Handfesterem herauskristallisieren, etwas, das Sie angehen können. Softwareentwicklung ist immer noch keine Wissenschaft. Lassen Sie Ihre Instinkte zu Ihrer Leistung beitragen.
Gutes Urteilsvermögen oder Aufschieberitis?
Der Beginn eines neuen Projekts oder sogar eines neuen Moduls in einem bestehenden Projekt kann eine schmerzhafte Erfahrung sein. Viele würden es vorziehen, den Anfang zu verschieben. Woran erkennen Sie also, dass Sie einfach nur zögern, anstatt verantwortungsbewusst darauf zu warten, dass alle Teile an ihren Platz fallen?
Unter diesen Umständen ist eine Technik, die Ihnen helfen kann, das Prototyping. Wählen Sie einen Bereich, den Sie für schwierig halten, und beginnen Sie mit der Erstellung eines Konzeptnachweises. Schon nach kurzer Zeit werden Sie denken, dass Sie Ihre Zeit verschwenden. Diese Langeweile ist wahrscheinlich ein gutes Indiz dafür, dass Ihre anfängliche Zurückhaltung nur der Wunsch war, die Verpflichtung, mit dem Projekt zu beginnen, aufzuschieben. Geben Sie den Prototyp auf und machen Sie sich an die eigentliche Entwicklung.
Lieblingszitat des Kapitels: „Vollkommenheit ist nicht erreicht, wenn es nichts mehr hinzuzufügen gibt, sondern wenn es nichts mehr wegzunehmen gibt….“
Achtes Kapitel: Pragmatische Projekte
- Rücksichtslose Tests
Testen Sie früh, testen Sie oft und testen Sie automatisch. Sie sollten mit dem Testen beginnen, sobald Sie den Code haben. Entwickeln Sie ausgefeilte Testpläne für Ihre Projekte. Teams, die automatisierte Tests verwenden, haben eine viel bessere Chance auf Erfolg. Tests, die bei jedem Build ausgeführt werden, sind viel effektiver als Testpläne, die im Regal liegen. Denken Sie daran: Je früher Sie einen Fehler finden, desto billiger ist es, ihn zu beheben. „Ein wenig kodieren, ein wenig testen.“
Damit Ihr Projekt zufriedenstellend ist, muss es mehr Testcode als Produktionscode enthalten. Die Zeit, die bei der Erstellung dieses Testcodes verloren geht, ist die Mühe wert und auf lange Sicht billiger. Außerdem haben Sie die Chance, ein Produkt mit nahezu null Fehlern zu erstellen.
- Große Erwartungen
Der Erfolg des Projekts Ihres Teams wird daran gemessen, wie es die Erwartungen der Benutzer erfüllt. Wenn Ihr Projekt hinter den Erwartungen zurückbleibt, gilt es als Misserfolg, unabhängig davon, wie gut das Ergebnis in absoluten Zahlen ist.
Die extra Meile
Übertreffen Sie sanft die Erwartungen Ihrer Benutzer. Erschrecken Sie Ihre Benutzer nicht, sondern überraschen Sie sie. Geben Sie ihnen ein bisschen mehr, als sie erwartet haben. Der zusätzliche Aufwand, der erforderlich ist, um dem System eine benutzerorientierte Funktion hinzuzufügen, wird sich immer wieder durch Wohlwollen auszahlen.
- Stolz und Vorurteile
Als pragmatischer Programmierer sollten Sie nicht vor der Verantwortung weglaufen. Freuen Sie sich vielmehr, wenn Sie vor Herausforderungen stehen, und machen Sie Ihre Arbeitsexpertise bekannt. Wenn Sie für ein Design oder ein Stück Code verantwortlich sind, werden Sie eine Arbeit machen, auf die Sie stolz sind.
Signieren Sie Ihre Arbeit
Seien Sie stolz auf Ihr Eigentum. „Sie haben das geschrieben und Sie sollten hinter Ihrer Arbeit stehen“. Ihre Unterschrift sollte als Indikator für Qualität anerkannt werden. Die Leute sollten Ihren Namen auf einem Stück Code sehen und erwarten, dass es solide, gut geschrieben, getestet und dokumentiert ist – eine professionelle Arbeit, die von einem echten Profi gemacht wurde.
Mein Lieblingszitat aus diesem Kapitel: „Die Zivilisation schreitet voran, indem sie die Zahl der wichtigen Operationen, die wir ohne Nachdenken ausführen können, erweitert.“
WIE DIESES BUCH SOFTWAREENTWICKLERN HELFEN KANN?
„The Pragmatic Programmer“ von Andrew Hunt und David Thomas ist ein klassisches Buch zur Softwareentwicklung, das praktische Ratschläge und Techniken zur Verbesserung der Arbeitsweise von Entwicklern bietet. Es behandelt Themen wie Debugging, Testen, Automatisierung und Projektmanagement sowie die Bedeutung von Kommunikation und kontinuierlichem Lernen. Wenn Sie den Anleitungen des Buches folgen, können Entwickler ihre Programmierkenntnisse verbessern, ihre Produktivität steigern, effizienter arbeiten, effektivere Teammitglieder werden und bessere Softwareprodukte liefern.