Geleitwort
Erinnern Sie sich noch an das erste Programm, das Sie geschrieben haben? Ich erinnere mich an meins. Es war ein kleines Grafikprogramm, das ich auf einem frühen PC geschrieben habe. Ich begann später als die meisten meiner Freunde mit dem Programmieren. Sicher, Computer waren mir von klein auf bekannt; und ich erinnere mich daran, wie nachhaltig mich ein Minicomputer beeindruckt hat, den ich in einem Büro sah. Aber jahrelang hatte ich keine Gelegenheit, mich an einen Computer zu setzen. Später in meiner Teenagerzeit kaufte einer meiner Freunde einige der ersten TRS-80s. Ich war interessiert, aber auch ein wenig besorgt. Ich wusste, dass ich dem Computer verfallen würde, sollte ich anfangen, mit ihm zu spielen. Er sah einfach zu verlockend aus. Ich weiß nicht, woher ich mich so gut kannte, aber ich hielt mich zurück. Später im College hatte ein Zimmergenosse einen Computer; und ich kaufte mir einen C-Compiler, um mir das Programmieren beizubringen. Damit fing es an. Ich blieb jede Nacht auf, um dieses und jenes auszuprobieren und den Quellcode des Emacs-Editors zu studieren, der dem Compiler beilag. Es machte süchtig, es war anspruchsvoll, und ich liebte es.
Ich hoffe, Sie haben ähnliche Erfahrungen gemacht und haben die reine Freude erlebt, Dinge auf einem Computer zum Laufen zu bringen. Fast jeder Programmierer, den ich frage, kennt dieses Gefühl. Diese Freude gehört zu den Motiven, die uns diese Arbeit haben wählen lassen; aber wohin ist sie im Alltag verschwunden?
Vor einigen Jahren rief ich abends nach erledigter Arbeit meinen Freund Erik Meade an. Ich wusste, dass Erik gerade einen Beratungsauftrag mit einem neuen Team angenommen hatte, und fragte ihn deshalb: »Wie läuft’s?« Er sagte: »Nicht zu glauben, die schreiben Legacy Code.« Dies war einer der wenigen Male in meinem Leben, bei denen mich eine Äußerung eines Kollegen wie ein unerwarteter Schlag erwischte. Ich fühlte ihn direkt in der Magengrube. Erik hatte das Gefühl punktgenau ausgedrückt, das mich oft beschleicht, wenn ich zum ersten Mal mit einem fremden Team zu tun habe. Seine Mitglieder bemühen sich redlich, aber letztlich schreiben viele Menschen einfach nur Legacy Code. Die Gründe? Vielleicht ist der Termindruck zu stark. Vielleicht wiegt die Last des überkommenden Codes zu schwer. Vielleicht ist einfach nur kein besserer Code vorhanden, mit dem sie ihre Anstrengungen vergleichen könnten.
Was ist Legacy Code? Ich habe diesen Terminus bis jetzt undefiniert verwendet. Betrachten wir die strenge Definition: Legacy Code ist Code, den wir von jemand anderem übernommen haben. Vielleicht hat unser Unternehmen Code von einem anderen Unternehmen übernommen; vielleicht sind die Mitarbeiter des ursprünglichen Teams zu anderen Projekten abgewandert. Legacy Code ist Code eines anderen. Aber im Programmiererjargon bedeutet der Terminus viel mehr als das. Der Terminus Legacy Code hat im Laufe der Zeit zusätzliche Bedeutungen und mehr Gewicht angenommen.
Was denken Sie, wenn Sie den Terminus Legacy Code hören? Denken Sie wie ich an eine verworrene, unverständliche Struktur, an Code, den Sie ändern müssen, den Sie aber nicht wirklich verstehen? Denken Sie an schlaflose Nächte, in denen Sie versuchen, Funktionen hinzuzufügen, die leicht hinzuzufügen sein sollten? Fühlen Sie sich entmutigt? Haben Sie den Eindruck, der Code sei den Mitgliedern Ihres Teams so über, dass ihnen alles egal ist und sie den Code am liebsten sterben sähen. Ein Teil von Ihnen fühlt sich sogar schlecht bei dem Gedanken, den Code zu verbessern. Er scheint Ihre Anstrengungen nicht zu verdienen. Diese Definition von Legacy Code hat nichts damit zu tun, wer ihn geschrieben hat. Die Qualität von Code kann durch viele Faktoren verschlechtert werden; und viele haben nichts damit zu tun, ob der Code von einem anderen Team geschrieben wurde.
In der Branche wird Legacy Code oft salopp zur Bezeichnung von Code verwendet, den man nicht versteht und der schwer zu ändern ist. Aber im Laufe der Jahre, in denen ich verschiedenen Teams geholfen habe, ernste Code-Probleme zu beseitigen, habe ich eine andere Definition entwickelt.
Für mich ist Legacy Code ganz einfach Code ohne Tests. Mit dieser Definition habe ich mir einigen Kummer eingehandelt. Was haben Tests damit zu tun, ob Code schlecht ist? Darauf hab ich eine unkomplizierte Antwort, die ich in diesem Buch immer wieder aus verschiedenen Blickwinkeln darstelle:
Code ohne Tests ist schlechter Code. Es spielt keine Rolle, wie gut er geschrieben ist; es spielt keine Rolle, wie schön oder objektorientiert oder gut eingekapselt er ist. Mit Tests können wir das Verhalten unseres Codes schnell und verifizierbar ändern. Ohne Tests wissen wir nicht wirklich, ob unser Code besser oder schlechter wird.
Vielleicht halten Sie das für streng. Was ist mit sauberem Code? Reicht es nicht aus, wenn eine Code-Basis sehr sauber und gut strukturiert ist? Bitte verstehen Sie mich nicht falsch. Ich liebe sauberen Code. Ich liebe ihn mehr als die meisten Menschen, die ich kenne; doch sauberer Code ist zwar gut, aber allein nicht gut genug. Teams gehen erhebliche Risiken ein, wenn sie große Änderungen ohne Tests durchführen wollen. Es ähnelt der Hochseilartistik ohne Sicherheitsnetz. Es erfordert unglaubliches Können und ein klares Verständnis, was bei jedem Schritt passieren wird. Die Auswirkung der Änderung einiger Variablen genau zu überschauen, ist oft mit der Gewissheit vergleichbar, dass Sie nach einem Salto von einem anderen Artisten an den Armen aufgefangen werden. Wenn Sie in einem Team an Code arbeiten, der derartig übersichtlich ist, sind Sie in einer besseren Position als die meisten Programmierer. Bei meiner Arbeit sind mir Teams mit einem solchen Code selten begegnet. Sie scheinen eine statistische Anomalie zu sein. Und wissen Sie was? Wenn sie nicht mit Testunterstützung arbeiten, brauchen sie für Code-Änderungen immer noch länger als Teams, die systematisch testen.
Es stimmt: Teams werden besser und schreiben von Anfang an klareren Code; aber es dauert sehr lange, bis älterer Code klarer wird. In vielen Fällen wird dieses Ziel nie ganz erreicht. Deshalb habe ich kein Problem damit, Legacy Code als Code ohne Tests zu definieren. Es ist eine brauchbare Arbeitsdefinition, die auf eine Lösung verweist.
Obwohl ich bis jetzt ausführlich auf Tests eingegangen bin, handelt dieses Buch nicht vom Testen. In diesem Buch geht es darum, eine beliebige Code-Basis erfolgssicher zu ändern. In den folgenden Kapiteln beschreibe ich Techniken, mit denen Sie Code verstehen, in eine Testumgebung integrieren, refaktorisieren und funktional erweitern können.
Sie werden beim Lesen dieses Buches bemerken, dass es hier nicht um »schönen« Code geht. Die Beispiele in diesem Buch sind konstruiert, weil ich mit Kunden unter einer Geheimhaltungsvereinbarung arbeite. Aber in vielen Beispielen bemühe ich mich, den »Geist« des Codes wiederzugeben, der mir im Feld begegnet ist. Ich will nicht behaupten, alle Beispiele wären repräsentativ. Sicher gibt es im Feld Oasen mit großartigem Code, aber, ganz ehrlich, es gibt dort auch Code-Basen, die viel schlechter als alles sind, was ich in diesem Buch als Beispiel verwenden kann. Abgesehen von der Vertraulichkeit konnte ich einfach keinen derartigen Code in dieses Buch einfügen, ohne Sie zu Tode zu langweilen und wichtige Punkte in einem Morast von Details zu versenken. Folglich sind viele Beispiele relativ kurz. Wenn Sie ein solches Beispiel sehen und denken: »Der hat ja keine Ahnung – meine Methoden sind viel länger und viel schlechter«, nehmen Sie bitte meinen zugehörigen Ratschlag für bare Münze und prüfen Sie seine Anwendbarkeit, auch wenn das Beispiel einfacher zu sein scheint.
Die hier vorgestellten Techniken sind mit erheblich umfangreicherem Code getestet worden. Die Beispiele sind nur wegen des Buchformats kürzer. Insbesondere können Sie Ellipsen (…) in einem Code-Fragment wie folgt interpretieren: »Fügen Sie hier 500 Zeilen mit hässlichem Code ein.« Ein Beispiel:
In diesem Buch geht es nicht nur nicht um »schönen« Code, sondern noch weniger um »schönes« Design. Gutes Design sollte zu den Zielen jedes Programmierers gehören; aber bei Legacy Code nähern wir uns diesem Ziel schrittweise. In einigen Kapiteln beschreibe ich Methoden, wie man eine vorhandene Code-Basis mit neuem Code erweitern und dabei gute Designprinzipien berücksichtigen kann. Sie können in eine Legacy-Code-Basis Bereiche mit qualitativ hochwertigem Code einführen, sollten aber nicht überrascht sein, wenn bei einigen Änderungen andere Teile des Codes etwas hässlicher werden. Diese Arbeit gleicht einem chirurgischen Eingriff. Wir müssen Einschnitte vornehmen, und wir müssen durch die Eingeweide gehen und gewisse ästhetische Überlegungen beiseitelassen. Könnten die Hauptorgane und Eingeweide dieses Patienten in besserer Verfassung sein? Ja. Vergessen wir deshalb das anstehende Problem, nähen ihn wieder zu und raten ihm zu einer besseren Ernährung und regelmäßigem Training? Dies könnten wir tun; doch hier und jetzt müssen wir den Patienten nehmen, wie er ist, die Mängel beseitigen und ihn gesünder machen. Vielleicht wird er nie um olympische Medaillen kämpfen, aber wir dürfen das »Beste« nicht zum Feind des »Besseren« machen. Die Code-Basis kann gesünder und leichter handhabbar werden. Wenn sich ein Patient ein wenig besser fühlt, ist oft der geeignete Zeitpunkt, ihn zu einem gesünderen Lebensstil zu führen. Genau dies möchten wir mit Legacy Code...