Refactorings – Eine Übersicht

Warum ist Refactoring wichtig?

Kennst du das? Dein Code funktioniert, aber nach einigen Monaten versteht ihn niemand mehr – nicht einmal du selbst. Hier kommt Refactoring ins Spiel: Es verbessert die Lesbarkeit und Wartbarkeit, ohne die Funktionalität zu ändern.

Was ist Refactoring?

Mit Refactoring bezeichnet man Änderungen am Code, die einerseits die Qualität der Codebasis verbessern und dabei gleichzeitig die Funktionalität aufrecht halten. Dabei müssen wir vor allem sicherstellen, dass keine neue Fehler entstehen, denn das äußere Verhalten der Software muss erhalten bleiben. Das beste Werkzeug dazu sind automatisierte Tests.

Refactorings können auch die Grundlage schaffen, nachträglich Tests ergänzen zu können. Was wie ein Henne-Ei-Problem klingt, beginnt häufig mit Integrationstests. Diese stellen die Korrektheit eines größeren Bereichs der Codebasis sicher. Auf dieser Grundlage können wir anschließend innerhalb des testabgedeckten Code refaktorisieren, um so auch effizient Unit Tests ergänzen zu können. Ohne Tests ist Refactoring riskant – aber oft ist alter Code nicht getestet. Daher beginnt man mit Integrationstests, um eine erste Absicherung zu schaffen. Danach kann man schrittweise weiter optimieren.

In der Softwareentwicklung ist es zentral, die Lesbarkeit des Codes ständig sicherzustellen. Dies gelingt nicht unbedingt sofort beim ersten Schreiben des Code. Auch hier kommen nachträgliche Refactorings zum Einsatz, um Code, der ursprünglich komplex war, zu vereinfachen und so die Wartbarkeit zu verbessern.

Beim Schreiben neuer Funktionen sind Refactorings oft der Ausgangspunkt, um Entwicklern die Möglichkeit zu geben, die bestehende Implementierung zu erweitern.

Martin Fowler hat in seinem Buch Refactoring: Improving the Design of Existing Code viele Beispiele gegeben. Seit dem ist das Thema Refactoring in der agilen Softwareentwicklung zum Standard geworden.

Wie führt man Refactoring von Code durch?

Das Ziel ist die Qualität der Codebasis zu erhöhen, ohne dabei das Verhalten der Software zu verändern. Vereinfachungen am Code, mit dem Ziel die Verständlichkeit zu verbessern, setzt man am Besten in kleinen Schritten um. Refactorings wie das Umbenennen von Klassen, Methoden, Parametern oder Variablen sind mit den heutigen Tools sehr leicht umzusetzen. Die Vereinfachungen, die von modernen Entwicklungsumgebungen (IDEs) ausgehen, sind enorm. Durch die KI und IDEs wie Cursor oder Windsurf macht das Thema Refactoring gerade nochmal einen gewaltigen Sprung. Doch auch hier ist es wichtig, automatisierte Tests zur Kontrolle der Korrektheit bereit zu haben.

Refactoring verbessert die Qualität des Codes im Sinne der beiden Werte Wandelbarkeit und Korrektheit. Ob kleine Änderungen oder große Umstrukturierung. Am Ende muss es um das Erreichen dieser Werte gehen. Das Beheben von Prinzipienverletzungen ist wichtig. Jedoch müssen beim Einsatz aller Refactoring-Techniken die Werte im Vordergrund stehen.

Die Durchführung von Refactorings sollte immer an einem Ziel orientiert sein. Entweder es geht um ein neues Feature oder um die Beseitigung eines Bugs. Wird „mit der Gießkanne“ refaktorisiert, ist der zeitliche Aufwand oft zu hoch und das Risiko zu groß, Schaden anzurichten.

Beispiele für Refactorings, die häufig zum Einsatz kommen:

  • Extract Method (z.B. große Methode in mehrere kleine aufteilen)
  • Rename Variable (bessere Lesbarkeit)
  • Inline Variable (unnötige Variablen entfernen)
  • Replace Magic Numbers with Constants

