Schluss mit Frust: Clean Code hilft bei der Softwarequalität

Seite 3: Aufwandsschätzung von Projekten

Inhaltsverzeichnis

Übertragen auf die Softwareentwicklung bedeutet das: Schätzen Entwickler den Aufwand für eine Feature-Implementierung nicht richtig ein und schaffen es nicht, den Code sauber zu hinterlassen, entstehen unweigerlich technische Schulden. In die Aufwandsschätzung sollten daher realistische Einschätzungen für die Dauer des Aufräumens und Säuberns des Codes einfließen, um eine saubere Codebasis zu gewährleisten. Summieren sich die erforderlichen Zeiten erst so weit, dass die Aufräumarbeiten eher einer Kernsanierung gleichen würden, lässt sich das Projekt meist nur noch mit einem neuen Plan retten, nach dem sich das Qualitätsziel Schritt für Schritt erreichen lässt.

Um sich in einem laufenden Projekt eine ungefähre Vorstellung davon zu verschaffen, wie viel Zeit für das Aufräumen einzuplanen ist, bedarf es eines umfassenden Überblicks. Obwohl die dafür geeignetsten Werkzeuge hinlänglich bekannt sind, werden sie allzu oft ignoriert. Eine statische Codeanalyse mit Compiler Warnings, Linter und Code Inspections verschafft Überblick, denn diese Werkzeuge kennen die wichtigsten Prinzipien sowie Coding-Regeln und weisen sogar auf bekannte Fehler hin. Die vorab durchgeführte Analyse betroffener Klassen oder Quelltexte verschafft dem Entwicklungsteam eine Einschätzung dazu, wie viel Aufwand für Implementierungen zusätzlich notwendig ist, um den Code zu säubern.

Dabei gilt es jedoch zu beachten, dass die Werkzeuge kaum Ausnahmen kennen und somit auch False Positives anzeigen, solang nicht die voreingestellten Regeln deaktiviert sind. Die Tools erfordern daher initial etwas Pflegeaufwand, um sich gezielt einsetzen zu lassen.

Die effektivste Methode bleibt es, direkt sauberen Code zu schreiben. Das gelingt allerdings nicht immer, weil die Clean-Coding-Regeln und Design-Prinzipien in Vergessenheit geraten, nachdem ein Feature programmiert und die Arbeit daran beendet ist.

In einer Restaurantküche wäre das undenkbar, denn dort weiß jeder, dass nach dem Kochen der Abwasch folgt. Ein Koch, der ein Drei-Gänge-Menü zubereitet hat, lässt anschließend nicht alle Töpfe, Pfannen und das Geschirr einfach stehen – sondern das gesamte Team macht schon während des Kochens sauber und räumt auf, damit die Arbeit am nächsten Tag reibungslos weitergehen kann.

Den Code nach einer Implementierung aufzuräumen, sollte daher auch in Softwareprojekten selbstverständlich sein. Je früher Code gesäubert ist, desto geringer fällt der künftige Aufwand aus. Besonders wichtig ist das in Bereichen wie Namensgebung, Funktionen, Kommentare, Klassen, Formatierung und Struktur sowie in der Fehlerbehandlung. Das folgende Listing präsentiert den nach Clean-Code-Prinzipien überarbeiteten und gesäuberten Code aus dem ursprünglichen Listing mit "unsauberem" Code.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DuplicateFinder {
  private static List duplicates;
  public static void main(String[] args) {
    String text = "Ohne Clean Code ist Code nicht wartbar.";

    welcomeMessage();
    collectDuplicateWordsInList(text);
    showDuplicates();
  }

  private static void welcomeMessage() {
    System.out.println("Duplikate: ");
  }

  private static void collectDuplicateWordsInList(String text) {
    duplicates = new ArrayList<String>();
    List<String> words = getWordsList(text);

    words.forEach(currentWord -> {
      addDuplicateToList(words, currentWord);
    });
  }

  private static void addDuplicateToList(List words, String currentWord) {
    Boolean alreadyContained = duplicates.contains(currentWord);
    Boolean hasDuplicate = checkCurrentWord(words, currentWord);

    if (alreadyContained == false && hasDuplicate) {
      duplicates.add(currentWord);
    }
  }

  private static Boolean checkCurrentWord(List words, String currentWord) {
    long wordCount = words.stream().filter(word -> currentWord.equals(word)).count();
    return wordCount > 1;
  }

  private static List getWordsList(String text) {
    text = text.toLowerCase();
    String words[] = text.split(" ");
    return Arrays.asList(words);
  }

  private static void showDuplicates() {
    duplicates.forEach(word -> System.out.println(word));
  }
}

Dieser Code ist nun intuitiv verständlich. Auch wenn das Beispiel jetzt deutlich mehr Codezeilen enthält, ist es leichter, die Funktionalität zu verstehen und weitere Änderungen am Code durchzuführen. Der Quelltext dokumentiert sich in dem Fall selbst.

Code besitzt dann eine lesbare Basis, wenn sich die Software anhand der Namen von Klassen, Methoden sowie Variablen selbst dokumentiert und Entwickler dadurch nahezu ohne Kommentare im Quelltext auskommen. Zu noch leichterer Verständlichkeit trägt bei, Funktionen und Klassen möglichst kleinzuhalten und sie nicht ihre Aufgaben sowie Verantwortlichkeiten überschreiten zu lassen.

Eine saubere Formatierung und Struktur des Codes erhält die Übersichtlichkeit des Projekts. Durch den Einsatz der gezeigten Clean-Code-Grundregeln erhält das Team sauberen Code, den auch andere später gerne und frustfrei bearbeiten können.

Nur in seltenen Fällen ist es möglich, von Grund auf Clean Code zu schreiben, denn meist müssen sich Programmierer mit Code aus bestehenden Projekten auseinandersetzen. Eine längst fällige Überarbeitung solcher Codeabschnitte tritt oft aber erst dann zutage, wenn die Software angepasst werden muss und die Entwickler gezwungen sind, den alten Code durchzulesen (vergleiche Listing mit "unsauberem" Code).

Schon das Lesen und Verstehen des Codes bedeutet Aufwand. Es empfiehlt sich daher, den Code bereits bei der Einarbeitung zu refaktorieren. Das Refactoring umfasst Maßnahmen zum Vereinfachen des Codes, ohne das Verhalten der Anwendung zu beeinflussen. Das Umbenennen, Verschieben, Extrahieren, Zusammenfassen oder Löschen von Code sind dabei die am häufigsten genutzten Methoden.

Ein umfangreicheres Refactoring sollte jedoch vor der Durchführung ausgiebig geplant sein, um nicht das eigentliche Ziel der Qualitätsverbesserung zu gefährden. Denn abhängig von der Codebasis im Projekt kann schon eine einzige irrtümlich vertauschte Codezeile oder ein falsch geänderter Variablenname zu plötzlich auftretenden Problemen im Livesystem führen.