Warum schon wieder ein Vortrag über refactoring?
Wie ist das Verhältnis bei den Tätigkeiten eines Entwicklers zwischen neuen Code erstellen und Code warten? Wie viele von den anwesenden Entwicklern haben so angefangen zu Coden in der letzten Woche? Laut Nicholas Zakas kann man von neuen Code erstellen sprechen, wenn ein Entwickler mit einem leeren Editor-Fenster beginnt. (Bücher von Nicholas Zarkas)
Folge: Die meiste Zeit verbringt ein Entwickler damit, Code zu warten. Jedes Mal, wenn Code gewartet wird, stellt sich implizit die Frage, kann soll darf ich diesen Code einem refactoring unterziehen?
Wo lernt man refactoring? Nicht in der Ausbildung, nicht in der Schule nicht im Studium1. - In der Praxis. daraus kann man ableiten:
- refactoring hat mit Erfahrung zu tun.
- refactoring wird unter anderem von dem Team beeinflusst, in dessen Umfeld der Entwickler arbeitet.
Deshalb kann auf dieses Thema nicht oft genug eingegangen werden, und zwar an der (Arbeits-)Stelle, an der es passiert.
Was ist der größte Feind von refactoring? Rockstar Mentalität - ‘Das ist mein Code und der ist so genial und kunstvoll, so dass niemand daran etwas ändern darf.’ Welcher Entwickler produziert den wertvolleren Code? Der Rockstar oder der Teamplayer? sicherlich der Teamplayer, denn was nützt der genialste Code, wenn er nicht gepflegt werden kann, weil keiner ihn versteht.
Oft haben Entwickler das Gefühl, wenn sie refactoring betreiben, dass sie sich freien Fall befinden, und sie wissen nicht, ob am Ende der Fallschirm aufgeht. Ziel ist es nun, dieses Gefühl gar nicht erst aufkommen zu lassen, beziehungsweise mehr Vertrauen in das gear und die eigenen Fähigkeiten zu bringen.
-
Ein Kollege hat mich auf ein Skript der Fernuniversität Hagen aufmerksam gemacht, welches anscheinend als Grundlage für einen Studiengang dient, und in welchem detailliert mit speziellen Methoden auf Techniken des refactoring eingegangen wird. ?
1. Was ist refactoring?
Martin Fowler, Vater des refactoring Prinzips hat gesagt:
“Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
Its heart is a series of small behavior preserving transformations. Each transformation (called a “refactoring”) does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it’s less likely to go wrong. The system is kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring."
Das bedeutet, sobald die Funktionalität sich vor und nach dem refactoring unterscheidet, sprechen wir nicht mehr von refactoring, aber vielleicht von restructuring.
2. Warum refactoring?
“The only reason for refactoring is economics.” (Martin Fowler)
Das Refactoring von Code ist nur dann sinnvoll, wenn dadurch
- das Hinzufügen von neuen features einfacher wird, oder
- das fixing von bugs einfacher ist.
Somit kann ein refactoring aufgrund der Vermutung von neuen features zurückgestellt werden. Wenn man allerdings für das fixen eines bugs zu lange braucht, weil der Sinn des Codes nicht sofort erfasst werden kann, dann ist das ein Grund für ein refactoring.
Refactoring erhöht die Code-Qualität. Die Code-Qualität wird immer wichtiger, weil die Systeme komplexer werden, und schneller entwickelt werden (müssen), und eine geringere Testabdeckung haben.
Refactoring ist das Fitnessprogramm für Code.
Refactoring hat zum Ziel, den Code leichter lesbar zu machen, so dass man sehr schnell versteht, was der Code bewirken soll. Es ist egal, wer den Code verstehen soll. Selbst der Autor wird nach Monaten genauso vor dem Code stehen, wie ein Fremder. Verständlicher Code spart Entwicklungszeit.
Oft ist der Code nach dem refactoring schlanker als vorher, da redundanter Code eliminiert wird. Ist der Code schlanker, ist es leichter ihn zu verändern (bug fixing, feature-request).
Ist der Code nach dem refactoring leichter zu verstehen, können Schwachpunkte leichter erkannt werden. Refactoring ist daher oft die Eingangstüre für ein restructuring.
In agilen Projekten werden Programme schon einmal schnell entworfen, um Ergebnisse zeigen zu können, ohne Rücksicht auf sicher kommende weitere Features. Daher ist gerade dort refactoring ein wichtiges Instrument. Refactoring ist die Chance, das Design der Software kontinuierlich anzupassen und zu verbessern.
3. Wann refactoring?
Code Reviews oder Screeenings können eine gute Gelegenheit sein, um den Startschuss für ein refactoring zu geben.
- Selten wird refactoring geplant mit einem eigenem Task umgesetzt.
- Hierbei handelt es sich meist um restructuring.
- Nachteil:
- schwer bei Stakeholdern durchzusetzen
- oft keine Zeit dafür
- refactoring kann / sollte im Rahmen eines Tasks durchgeführt werden.
- Pfadfinder Regel: Man verlässt einen Ort (den Code) in einem besseren Zustand, als man ihn vorgefunden hat.
- Das Refactoring kann die Ausgangslage für bug fixing und feature-request wesentlich verbessern. Änderungen zu einem Task können im SVN zum Beispiel auch zwei Mal abgestellt werden: Beim ersten Mal erfasst man die vorausgehenden refactoring Maßnahmen, beim zweiten Mal die funktionellen Änderungen.
- Ist der Aufwand für das refactoring sehr groß (und nur dann), sollte dies mit dem PM abgeklärt werden.
- Es darf aber nur der Code einem refactoring unterzogen werden, der Bestandteil des Tasks ist. Eine Abweichung hier kann zu fatalen Folgen und totalem Wildwuchs führen.
- bei einem neuen feature
- zwei Hüte Metapher von Kent Beck: Bauarbeiter Helm für das Hinzufügen von neuen Funktionen und eine Baseballcap für das refactoring. Es soll immer zwischen den beiden Hüten gewechselt werden. Beim Wechsel der Hüte muss ein stabiler Zustand (compilierber, testbar) erreicht worden sein.
Wie sag ich es meinem Chef? Gar nicht! Refactoring ist Teil des Entwicklungsprozesses und muss daher vom Entwickler in die Beurteilung eines Tasks hinsichtlich des Aufwands einkalkuliert werden.
4. Wie refactoring?
Wo fange ich an?
Die Schnittstellen des Codes für das refactoring müssen vorher klar sein: Was ist der Input und der Output der Einheit, die einem refactoring unterzogen wird.
Wird eine Weinpresse optimiert, muss vor und nach dem Optimieren die Qalität der Reben und die des Weins identisch sein, erst dann kann die Länge des Hebels erweitert werden, um die Kraft zu minimieren.
Diese Schnittstellen sollten mit einem möglichst automatisierten Test überprüfbar sein. Die Fehlerwahrscheinlichkeit sinkt mit der steigenden Qualität und der steigenden Code-Abdeckung des Tests. Der Test darf durch Änderungen im refactoring nicht angefasst werden.
Beispiel Lohn Änderungen wie zum Beispiel die Performance Verbesserungen wären ohne Tests in Lohn nicht denkbar gewesen.
duplicate code
Martin Fowler fordert in seiner Dreier Regel, dass man spätestens beim dritten Copy and Paste refactoring betreiben sollte. Manchmal ist es dann schon zu spät, denn woher weiß man denn, dass man schon einmal kopiert hat und wo steht das Duplikat.
Lange Methoden
Eine Methode ist schwerer zu verstehen, je länger sie ist.
Bei dem Versuch ein Methode in verschiedene kleinere aufzuteilen (zu delegieren) kann es zu dem Problem kommen, dass die neuen kleineren Methoden lange Parameterlisten haben. Diesem Problem kann vor allem wenn sich die Parameterlisten in Teilen gleich sind, durch sogenannte Methodenobjekte gelöst werden.
Ganz Große Klasse
Ist eine Klasse zu groß, sollte sie in mehrere kleine zerlegt (delegiert) werden. Ein Hinweis für die Durchführbarkeit ist eine lange Instanz-Variablen Liste.
Lange, lange, lange Parameterlisten
Lange Parameterlisten können bei Deckungsgleichheit in mehreren Methoden sinnvoll durch Parameter-Methoden ersetzt werden.
Divergierende Änderungen
Gibt es für eine Klasse zu viele trigger, kann dies ein Hinweis dafür sein, aus dieser einen Klasse zwei verschiedene zu machen.
Anders herum kann es sein, wenn sich Änderungen an vielen verschiedenen Stellen auswirken, dass diese Stellen vielleicht besser an einem Ort gesammelt werden sollten.
Methoden verschiiieben
Benutzt eine Methode viele Abfragen einer anderen Klasse, kann es sinnvoller erscheinen diese oder Teile daraus in die andere Klasse zu verlegen.
Asymmetrie
Die gleiche Funktionalität wird an verschiedenen Stellen unterschiedlich gelöst. Dies kann beim bug fixing zu Problemen führen, weil immer wieder unterschiedlich an das Problem herangegangen werden muss.
technical debt
Stolpert man wiederholt über den gleichen Fehler und versucht man wiederholt diesen mit einer vorläufigen Lösung zu umschiffen, dann ist dies ein Zeichen dafür, diese technical debt endlich aufzulösen.
Umverstndlichär Code
Oft kann eine einfache Namens-Änderung einer Variable, Methode oder Klasse wesentlich zur Klarheit des Codes beitragen.
5. Wie drücke ich den Start Button?
Welche Gründe sprechen gegen ein refactoring?
- “Nach dem refactoring geht meistens nichts mehr.”
- “Ich kann nicht abschätzen, ob ich es in den Griff bekomme.”
- Der Entwicklungsaufwand ist meistens momentan größer.
- Es gehört ein wenig Mut, Überwindung und Erfahrung dazu, auf den Startknopf zu drücken. Diese Eigenschaften sind nicht bei jedem Entwickler gleich stark ausgeprägt.
Warum sträuben sich Entwickler gegen refactoring?
- Entwickler haben Angst vor dem refactoring, weil die Auswirkungen ungewiss sind.
- Gegenmaßnahmen:
- viele kleine refactorning Einheiten
- Testabdeckung erhöhen
- Akzeptanztests einführen
- Gegenmaßnahmen:
- In einem Team gibt es unterschiedliche Meinungen zum Thema refactoring (oder über Software-Architektur und Coding Style / Standards).
- Gegenmaßnahmen:
- Kommunikation verbessern
- code reviews
- parallel programming (4-Augen-Prinzip), aber das ist nicht jedermanns Sache
- Gegenmaßnahmen:
- Wo hört refactoring auf und wo beginnt restructuring? (Revolution!)
- Gegenmaßnahmen:
- Risikoabwägung vor dem refactoring: Wie wahrscheinlich ist es, dass ich in einem restructuring ende?
- Unit-Tests
- hohe Testabdeckung
- Gegenmaßnahmen:
- “Ich kann mich auf meine IDE nicht verlassen.”
- Beispiele
- Beim renaming werden nicht alle notwendigen Variablen, Konstanten, Methoden und Klassennamen erfasst.
- RM: renaming von globalen Bezeichnern funktioniert in Delphi nicht
- Gegenmaßnahme
- einen Kollegen fragen, der sich damit auskennt
- Einweisung in Funktionalität am Arbeitsplatz
- Beispiele
- Nach dem refactoring sind mehrere kleinere Einheiten (Lego) zu pflegen, als vorher.
- Es erfordert Disziplin, die dependencies strukturiert, logisch und von der Anzahl her klein zu halten.
- Gegenmaßnahmen:
- Es gilt hier ein gesundes Mittelmaß zu finden. Dieses entwickelt sich im Team am schnellsten in Rahmen von Code reviews und screenings.
- Mehrere kleinere Module sind nicht schwerer zu durchschauen, wenn diese sauber strukturiert sind, aussagekräftige Bezeichner verwendet werden und das Prinzip ‘teile und herrsche’ angewendet wird.
- Das refactoring hat womöglich Auswirkungen auf Bereiche, die nicht in meiner Verantwortung liegen, und die eventuell gerade von anderen Entwicklern bearbeitet werden.
- Gegenmaßnahmen
- Kommunikation verbessern
- Continouos Delivery
- Es kann eine vorläufige Bridge zur alten Schnittstelle eingerichtet werden, bis die neue im restlichen Code angekommen ist; die Bridge und die alte Schnittstelle sollte so bald wie möglich komplett entfernt werden.
- Gegenmaßnahmen
- Was mache ich bei Datenbank-Änderungen?
- Gegenmaßnahmen:
- vermeiden
- oder es ist ein Objektmodell vorhanden
- Gegenmaßnahmen:
Quellen
- Vortrag von Michael Stahl und Jörg Barthold mit dem Thema “Nachhaltigkeit und Evolution von Softwarearchitekturen” auf der OOP 2014
- Keynote von Martin Fowler auf der OOP2014
- Buch “Refactoring - Wie Sie das Design vorhandener Software verbessern” von Martin Fowler, erschienen 2000;
- Vortrag von Nicolas Zarkas über ‘Maintainable Javascript’ auf youtube
Datum 22.03.2014 / Text Thomas Steglich / Photo / Artikel als PDF ( KB) / tweet Artikel / feedback Formular.