Einige Ideen zur konkreten Vorgehensweise habe ich in diesem Blogbeitrag beschrieben.

Was sind die Vorteile des Refactorings?

Ultimativ geht kein Weg daran vorbei, Code durch viele kleine Refactorings leichter verständlich zu machen. Allerdings muss dabei jeweils das Ziel des Refactoring berücksichtigt werden. Refactoring ist eine Disziplin, die im Zweifel viel Zeit braucht und damit die Kosten erhöht. Wenn mit einem Refactoring die Erweiterbarkeit des Quellcodes verbessert wird, diese Erweiterbarkeit aber tatsächlich nicht benötigt wird, geht der Schuss nach hinten los.

Der Vorteil liegt somit üblicherweise nicht darin, den Code noch flexibler und generischer zu machen. Im Gegenteil sollte das Vorgehen beim Refactoring darauf ausgerichtet sein, den bestehenden Code einfacher zu machen, um ihn schneller verstehen zu können. Im Sinne der Effizienz ergibt sich der Vorteil des Refactorings daher häufig eher daraus, Dinge zu vereinfachen und klarer zu gestalten. Die kognitive Last, die ich als Entwickler aufbringen muss, um die Codebasis zu verstehen, muss reduziert werden. Es geht also um eine Vereinfachung der Struktur.

Gibt es auch Nachteile des Refactorings?

Es gibt meiner Beobachtung nach zwei Probleme bei Refactoringmaßnahmen:

  • Sie dauern zu lange
  • Die Korrektheit leidet

Beim Thema Zeit bzw. Aufwand ist es wichtig zu berücksichtigen, dass Refactorings trotz aller Wichtigkeit kein Selbstzweck sind. In der Regel sollten umfangreiche Refactorings nur im Kontext eines neuen Features oder eines Bugfix durchgeführt werden.

Kleine Refactorings am bestehenden Code sind durch die Pfadfinderregel abgedeckt: „Verlasse einen Platz immer besser, als du ihn vorgefunden hast“. Allerdings sollte der Zeitaufwand hier tatsächlich nur im Bereich weniger Minuten liegen. Umfangreiche Strukturänderungen sind dadurch nicht abgedeckt.

Wenn ich bei einem Refactoring merken sollte, dass es zu lange dauert, kann ich mich entscheiden, abzubrechen. Solange ich zu Beginn die Versionskontrolle genutzt habe, kann ich an dieser Stelle mit einem Revert alle Änderungen verwerfen. Dies erfordert allerdings, dass ich im Prozess der Umstrukturierung präsent bleibe und reflektiere. Manchmal ist es besser, abzubrechen, als immer mehr Zeit zu investieren mit am Ende vielleicht zweifelhaften Ergebnissen.

Wenn beim Refactoring etwas kaputt geht, liegt das in der Regel an einer zu geringen Testabdeckung. Im Zweifel sollte man daher vor einem Refactoring eine Coverage Analyse durchführen, um so den Überblick zu erhalten, ob Zeilen, die vom Refactoring betroffen sind, ausreichend durch Tests abgedeckt sind. Manuelle oder automatisierte Tests sind hier zwingend.

Was hat es mit den technischen Schulden auf sich?

Ursprünglich geprägt hat den Begriff Ward Cunningham. Beim Übergang vom Wasserfall zur agilen Softwareentwicklung hat sich gezeigt, dass durch den (absichtlich!) enger gewählten Scope manche Entscheidung zur Struktur der Software später überdacht werde muss. Insofern gehört das Refaktorisieren zur Agilität dazu. Denn bei jeder Iteration mit neuen Anforderungen kann man feststellen, dass die bisherige Struktur nicht geeignet ist, die neuen Anforderungen aufzunehmen.

Wenn ein Team also wirklich agil entwickelt, steht potentiell vor einem neuen Feature eine Refactoring Maßnahme an. Diese muss dafür sorgen, dass die ursprünglich angemessene Struktur nun den neuen Anforderungen ebenfalls gerecht wird. Neue Features, die hinzugefügt werden, dürfen die Komplexität nicht drastisch erhöhen.

