Qualitätssicherung Die Problematik bei der Qualitätsmessung von Legacy Code

Dr. Richard Kölbl *

Anbieter zum Thema

Qualitätsmessung und Test von Legacy Code stellen gewichtige Herausforderungen dar. Im besten Fall muss es gar nicht erst dazu kommen, dass Code unkontrolliert wuchert.

Logisches Gestrüpp: Mixed Mode geht der Problematik bei der Qualitätsmessung und Test von Legacy Code auf den Grund
Logisches Gestrüpp: Mixed Mode geht der Problematik bei der Qualitätsmessung und Test von Legacy Code auf den Grund
(Bild: Tirza van Dijk - Unsplash.com)

Die Thematik „Qualitätsmessung und Test von Legacy Code“ ist hochaktuell. Unter dem etwas unscharfen Begriff Legacy Code versteht man den überlieferten, alten Bestand eines Programmcodes. Dabei ist nicht das absolute Alter des Codes entscheidend, sondern die Umstände, unter denen mit diesem Code gearbeitet wird und die dazu führen, dass der Code einen älteren, nicht mehr aktuellen Softwarestand repräsentiert.

Gleichwohl muss dieser Code nicht zwingend seine Funktionalität eingebüßt haben. Vielmehr ist es aus Kostengründen oft günstiger, diesen alten Code beispielsweise in eine neue Umgebung oder auf eine neue Plattform zu übernehmen und an die erforderlichen Schnittstellen anzupassen. Dieses Verfahren wird in der Praxis häufig angewandt und stellt für sich genommen noch nicht das Problem dar, das hier betrachtet werden soll.

Nicht selten sind aktuelle Softwareprodukte schalenartig aufgebaut, so dass die Funktionalität der Software größtenteils in ihrem Kern abgebildet ist, während die Anpassungen an die sich wandelnde Umwelt und ihre Schnittstellen in der Peripherie des Programmcodes liegen.

Daher wird oft vom „core“ des Programms gesprochen, in dem seit geraumer Zeit zentrale Funktionen der Software liegen, der ebenso lange durch den praktischen Einsatz bewährt ist - und den (unter anderem) aus diesem Grund niemand anfasst. Hier taucht der Umriss des Problems auf: der alte, überlieferte Programmcore, der unter Umständen eben doch Fehlfunktionen produzieren kann.

In der Praxis sind nicht selten für den Programmcode kaum oder keine hinreichenden Spezifikationen oder Kommentare vorhanden, ebenso wenig wie Vorgaben für das Verhalten bei Bedienung innerhalb des Definitionsbereichs, geschweige außerhalb davon.

Nicht selten wird aber für Legacy Code dieser Art eine Qualitätsaussage notwendig. Die Qualität einer Software wird durch systematische Tests gemessen. Und hier zeigt sich der Kern des Problems: Die relevanten, auf den Standards des ISTQB basierenden Testmethodiken gehen vom Vorliegen normativer Vorgaben und von einem geordneten Entwicklungsprozess aus. Tatsächlich zeigt die Praxis, dass beides oft genug nicht der Fall ist.

Analysiert man Problemfälle dieser Art genauer, ergeben sich oft weitere Umstände: Die Entwicklung geht ohne oder unter Missachtung der Prozessvorgaben zur Qualitätssicherung vor sich oder es fehlt das notwendige Wissen für eine State-of-the-Art Softwareentwicklung oder die Einsicht in die Notwendigkeit eines angemessenen, dem Entwicklungsprozess gleichberechtigt beigeordneten Testprozesses fehlt.

Unter solchen Umständen entsteht zwar durchaus lauffähiger Code, dessen Struktur und Architektur aber zumindest teilweise schwer zu verstehen und damit nur unter unnötig hohem Aufwand oder überhaupt nicht testbar ist. Gewachsener Code dieser Art kann als logisches Gestrüpp bezeichnet werden, der bei der Überführung in neue Umgebungen oder bei der Addition neuer Funktionalitäten unerwartete Fehlfunktionen produziert.

