Refactoring in der praktischen Umsetzung

Von Annette Kempf *

Anbieter zum Thema

Im Laufe der Jahre sammelt sich in zahlreichen Softwareprojekten alter, aber weiterhin funktionaler Code an, der – um Entwicklungszeit und Aufwand zu sparen – von Iteration zu Iteration weitergezogen wird. Solcher Legacy-Code verursacht oft Probleme, eine Modernisierung scheint aber oft schwierig. Beim Refactoring oder „Umbau“ solchen Altcodes können Software-Bibliotheken wertvolle Dienste leisten.

Software-Bibliotheken erleichtern die Überarbeitung existierenden Codes.
Software-Bibliotheken erleichtern die Überarbeitung existierenden Codes.
(Bild: Clipdealer)

In allen Software-Entwicklungsunternehmen hat sich über die Zeit eine Menge an funktionalem Code angesammelt. Diesen an anderen Stellen zu verwenden oder mit neuen Funktionen zu erweitern ist eine bestechende Idee, die Entwicklungszeiten verkürzt, damit Kosten reduziert und schneller zum Ergebnis führt. Das Kernproblem dabei: Diese vorhandene Code-Landschaft ist ziemlich heterogen, oft lückenhaft dokumentiert ebenso die Unit-Tests und schon länger wurde der Code nicht von Redundanzen befreit. „Da müsste mal jemand aufräumen“, hört man oft – doch wer? Kann diese Aufgabe des Refactorings nach draußen vergeben werden - und wenn ja, wie macht man das am besten?

Fehler von Anfang an vermeiden

Bild 1: Aufgabenverteilung zwischen dem Kunden und Eclipseina im Beispielprojekt.
Bild 1: Aufgabenverteilung zwischen dem Kunden und Eclipseina im Beispielprojekt.
(Bild: Eclipseina)

Weil jede Änderung am vorhandenen Code grundsätzlich das Potential für Fehler birgt, ist es ganz entscheidend erst die Rahmenbedingungen zu klären, bevor man mit der Einführung neuer Konzepte beginnt. In dieser Phase hat sich der Einsatz einer umfangreichen Checkliste bewährt, um Fragen zu klären, die erheblichen Einfluss auf den zeitlichen und finanziellen Aufwand haben, z.B. ob es sich um ein sicherheitskritisches System handelt. Entwicklungsprozesse laufen zwar heute so gut wie immer nach dem V-Modell oder darauf aufbauenden Prozessen ab, was die Vermutung zulässt, dass sich das Abarbeiten von Refactoring-Projekten in der Praxis kaum unterscheidet. Doch weit gefehlt! Jedes Projekt ist anders. Generelle Aussagen sind immer schwierig. Wie aber das in Bild 1 dargestellte Beispielprojekt zeigt hilft ein strukturiertes Vorgehen dabei, erfolgreich ein Refactoring umzusetzen.

Neben den klassischen planerischen Schritten nehmen die technischen Aspekte in der Umsetzung des Refactorings den größten Raum ein, worauf in den folgenden Abschnitten detailliert eingegangen wird.

Ziele des Refactoring

Ein Refactoring wird mit dem Ziel umgesetzt, bestehenden Programmcode zu optimieren und gefährliche Redudanzen im Code zu eliminieren, indem diese durch verifizierte Bibliotheksfunktionen ersetzt werden. Weitere mögliche Refactoring-Schritte sind formale Verbesserungen für eine bessere Lesbarkeit des Codes sowie die Ergänzung und Korrektur von Spezifikationen und Code Kommentaren.

Refactoring auf der technischen Ebene

Folgende Vorgehensweise hat sich beim Refactoring bewährt:

Analyse von Architektur und Designgrundsätzen

Zuerst sollte der aktuelle Status analysiert werden. Angefangen bei der Spezifikation der Anforderungen, über bestehenden Dokumente der Architektur und des Designs, die Designregeln und die vorhandenen Unit- und Integrationstests. Bei der Analyse wird ein besonderes Augenmerk auf Architektur und Design gelegt.

Im betrachteten Projekt stellte sich heraus, dass für den eingesetzten Mikrocontroller bereits, speziell für diesen Mikrocontrollertyp optimierte Funktionen zur Verfügung stehen.

Diese wurden in Zusammenarbeit von Softwaredesign und Architektur in die für Hardwareabstraktion vorgesehenen Funktionen der Softwarebibliothek EC-LIB integriert. Dies hat auch für die Zukunft den Vorteil, dass die Schnittstellen für die Anwender der Funktionen, also die Softwareentwickler und Tester auch bei einem Wechsel auf eine neue Mikrocontroller-Plattform gleich gehalten werden können.

Im Rahmen der gemeinsamen Analyse und einzelner Architektur-Entscheidungen werden klare Handlungsanweisungen für die Entwickler erstellt.

Detailed Design

