Einstieg in Brownfield-Projekte
Von Carsten
Da ich mich gerade mitten in einem sog. Brownfield-Projekt befinde, d.h. einem Projekt mit hauptsächlich gewachsener Architektur, mit fehlender Testabdeckung, mit manuellem Build- und Deploymentprozess und mit hohem manuellen Test- und Änderungsaufwand, will ich hier meine Erfahrungen schildern und einen Weg aufzeigen, wie man in solche Projekte einsteigen kann. Auf heise developer gibt es gerade eine Artikelserie zum Thema Brownfield, die von den beiden Clean Code Initiatoren Stefan Lieser und Ralf Westphal verfasst wird. Und nachdem ich den Artikel gelesen habe, habe ich festgestellt, dass ich genau der dort beschriebenen Vorgehensweise folge. 🙂
Der Buildprozess
Der erste Schritt für mich war das Verstehen des Buildprozess. Ohne einen funktionieren Buildprozess hat man als Entwickler keine Möglichkeit, überhaupt einen Fehler in seinem Code festzustellen, der das fehlerfreie Übersetzen der Software verhindert. Als allererstes muss man also (nach dem Einrichten des VCS natürlich) die Software bauen – möglichst in einem einzigen Schritt. Ein Merkmal von Brownfield-Projekten ist aber gerade, dass der Buildprozess manuell, kompliziert und fehleranfällig ist. Es gilt also herauszufinden, wie der Ablauf aussieht, wo evtl. festverdrahtete Pfadnamen im Spiel sind, wo die Ergebnisse abgelegt werden und was in welchem Schritt weiterverwendet wird. Anschliessend habe ich angefangen, die weit verstreuten Verzeichnisse mit Ergebnissen in Form von JAR, WAR und EAR-Dateien zusammenzuführen und in einem Verzeichnis abzulegen, dass ich auch in meinem Eclipse-Workspace sehen konnte. So habe ich direkt nach dem Build das Ergebnis gesehen und konnte die Änderungen und neu erstellten Dateien mitverfolgen.
Testautomatisierung
Der zweite Schritt ist eigentlich kein Schritt, sondern ein Prozess. In Brownfield-Projekten sind automatisierte Tests so gut wie nicht vorhanden. Weder Unit- noch Integrationstests werden regelmässig und automatisiert ausgeführt. Daher sind Änderungen mit einem hohen Risiko verbunden und als Entwickler neige ich dazu,änderungen so klein wie möglich zu halten und es ist immer eine latente Furcht mit änderungen verbunden. Wer weiß schon so genau, was man alles kaputt macht. Der Code ist in den Jahren so weit verrottet, dass man keine Möglichkeit hat, alle Seiteneffekte und Auswirkungen der änderung zuüberblicken. Also besteht der zweite Schritt in der stetigen Erstellung von automatisierten Tests. Vor jeder änderung versuche ich Unit Tests zu schreiben, die eine Dokumentation des aktuellen Verhaltens sein sollen. Anschliessend kann ich änderungen am Code vornehmen, und die unveränderte Funktion durch meine Testsüberprüfen. Leider ist es im Brownfield-Projekt sehr schwer, auf Unit-Ebene Tests zu schreiben, da es sehr viele innere Abhängigkeiten zwischen den Softwaremodulen gibt. Die Tests sind daher eher Integrationstests, aber durch intensives Mocking (mit meinem Lieblingsmockframework JMockIt) versuche ich die Abhängigkeiten so niedrig wie möglich zu halten. Das gelingt manchmal gut, manchmal schlecht. Es ist nicht immer möglich, gute Tests zu schreiben – aber es ist immer möglich, überhaupt Tests zu schreiben.
Automatisierter Build
Als nächsten Schritt nehme ich mir den automatisierten Build vor, der auch die Ausführung der automatisierten Tests enthält. Dazu muss der Buildprozess angepasst werden, in meinem Fall also einige neue Targets in die Ant-Skripte eingefügt werden. Das ganze werde ich erstmal auf meinem Rechner machen, aber früher oder später sollte der gesamte Build auf einem dedizierten Rechner ausgeführt werden. Es gibt bereits zu viele Umgebungsvariablen, zu viele Bibliotheken, die auf den Entwicklungsrechner installiert und eingerichtet werden müssen, damit der Build läuft. Egal, ob der Build auf einem (beliebigen) Entwicklerrechner oder auf einem eigenen Buildrechner erfolgt, er muss immer mit einem einzigen Klick zu starten sein. Alle manuellen Schritte müssen eliminiert werden, alle vor- oder nachbereitenden Tätigkeiten müssen automatisiert werden. Erst wenn jeder Mitarbeiter einen Build erstellen kann, ist schluss. Dann verliert der Vorgang seinen Schrecken und muss nicht immer vom selben „Buildbeauftragten“ ausgeführt werden.
Continuous Integration
**** Der nächste logische Schritt ist dann die Einführung von Continuous Integration, wahrscheinlich mit Hudson. Damit soll bei jederänderung im VCS ein Build ausgelöst werden, so dass auch die Notwendigkeit von „Build-Tagen“ entfällt. Ausserdem muss man nicht mehr einen Entwickler mit der Erstellung von Builds belästigen, wenn er eigentlich etwas besseres zu tun hätte… 😉 Und schliesslich gehört zur CI auch die Festlegung von Orten, wo die Ergebnisse erwartet werden. Jeder weiß dann, wo die letzte Version liegt. Und jeder kann sich dort bedienen.
Continuous Deployment
Das wäre die Krönung. Automatisches Deployment auf alle Umgebungen. Aber bis dahin ist der Weg noch so weit, dass ich mich erst später damit beschäftige.