Dennoch gibt es Möglichkeiten, undokumentierten und gewachsenen Code bis zu einem gewissen Grad zu testen. Dieser rein technische Aspekt soll jedoch hier nicht behandelt werden, sondern die Ursachen für das unkontrollierte Wuchern von Code und die Möglichkeit, dies zu verhindern.

Gründe für den Test von scheinbar bewährten Codes

1. Zunehmend kommt von Abnehmern zugelieferter Software, ob innerhalb eines Konzerns oder zwischen unabhängigen Zulieferern, die Anforderung von Qualitätsnachweisen sowohl des gelieferten Produkts als auch der Prozesse (Zertifizierung) und der beteiligten Mitarbeiter.

2. Bewährung von Programmlogiken im Feld gibt keinesfalls die Sicherheit, bei Übertragung in neue Umgebungen oder auf neue Plattformen Fehlfunktionalitäten ausschließen zu können. Im Gegenteil sind gerade solche Umzüge aus verschiedenen Gründen gerne fehleranfällig: Zum einen wird der alte Code neuen Parametrisierungen ausgesetzt, die unter Umständen bislang nie auftraten und dadurch Fehler aufdecken können.

Zum anderen ist der überlieferte Code in den Fällen, um die es hier geht, eben nicht durch Kommentare oder Spezifikationen erklärt, so dass bei der Anpassung an neue Schnittstellen leicht Fehler gemacht werden können.

3. Einen Testprozess einzurichten oder zu verbessern, ein verbessertes Wissen und Mindset zu schaffen kann mehrere Monate bis zu anderthalb Jahren oder länger dauern. Es ist daher von Vorteil, rechtzeitig für die Fälle 1. und 2. vorbereitet zu sein.

4. Tests in Form von Code-Reviews haben unter anderem den Vorteil, dass die Wissensbasis in der Entwicklermannschaft wesentlich verbreitert wird. Zusätzlich können sie, adäquat umgesetzt, als eine Art Schulung und Transfer von Know-how dienen.

5. Eine nachprüfbare Qualitätsaussage über ein Produkt ist ausschließlich über einen systematischen Testprozess zu erhalten.

Der Kunde, ob Endverbraucher oder weiterverarbeitender Zwischenabnehmer, braucht im komplexen Marktgeschehen ein zuverlässiges Merkmal, an dem er sich orientieren kann. Aus diversen, einsichtigen Gründen ist es dem Kunden vielfach nicht möglich, Prüfungen des Produkts selbst vorzunehmen. Gleichzeitig steigen die Ansprüche, weswegen die Notwendigkeit von Qualitätsaussagen in der Zukunft mit Sicherheit weiter zunehmen wird.

Ursachen für das Wuchern logischen Gestrüpps

Welche Umstände führen dazu, dass statt wohlstrukturiertem und testbarem Code ein schwer handhabbares logisches Gestrüpp entsteht? Es wurde bereits zusammengefasst: Die Vorgaben für das Was und das Wie der Entwicklungstätigkeit werden nicht beachtet. Unter „Was“ können alle Zielvorgaben für das zu bauende Softwareprodukt subsummiert werden: welche Funktionalitäten werden erwartet, welche Schnittstellen gibt es, welche Eingabebereiche sind als gültig zu definieren, wie ist mit ungültigen Eingaben umzugehen - nur um einige Beispiele zu nennen.

Das „Wie“ definiert die Vorgaben, deren Einhaltung die Erreichung dieser Vorgaben mit einem hohem Maß an Qualität erreichen lassen sollte: Codier-Richtlinien, Fristen und Checklisten für Codereviews, ein klar definierter Prozess für dynamische Softwaretests und dergleichen.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Softwareentwicklung und DevOps

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

Eine Einhaltung dieser Vorgaben setzt voraus, dass sie existieren und dass ihre Einhaltung eingesehen und gelebt wird. Aber selbst wenn diese Voraussetzungen gegeben sind, kann mangelnde Kommunikation zwischen den Akteuren sowie Zeitdruck wegen Markt- oder Kundenanforderungen zu Unterschlagungen führen. Schließlich bedingt eine nicht ausreichende Bemannung, dass beispielsweise die Rolle des Reviewers oder des Testers nicht adäquat ausgefüllt wird.