Die Entwickler analysieren, in welcher Form die Funktionen, die zu ihren Aufgabenpaketen gehören, durch Einführung der EC-LIB Funktionenbibliothek umgesetzt werden sollen und versuchen dabei, eine möglichst große Vereinfachung zu erreichen.

Da die Dokumentation der bestehenden Funktionen Lücken und zum Teil Inkonsistenzen zur Implementierung aufweist, ist eine enge Zusammenarbeit mit dem Kunden essentiell! Jede noch so kleine funktionale Änderung birgt dabei ein schwer abschätzbares Risiko und muss deshalb mit dem Architekten auf Kundenseite und mit dem Design-Zuständigen im Refactoring Team geklärt werden.

Risiko: Auch wenn Funktionen nur minimal verändert werden, können diese Änderungen in bestimmten Systemzuständen größere Auswirkungen auf das Gesamtsystemverhalten hervorrufen.

Erstellung neuer EC-LIB Funktionen

Bild 2: Ein Beispiel für eine komplexe Reglerfunktion aus der EC-LIB-Funktionenbibliothek: Vollständiges Blockdiagramm eines PID-Reglers mit PT1-Kompensation.
Bild 2: Ein Beispiel für eine komplexe Reglerfunktion aus der EC-LIB-Funktionenbibliothek: Vollständiges Blockdiagramm eines PID-Reglers mit PT1-Kompensation.
(Bild: Eclipseina)

Wird festgestellt, dass bestimmte Funktionen oder einzelne Aspekte in der Umsetzung von Funktionen hilfreich wären, können die EC-LIB-Entwickler diese entweder kundenspezifisch realisieren oder im Rahmen eines Updates allen Kunden der Bibliothek zur Verfügung stellen. Manchmal ist es sinnvoll, auch wenn die EC-LIB das Verhalten durch Kombination mehrerer Funktionen abdecken kann, eine auf den spezifischen Bedarf zugeschnittene Funktion zu erstellen, die einfacher zu handhaben ist und häufiger ein besseres Laufzeitverhalten aufweist, wie z.B. eine komplexe Reglerstruktur (siehe Bild 2).

Jetzt Newsletter abonnieren

Verpassen Sie nicht unsere besten Inhalte

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Software Unit Implementation und Unit Test

Die Entwickler führen die ausgewählte Funktionenbibliothek EC-LIB in den Code ein, indem sie vorhandenen Code durch Bibliotheksfunktionen ersetzen, und reduzieren somit die Anzahl der Codezeilen im Projekt. Parallel zur Änderung im Code arbeiten die Softwaretester an der Vervollständigung der Unit-Tests.

Nach unserer Erfahrung liegen die Herausforderungen nicht nur in einer zu geringen Testabdeckung; meist sind zudem die Anforderungen, die Architektur sowie das Design nur unzureichend beschrieben. Dieser Mangel wird oft beim Erstellen und Nachziehen der Unit Tests als ein Testvorfall festgestellt und ermöglicht eine Anpassung der Spezifikationen der Anforderungen an die Software und Architektur. Beheben lassen sich diese Mängel nur in enger Zusammenarbeit zwischen dem Kunden und dem Test-Team bei der Testerstellung. Das Ziel ist nicht nur alle Fehler zu finden und zu beheben und 100% Testabdeckung zu erreichen, sondern auch eine vollständige und fehlerfreie Spezifikation zu erhalten, so dass die Softwarearchitektur einen hohen Reifegrad für ein besseres gemeinsames Verständnis aufweist.

Vor der Durchführung des Refactorings und der Umsetzung der Tests wird anhand der vorhandenen Spezifikation ein Verständnis der Software Architektur im Test Team aufgebaut. Zum Einsatz kommt in diesem Projekt das professionelle Testwerkzeug TESSY von Razorcat.

Sieht das Refactoring vor, dass die Funktion eines Code-Segments verändert wird, z.B. durch das Bereinigen von Fehlern im Design, erstellt das Test-Team einen entsprechenden neuen Unit Test. Im Idealfall geschieht dies zeitgleich mit der Entwicklung, damit der Test zur Verfügung steht, sobald die Änderungen implementiert wurden.

Sobald das Refactoring für ein Code-Segment abgeschlossen ist, wird dieses zunächst durch eine statische Code-Analyse formal geprüft. Im betrachteten Projekt erfolgte dies mit PC-Lint. Anschließend werden bestehende oder im Vorfeld erstellte Unit Tests auf die einzelnen Softwaremodule angewendet.

Softwareintegration

Die Softwareintegration erfolgt durch den Kunden. Im Beispielprojekt übernimmt der Softwarearchitekt in Absprache mit dem Softwareprojektleiter diese Aufgabe, da die zugelieferten Softwarekomponenten in die Produktlinie, d.h. in verschiedene Softwarelösungen, einfließen sollen.

Risiko: Selbst perfektes Refactoring, welches das statische Modulverhalten nicht verändert, ändert fast immer das Zeitverhalten und somit möglicherweise das Verhalten des Gesamtsystems.