Wie sollte ich Vorgehen beim Refactoring?

In der Programmierung findet durch die KI gerade eine drastische Veränderung statt. Code kann nun durch die KI erzeugt werden. Doch die Qualität ist bei weitem nicht in jedem Fall so, dass man die Vorschläge der KI ungeprüft übernehmen kann. Insofern gehört auch bei dieser geänderten Arbeitsweise das Refaktorisieren zur Arbeit eines Entwicklers dazu.

Refactoring kann Softwarefehler herbeiführen. Daher ist der erste Schritt immer, die Testabdeckung zu überprüfen. Soll die ursprüngliche Funktionalität erweitert werden, orientiert man sich beim Umgestalten des Codes an den neuen Anforderungen. Häufig wird man dabei Code aus Methoden extrahieren, um diese bestehende Funktionalität an anderer Stelle verwenden zu können. Die Absicht von Refactoring ist hier die Vermeidung von Dopplungen. Am Ende will ich den Code vereinfachen, damit trotz neuer Features alles leicht verständlich bleibt.

Ist dieses Ziel klar, kann man mit einfachen Refaktorisierungen beginnen, die per Definition toolgestützt sind. Dabei kann auch die Testabdeckung etwas geringer sein, weil die IDE sicher stellt, dass die Maßnahme nicht zu neuen Fehlern führt.

Bevor man mit einer Veränderung am Code beginnt, sollte die Versionskontrolle zur Hilfe genommen werden. Ein Commit aller anstehenden Änderungen stellt sicher, dass mit einem Revert der Ausgangszustand effizient wieder hergestellt werden kann. Nach jedem größeren Schritt sollte ein Commit erfolgen.

Beim Bugfix stellen Refactorings sicher, dass Erkenntnisse, die ich über den Code gewonnen habe, im Code manifestiert werden. So kann bspw. das Herausziehen einiger Zeilen aus einer umfangreichen Methode dafür sorgen, dass die Bedeutung dieser Zeilen durch einen gut gewählten Methodennamen kommuniziert werden. Auf diese Weise wird die kognitive Last reduziert.

Fazit

Zunächst sollten mir die Unterschiede zwischen einfachen und komplexen Refactorings klar sein. Einfache Refactorings sind solche, die vollständig toolgestützt durchgeführt werden können. Das Risiko, hierbei Fehler an vorhandenem Code neu einzuführen ist eher gering. Die Pfadfinderregel setzt am besten vollständig auf einfache Refactorings.

Für komplexe Refactorings kann man die Mikado Methode verwenden. Allerdings muss hierbei immer ein Ziel an erster Stelle stehen, das von wirtschaftlichen Überlegungen geprägt ist. Neue Features oder Bugfixes sind die beiden wichtigsten Gründe. Sauberer Code ist kein Selbstzweck. Unübersichtlicher Code ist ärgerlich, aber nicht immer vermeidbar. Die Struktur des Quellcodes sollte immer wieder beobachtet und verbessert werden, gleichzeitig müssen die Kosten und der Nutzen für die Anwender im Blick behalten werden.

Unsere Seminare

course
Clean Code Developer Basics

Prinzipien und Tests – Das Seminar wendet sich an Softwareentwickler, die gerade beginnen, sich mit dem Thema Softwarequalität auseinanderzusetzen. Es werden die wichtigsten Prinzipien und Praktiken der Clean Code Developer Initiative vermittelt.

zum Seminar »
course
Clean Code Developer Advanced

Mit Flow Design von den Anforderungen zum Clean Code – Lernen Sie mit Flow Design einen Softwareentwicklungsprozess kennen, der Sie flüssig von den Anforderungen zum Clean Code führt.

zum Seminar »
course
Clean Code Developer Trainer

Seminare als Trainer durchführen – Dieses Seminar wendet sich an Softwareentwickler, die ihr Wissen über die Clean Code Developer Prinzipien und Praktiken bzw. über Flow Design als Trainer an andere weitergeben möchten.

zum Seminar »

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

de_DEGerman