Hinzu kommt, dass ein systematischer Softwaretest ein Mindestmaß an Wissen zu Testmethodiken voraussetzt. Gleiches gilt im Übrigen für den systematischen Test der gesamten internen Logik eines Produkts. Es besteht nach meiner Erfahrung kaum ein Unterschied zwischen den Entwicklungsmannschaften kleiner mittelständischer Betriebe mit zwei bis zwölf Entwicklern und den zuliefernden Abteilungen innerhalb von Großfirmen: In beiden können ähnliche Entwicklungsdynamiken entstehen, in deren Verlauf Code aus dem Augenblick heraus und auf Zuruf heranwächst, ohne größere Abstimmung mit anderen Mitarbeitern oder übergeordneten Anforderungen.

Bei näherer Untersuchung stellt sich die Arbeitsweise bei solchem Vorgehen wie folgt dar: Geprägt durch einen traditionell antrainierten Arbeits- und Kommunikationsstil, der bei weniger komplexen Logiken in der Vergangenheit noch nicht an seine Grenzen gestoßen war, setzt sich der Entwicklungsprozess unverändert in die zunehmende Komplexität (neue Plattformen, steigende Anzahl von Produkten und Rückwärtskompatibilität) fort.

Anstatt diesen inadäquat gewordenen Entwicklungsprozess zu reformieren, treten typischerweise lokale Verdrängungs- und Überbrückungsmethodiken auf: An Fehlstellen des Codes werden unsystematische, nur einen Spezialfall behebende Patches eingebaut, nur einige Anwendungsfälle werden getestet. Mehrarbeit und Überstunden mit entsprechender geringerer Produktivität und höherer Fehleranfälligkeit oder unsystematische Teil-Redesigns unter hohem Zeitdruck sind die Folge. Diese Vorgehensweise bietet das beste Klima für das Wuchern logischen Gestrüpps.

Wirtschaftliche Folgen undurchschaubaren Codes

Dass fehlerhafte Produkte Kosten verursachen können, ist trivial. Diese Kosten sind aber kaum genau zu beziffern, weswegen sie schwer gegen die weitaus besser abzuschätzenden Kosten angesetzt werden können, die durch einen neu eingeführten oder erweiterten Testprozess entstehen. Darin liegt das Kernproblem der Argumentation für die, die dem Testen gerne mehr Raum und Budget zugestehen wollen. Tendenziell werden abstrakte Risiken zu niedrig und konkrete Risiken zu hoch angesetzt.

Diese Erkenntnis stammt aus der Spieltheorie, die auch belegt, dass insbesondere Risiken bei fehlenden negativen Erfahrungen negiert oder zu gering angesetzt werden: Bei wem beispielsweise (zum Glück) noch nicht in die eigene Wohnung eingebrochen wurde, der kann sich diesen Umstand kaum realistisch genug vorstellen und wird daher ohne Anstoß und Beratung kaum genügend Vorkehrungen treffen. Eine adäquate Gegenmaßnahme unter anderen ist die Schaffung eines qualitätssichernden Systems, das zumindest gegen die zwar verhältnismäßig selten auftretenden schweren Fehler immunisiert, die aber in ihrer Auswirkung vernichtend sein können. Hierfür gibt es eine Reihe von Beispielen wie den bekannten Elchtest, die missglückte Ariane-Mission, die Raumsonde Pathfinder und dergleichen.

Vermeiden von logischen Wucherungen

Die Maßnahmen, die dem schlecht kontrollierten Wachstum von Programmlogiken den Boden entziehen sollen, zielen letztlich darauf ab, Voraussetzungen für so viel wohlstrukturierten und spezifizierten Code wie möglich zu schaffen.