Integrationstest der Umsetzung

Selbst wenn funktional nichts geändert wurde und alle Unit Tests die gleichen Resultate wie vor der Durchführung des Refactorings liefern, kann ein verändertes Zeitverhalten zu bisher nicht bekannten Fehlern führen. Der bestehende Code, und der durch Refactoring mit Funktionen der Softwarebibliothek EC-LIB optimierte Code, unterscheiden sich sehr wahrscheinlich in ihrem dynamisches Verhalten selbst wenn das statische Verhalten identisch ist. Ein Test des dynamischen Verhaltens - z.B. zur Ansteuerung einer mechatronischen Komponente - kann mit Unit Tests nicht real getestet werden. Eine Zusammenarbeit zwischen Kunde und Eclipseina ist in diesem Fall unabdingbar. Die Integration in das Gesamtsystem und der Test auf Systemebene erfolgt dabei beim Kunden und wird durch Eclipseina lediglich unterstützt.

Abschluss und Übergabe der Themen

Ein spezialisierter Trainer führt Schulungen beim Kunden mit den Entwicklern und Testern, welche die Software zukünftig pflegen werden, durch.

Vorteile von Funktionenbibliotheken

Bestehender Code enthält prinzipbedingt viele mathematische und technische Standardfunktionen. Diese aus den einzelnen Modulen zu entfernen und durch einheitliche Funktionen aus einer Bibliothek zu ersetzen, ist ein erster Schritt beim Refactoring. Die einzelnen Funktionen einer Bibliothek, beispielsweise der EC-LIB, sind bezüglich Algorithmik, Laufzeit und Codegröße optimiert, umfassend dokumentiert und enthalten umfangreiche Routinen zur Fehlererkennung bzw. -behandlung. Vom jeweiligen Prozessorhersteller optimierte Funktionsbausteine bzw. in Hardware vorhandene Rechenfunktionen der Mikrocontroller lassen sich per Konfiguration einbinden, um gegenüber dem C-Code der EC-LIB Rechenzeit zu sparen. Die EC-LIB ist sowohl als preiswerte, vorkompilierte Version verfügbar, als auch als Quellcode in C mit vollständig geklärten Lizenzrechten.

Bild 3: Die EC-LIB verhindert, dass der Workflow immer wieder unterbrochen werden muss, um vermeintlich einfache Funktionen zu programmieren.
Bild 3: Die EC-LIB verhindert, dass der Workflow immer wieder unterbrochen werden muss, um vermeintlich einfache Funktionen zu programmieren.
(Bild: Eclipseina)

Für die Softwareautoren vereinfacht und beschleunigt sich durch die EC-LIB die Arbeitsweise: Statt vermeintlich triviale Funktionen für jeden Prozessor immer wieder neu zu programmieren, profitieren sie von einheitlichen Funktionsaufrufen und identischen Schnittstellen ebenso wie von der umfangreichen Dokumentation der Funktionen in der EC-LIB: Die im Doxygen-Format vorliegenden Informationen können direkt in die Entwicklungsdokumentation übernommen werden.

Welchen Vorteil bieten externe Refactoring-Experten?

Für ein Refactoring ist meistens keine Zeit eingeplant und die Änderungen sollen sukzessive im Rahmen von sowieso notwendigen Änderungen durchgeführt werden. Diese graduelle Migration zu neuen Bibliotheken oder Regelwerken im Rahmen der normalen Projektarbeit klingt zwar gut, funktioniert aber in der Praxis nicht wirklich. Ein abgeschlossenes Projekt, bei dem externe Kräfte die Überarbeitung des Codes vornehmen, führt meist deutlich schneller – und günstiger – zum Erfolg.

  • Das externe Team konzentriert sich auf ein Thema. Das sorgt für eine schnellere Bearbeitung und eine systematische Umsetzung; und
  • Praktische, bereits im Code umgesetzte Verbesserungen erleichtern die weitere Bearbeitung

Grob überspitzt lässt sich die Arbeitsteilung beim Refactoring mit externer Unterstützung so zusammenfassen: Der Dienstleister erledigt die systematische Aufräumarbeit und erweitert die Unit-Tests, damit die Entwickler des Kunden in Zukunft effizienter und weniger fehleranfällig programmieren können. Letztere können sich dadurch auf die spannenden, neuen Funktionen und Projekte konzentrieren, neue Produktideen schneller umsetzen und so die Wertschöpfung im eigenen Unternehmen erhöhen.

Annette Kempf, Gründerin und Geschäftsführerin von Eclipseina.
Annette Kempf, Gründerin und Geschäftsführerin von Eclipseina.
(Bild: Eclipseina)

Die Autorin

* Annette Kempf ist seit vielen Jahren als Entwicklerin, Projektleiterin und Führungskraft im Bereich der Querschnittstechnologie Embedded Systeme tätig. Seit 2013 leitet Sie das von ihr gegründete Unternehmen Eclipseina.

(ID:46519469)