Der Weg zum guten Code
Um entscheiden zu können welcher Weg zum guten Code führt, muß man festlegen was guten Code von schlechtem unterscheidet. Die Erkenntnis, daß es nicht gleichgültig ist, wie Code beschaffen ist wurde ja offensichtlich schon gewonnen -- andernfalls wäre die Suche eine sinnlose.
"Gut" ist der Code, wenn er über die blanke Funktionalität hinaus weitere Eigenschaften erfüllt. Über diese nichtfunktionalen Eigenschaften giebt es eine große Literatur, die Worte "Qualiät" und "Architektur" fallen einem in diesem Zusammenhang ein. Dabei geht es aber in der Regel um die Software-Qualität, also die Qualität des gesamten Erzeugnisses im Zusammenspiel mit der Umgebung in der es betrieben wird.
Diese Seite engt den Blick ganz bewußt auf den "Code" ein. Damit ist der Quelltext gemeint, aus dem das Erzeugnis hergestellt wird. Noch genauer geht es dabei um den Programm-Code -- wann kann man diesen als "gut" bezeichnen?
Für den Entwickler -- den Einzigen, der ursächlich an der Code-Qualität interessiert ist -- muß der Code wartbar, erweiterbar und änderbar sein. Der Grad dieser Eigenschaften bestimmt die Güte des Codes. Die Optimierung dieser Eigenschaften steht bisweilen im Widerspruch zu anderen Eigenschaften -- insbesondere der Performance. Das Abwägen ist Aufgabe des Architekten und liegt außerhalb des Scope dieses Textes; hier geht es ausschließlich darum die Code-Qualität zu verbessern.
Es giebt eine unüberschaubare Fülle an Regeln, Tipps, Techniken und Ideologien zum Thema. Nahezu jeder Entwickler, den man zum Thema Code-Qualität befragt weiß einen anderen Strauß aus seinen Erfahrungen zu binden.
Aus dem Wunsch heraus, einen Überblick über die verwirrende Vielzahl von Begriffe, Ansichten und Ansätzen zu gewinnen ist die untige Sammlung entstanden. Sie ist weder vollständig noch verbindlich, bietet dem interessierten Leser aber eine Basis für die weitere Erkundung der Materie. Es wird zwar der Versuch eine Einteilung gemacht um den Überblick zu verbessern, aber es wird versucht zu vermeiden eine Wertung vorzunehmen.
Leitgedanken
Ein Leitgedanke zu dem es bislang noch kein vernünftiges Gegenargument giebt ist folgender:
Die Qualität von Code bemißt sich daran, wie leicht er zu lesen ist
Über die Frage, wie "lesbarer" Code konkret auszusehen hat giebt es allerdings unterschiedliche Auffassungen. Daraus kann man auch ableiten, daß bessere Lesbarkeit nicht automatisch zu besserer Änderbarkeit führt. Sie führt aber dazu, daß der Code besser zu verstehen ist, daher besser bearbeitet werden kann und damit besser zu warten ist. "Lesbarkeit" bleibt ein Kernzeil des Qualitäts-Suchenden und er wird sich die Frage bei jeder Code-Zeile zu stellen haben.
Die Grenzen meiner Sprache sind die Grenzen meiner Welt
Im Kontext der Code-Qualität bedeutet das: So klar meine Sprache ist, so klar ist mein Code. Computer-Sprachen (formale Sprachen überhaupt) lassen keine Mehrdeutigkeiten zu. Wird der gleiche Code bei mehrfacher Ausführung unterschiedlich interpretiert, so ist das ein Fehler. Eine klare Ausdrucksweise, als Ausdruck klarer Gedanken ist eine Grundvoraussetzung klaren Programm-Codes.
Separation of Concerns
ToDo: Erklärung wie die Idee von Dijkstraa als Leitgedanke zu verstehen ist
Regeln
Eine Regel ist eine Richtlinie die vermittelt was man tun – oder lassen – sollte, aber nicht vorgeben wie man das erreicht. Regeln weisen die Richtung, bestimmen den Generalkurs; bieten aber wenig Hilfe bei der Überwindung konkreter Hindernisse. Regeln sind keine Naturgesetze; es liegt in ihrer Natur, daß man auch von ihnen abweichen kann. Es ist wichtig, den Wert einer Regel zu erkennen und daran die Maßnahmen auszuwählen die sie unterstützen.
Die Abgrenzung von Regeln gegen best practices und auch gegen konkrete Maßnahmen ist nicht immer scharf auszumachen. Aber bevor man sich in exegetische Schlachten begibt, sollte man sich immer fragen, worum es eigentlich geht. Das Ziel ist klarer, handhabbarer Code, das zu erreichen ist jeder Weg erlaubt. Das gilt im Übrigen auch für die Gewichtung von Regeln, Ideologie ist immer der falsche Weg -- egal wohin.
- DRY - Don't repeat yourself
- KISS – Keep it simple stupid commitstrip
- Ockhams Razor
- Law of Parsimony
- Principle of Least Surprise
- SoC – Separation of Concerns (im Sinne von Aufgaben-Trennung)
- YAGNI – You Aren't Gonna Need It Wikipedia
- high cohesion/loose coupling
- OCP – Open/Closed Principle
- SRP – Single Responsibility Principle
- Don’t make me think/write your code for everybody
- IoC – Inversion of Control / Dependency Inversion
- Form follows Function – Pragmatismus vor Ästhetik
- ISP – Interface Segregation Principle
- Stick to the Paradigm
- Tell don't ask (Martin Fowler)
Was ist mit SOLID?
Robert C. Martin hat fünf Prinzipien (SRP, open/closed, Liskov, ISP, DI) zusammengefaßt und damit ein tolles backronym zu bilden. Ob man dieser Kollektion einen besonderen Wert zuordnen möchte oder nicht sei mal dahingestellt. Das Liskov-Prinzip wurde hier - wegen seiner Konkretheit - unten den best practices eingeordnet. Die Frage wie man IoC und DI beschreibt und gegen einander abgrenzt ist noch zu diskutieren.
Best Practice
Best Practices – auch Heuristiken genannt – sind Techniken oder Vorgehensweisen, die sich in der Praxis in irgendeiner Form und iregndeiner Situation bewährt haben. Meist lassen sie Gestaltungsspielraum, wenn es um die konkrete Anwendung geht – die Abgrenzung zwischen einer konkreten Regel und einer allgemeinen Heuristik ist fließend. Und man sollte immer bedenken, daß best practices in der Regel aus konkreten Situationen heraus entstanden sind und nicht auf jede beliebige Situation übertragbar sind -- dazu giebt es unten ein Antipattern.
- Clean Code (hier nur einige Heuristiken, die sich bei meiner Arbeit bewährt haben)
- Jeder Bezeichner soll exakt beschreiben, was das Bezeichnete tut oder enthält
- Eine Methode soll eine Wert liefern oder etwas tun, aber nicht beides
- Methoden (auch Konstruktoren) soll nicht mehr als drei Parameter haben
- Death to Magic numbers
- saubere Bezeichner sind besser als gute Kommentare sind besser als keine Kommentare sind besser als falsche Kommentare
- Eine Methode – eine Aufgabe
- lokale Variablen dort deklarieren, wo sie gebraucht werden
- Kent Beck's Design Rules (according to Martin Fowler)
- Passes the Tests
- Reveals intention
- No Duplication
- Fewest Elements
- DI – Dependency Injection (als Implementierung von IoC)
- LSP - Liskov substitution principle
- Demeter-Prinzip
- Trennung von Abstraktions-Ebenen
- Trennung von Interface und Implementierung siehe dazu auch: Bridge-Pattern
- Interfaces sollen so schmal wie möglich sein und nur das enthalten was tatsächlich erforderlich ist.
- verwende Architektur-Pattern
- verwende das Schichten-Modell
- IOSP– Integration Operation Segregation Principle
- verwende Design-Pattern (hier einige GoF-Pattern, die ich besonders häufig verwende)
- Factory Method
- Adapter
- (simplified) Builder
- Singleton
- Proxy
- Facade
- Decorator
- Delegation statt Vererbung (Delegation wird im GoF-Buch nicht als Pattern sondern als Konzept beschrieben)
- Ein Konzept – ein Begriff
- Bsp: die eMail-Adresse sollte im Code nicht durcheinander als "eMail_Adresse", "mail-Adresse", "mail", "eMail" oder sonstwie bezeichnet werden, sondern immer gleich
- vermeide Antipattern
- Interface bloat -- Interfaces verlangen mehr als erforderlich
- Inner-platform effect – Das System erlaubt soviel Customization, daß eine schlechte Plattform daraus geworden ist.
- Constant Interface -- Interfaces dienen nur der Definition von Konstanten
- Sequential coupling -– Die Klasse verlangt den Aufruf ihrer Methoden in einer bestimmten Reihenfolge
- Cargo cult programming -– Pattern und Methoden verwenden ohne sie zu verstehen
- Golden hammer / Silver bullet -– Alle Probleme mit der gleichen (Lieblings-)Methode lösen
- Base Bean – Vererbung soll is-a-Beziehungen abbilden, nicht has-a
- Stellvertreter-Typen – Statt einen Datentyp zu definieren, existierende Klassen (besonders String) mißbrauchen
- Petting Zoo - keeping code that is not worth it
- vermeide Code Smells(see page 18)
- Refactoring
- Verwende eine durchgehende Nomenklatur
- Stick to the Coding Guidelines
- Das gleiche Problem sollte immer auf gleiche Art und Weise gelöst werden
- Verwende für Kommentare und Bezeichner nur natürliche Sprachen die Du auch beherrschst
- actualValue für den aktuellen Wert (richtig ist current)
- bound als Übersetzung für "verknüpft" (richtig ist linked)
- Don't state the obvious:
- Logger logger = LoggerFactory.obtainLogger();
- String stringToBeLogged = "log: logging start of action Foo";
- logger.log(stringToBeLogged);
- toString() ist kein Serialisierungs-Tool
- zu viel von etwas ist selten gut:
If you know what you're doing, three layers is enough; if you don't, even seventeen levels won't help.
- (Michael Padlipsky)
Hilfsmittel
Entgegen anderslautender Meinungen erhöht der Einsatz von Tools nicht automatisch die Qualität. Das Berechnen von Werten anhand von Metriken beispielsweise trägt zunächst gar nichts zur Qualität bei, erst die Auswertung und das Durchführen von Maßnahmen zur Änderung der Werte kann als qualitätsstiftende Maßnahme gelten. Nichtsdestotrotz unterstützt die Verwendung von Hilfsmitteln den Entwickler dabei den Weg zum Code hoher Qualität zu beschreiten.
- automatisch durchgeführte Unit-Tests
- Unit-Tests als "Tool" zu beschreiben mag seltsam klingen. Tatsächlich sind Unit-Tests aber nicht Teil des produktiven Codes und daher kann man mit Fug und Recht von einem Tool sprechen.
- Metriken
- Tools zur Code-Analyse (sonar, java-Compiler-Warnings)
- Tools zur Messung der Test-Abdeckung (EclEmma)
- Automatische Code-Formatierung (Eclipse Code Formatter)
- Coding-Guidelines
- Sun Code Conventions
- Sie werden seit 1999 nicht mehr gepflegt. Einige Regeln sind überholt, andere zweifelhaft (z.B. 6.2: Variablen-Deklarationen nur am Beginn eines Code-Blocks plazieren), Sie bilden aber die Grundlage für das Code-Layout und man sollte sie kennen und berücksichtigen.
- (Fun fact: Der Autor hat anscheinend von C abgeschrieben – es sind tatsächlich ein paar Fehler im Dokument)
- Refactoring-Tools (IDEs wie Eclipse bringen eine Menge davon mit
Arbeitstechniken
Die klassische Technik des Entwicklers ist das Zurückziehen hinter die Tastatur. Der Entwickler kriecht erst hervor, wenn das Werk vollendet ist. Das beschreibt einen Teil der Tätigkeit, reicht aber schon seit einem halben Jahrhundert nicht mehr aus. Und auch die Handgriffe, mit denen der Entwickler im stillen Kämmerlein den Wandel von Buchstaben in Code vollzieht wandeln sich im Laufe der Zeit. Man muß nicht jede Technik übernehmen, aber man sollte sich damit befassen und zumindest genau wissen, warum man sie nicht einsetzt.
- Boy Scout Principle
- Code – fremden und eigenen – lesen und reflektieren
- zuhören
- Diskussion über Code
- Code-Reviews
- XP – extreme Programming
- TDD – Test Driven Development
- Test First Ansatz
- Pair Programming
- Root Cause Analysis
- Incremental Design
- Embrace the CATSAN method
- Programming by Difference (→ Michael Feathers)
- Rubber Duck Debugging
- Technologische Schulden minimieren
Literatur
- Edsger Dijkstra On the role of scientific thought
- Refactoring: Improving the Design of Existing Code (Martin Fowler/Kent Beck)
- Working Effectively with Legacy Code (Michael C. Feathers)
- Clean Code (Robert C. Martin)
- Design Patterns: Elements of Reusable Object-Oriented Software (Gamma/Helm/Vlissides/Johnson)
- The Mythical Man Month (Fred Brooks)
- The Systems Bible – 3rd edition of "Systemantics" (John Gall) Überblick in der Wikipedia
- No Silver Bullet (Fred Brooks)
- Goto Considered Harmful (Edsger Dijkstra)
- clean code developer site
- Regeln für GIT-commit-Kommentare
- Epigramme von Alan Perlis
- Programmier-Regeln von John Romero
- Gute Sammlung von Pattern, Anti-Pattern etc.
- Bad Code in XKCD
Zen
Keine Richtlinien, keine Anweisungen, einfach Aussagen über die man nachdenken kann. Hier kann man beliebig hitzig diskutieren; wichtig ist nur, daß man darüber – möglichst unvoreingenommen – nachdenkt.
- Wer sich nicht klar ausdrückt, denkt auch nicht klar
- big bang features lead to big bang
- Software Engineering ist agnostisch
- Ein Beispiel beweist nichts, widerlegt aber alles – soviel zu TDD- und Test-Abdeckungs-Fetischismus
- Ist TDD die zeitgemäße Formulierung für "trial and error"?
- Wenn Du weißt was Du tust, bist Du gesegnet
- If it ain't broken, don't fix it vs. Refactoring
- Verwende nur was Du auch verstehst (Wie XKCD GIT charakterisiert)
- Wer etwas ablehnt oder verteidigt sollte genau wissen warum
- Nichts hält so lange wie ein Provisorium
- Wer trennt schafft Abhängigkeiten, wer nicht trennt schafft Chaos
- Nichts ist so einfach, daß man es nicht falsch machen könnte
- Eine Entscheidung ist richtig, wenn sie rational begründet werden kann
- Was wert ist daß man es tut, ist wert daß man es richtig tut.
- Wenn man versucht Probleme auszusitzen, kriechen sie irgendwann hervor und treten einem in den Hintern