1. Schaffung eines angemessenen Mindsets bei Entwicklern und Projektleitern: Bei den Entwicklern selbst, die für die Tester- und Reviewerrolle eingesetzt werden können, ist eine Schulung in den entsprechenden Techniken unerlässlich. Beim Führungspersonal sollte zumindest die Kenntnis des Fundamentalen Testprozesses (ISTQB) vorhanden sein sowie eine Vorstellung von den heute verfügbaren Testprozessmodellen jenseits des vielfach noch gelebten Wasserfallmodells.

2. Festlegung von Qualitätszielen und dorthin führende Richtlinien: Kenntnisse selbst von erprobten Techniken und Herangehensweisen sind nur insoweit sinnvoll und nützlich, wie sie an die Lebenswirklichkeit der jeweiligen Entwicklungsmannschaft angepasst sind. Der Versuch, zu große oder als fremd empfundene Prozesse oder Regelwerke einer Entwicklungsmannschaft überzustülpen, wird scheitern. Die umzusetzenden Richtlinien mit und aus dem Team heraus schrittweise zu entwickeln, zu testen, anzupassen und zu erweitern, erzielt ungleich bessere Resultate. Dabei sollten die etablierten Techniken als Vorlage genommen werden.

Für Reviews beispielsweise ist es unerlässlich, immer anhand einer Checkliste vorzugehen, die dem Autor des zu reviewenden Arbeitsergebnisses schon vorher vorlag und mit ihm abgestimmt wurde. Den Review-Kriterien müssen ausschließlich objektive Qualitätsziele (vgl. ISO/IEC 25000, früher ISO 9126) zugrunde liegen sowie die Sicherstellung einer grundlegenden Testbarkeit.

Oft sind es tatsächlich nur wenige Schritte, die zu einer nachhaltigen Verbesserung der Arbeitsergebnisse führen, beispielsweise die Einführung von Code Guides und regelmäßigen Code Reviews. Richtig durchgeführte Code Reviews haben eine ganze Reihe positiver Effekte auf die Entwicklungsmannschaft.

Daneben sind sie eine der kostengünstigsten Maßnahmen, weswegen ihre Einführung nur sehr empfohlen werden kann: In den Entwicklungsmannschaften werden in der Regel hochqualifizierte Fachleute wie Elektrotechniker, Maschinenbauer, Physiker und dergleichen eingesetzt, die ihre Domäne hervorragend überblicken.

Dennoch ist es Fakt, dass sie bisweilen die Anforderungen ihres spezifischen Fachbereichs nicht in angemessener Weise in eine Programmlogik übertragen können. Hierzu gehören beispielsweise objektorientiertes Design und Architektur, Begriffe wie Kapselung, Modularität und dergleichen. In diesen Fällen genügen oft geringfügige Maßnahmen wie Reviews durch erfahrenere Kollegen oder externe Fachleute, um Defizite zu beheben. Guten Code zu schreiben ist wie gute Texte zu schreiben: Ohne beständige Anleitung, Übung und Review wird kaum jemand über ein gewisses radebrechendes Niveau hinauskommen.

3. Einrichtung von mitwachsenden Prozessen: Wesentlich für eine auf Dauer erfolgreiche und Fehlerkosten vermeidende Entwicklung ist eine dynamische Anpassungsfähigkeit der Entwicklungsprozesse an die sich verändernden Produkte und Produktumgebungen. Ein gestern noch adäquater Entwicklungs- und Testprozess kann durch eine heute neu auftretende Anforderung schleichend ungeeignet werden. Gerade in dem schleichenden Herausfallen aus der Angemessenheit besteht die Gefahr für den Entwicklungsprozess, zu einer begünstigenden ökologischen Nische für logisches Gestrüpp zu werden. Deshalb sollte in regelmäßigen Abständen geprüft werden, ob diese Angemessenheit noch gegeben ist.

Dieser Beitrag ist ursprünglich auf unserem Schwesterportal Elektronikpraxis.de erschienen.

* Dr. Richard Kölbl ist Experte für Embedded-Software bei Mixed Mode in Gräfelfing bei München.

(ID:44415630)