Bei aller Diskussion um Softwarequalität, die beste technische und fachliche Schichtung von Anwendungscode und die optimale Frameworkauwahl wird oftmals ein wichtiger Bestandteil des Softwareentwicklungszyklus komplett vernachlässigt: das professionelle Set-up der Entwicklungsumgebung. Mit kleinen Optimierungen lassen sich hier nicht selten große Effekte erzielen. Ein Überblick über eine Reihe von Verbesserungen, die sich schnell und einfach in den eigenen Arbeitsprozess integrieren lassen und das Entwicklerleben deutlich angenehmer machen.
Eine gute Entwicklungsumgebung ist weit mehr als eine IDE. Vielmehr setzt sie sich aus einer ganzen Reihe von Werkzeugen, aber auch Prozessen und Architekturentscheidungen zusammen, die alle das Ziel verfolgen, den einzelnen Entwickler bzw. das Entwicklungsteam in die Lage zu versetzen, schnell und einfach neue Features auf einer Codebasis zu entwickeln.
In der Praxis ist häufig ein wiederkehrendes Verhalten beim Aufsetzen von Entwicklungsprojekten zu beobachten: Nach den ersten Kundengesprächen passiert eine Weile zunächst relativ wenig, da zuerst die finanziellen und organisatorischen Rahmenbedingungen abgesteckt werden müssen. Außer etwas Prototyping und dem einen oder anderen Proof of Concept geschieht kaum etwas. Kommt dann der Startschuss, muss es von heute auf morgen losgehen – dem Kunden ist ja das erste Release schon innerhalb kürzester Zeit versprochen worden. Nicht selten ist hier schon der Punkt erreicht, an dem für eine ordentliche Anwendungsarchitektur wenig Zeit bleibt – das Drumherum, also eine gut eingerichtete Umgebung, wird da fast automatisch aus den Augen verloren.
Die Folgen hiervon sind nicht immer sofort erkennbar, aber auf Dauer extrem teuer. Im günstigsten Fall dauert die eigentliche Entwicklungsarbeit nur länger als nötig, im schlimmsten Fall entstehen enorme Reibungsverluste, und das Ergebnis ist durchsetzt von Bugs und sonstigen Fehlern. Häufig kommt man nach einiger Zeit zu der Erkenntnis „Eigentlich müssten wir uns da einmal richtig Gedanken zu machen – wenn da nur nicht dieser Zeitdruck wäre, die nächste Version zu liefern.“ Im Projektalltag bleibt daher kaum noch Zeit übrig. Es ist ein wenig wie in der Holzfällerparabel (siehe gleichnamiger Kasten).
Ob wir uns also nun die Zeit vor dem Projektstart nehmen oder erst im Nachhinein – sich über das eigene Set-up Gedanken zu machen und dort Verbesserungen einzuführen, wird sich auf jeden Fall auszahlen.
Als ein Mann im Wald spazieren geht, kommt er an einer Lichtung vorbei, wo ein Waldarbeiter gerade Holz hackt. Er sieht ihm eine Weile zu und bemerkt dabei, dass der Arme sich recht abrackert, müht und plagt, nur weil seine Axt recht stumpf zu sein scheint. Schließlich gibt er sich einen Ruck und spricht ihn an: „Hallo! Warum schärft Ihr denn Eure Axt nicht? Die ist ja total stumpf.“ – Der Holzfäller sieht kurz auf und antwortet außer Atem: „Was? Die Axt schärfen? Nein – ausgeschlossen, dazu habe ich keine Zeit – ich muss doch noch soviel Holz hacken!“
Sehen wir uns hierzu zunächst ein Beispiel aus der Praxis an, das der Autor selbst vor nicht allzulanger Zeit erlebt hat. Zur Entwicklung einer Java-Webanwendung mussten eine Reihe von unterschiedlichen Projekten mit Maven gebaut und vom Entwickler jeweils auf einem lokalen Entwicklungsserver deployt werden, damit diese – ebenfalls lokal – getestet werden konnten. Für den einzelnen Entwickler bedeutet dies die immer wiederkehrende Abfolge von monotonen Einzelschritten, nachdem er die eigentlichen Änderungen umgesetzt hat: den Maven-Build- und Deploy-Prozess starten, den lokalen Tomcat-Webserver durchstarten und auf das Redeployment der Anwendung warten.
Alles in allem ein Prozess von bestenfalls etwa fünf Minuten. Auf den ersten Blick kein allzu großer Zusatzaufwand. Auf den zweiten Blick jedoch ein enormer Zeitfresser. Der Build musste schließlich nicht nur einmalig, sondern regelmäßig den gesamten Tag über durchgeführt werden – an einem normalen Arbeitstag durchschnittlich fünfzehn bis zwanzig Mal. Zusammengefasst hat daher jeder einzelne Entwickler mit diesem Prozedere zwischen einer und anderthalb Stunden pro Tag verbracht – immerhin gute 15 Prozent der Arbeitszeit bei einem Acht-Stunden-Tag.
Aufsummiert geht bei einer Teamgröße von fünf Entwicklern dabei fast ein gesamter Manntag nur für Wartezeit verloren. Oder anders ausgedrückt: Eigentlich ist das Fünferteam nur so produktiv wie ein Viererteam. Ein extrem hoher Preis.
Die Verbesserung dieser speziellen Situation war relativ einfach: Um den skizzierten Prozess zu optimieren, wurde ein kleines Unterprojekt erstellt. Dieses Unterprojekt stellte eine Launcheranwendung zur Verfügung, deren einzige Aufgabe darin bestand, einen embedded Tomcat-Webserver zu starten und die in der bestehenden Quellcodebasis vorhandenen Projekte entsprechend zur Laufzeit in diesen zu integrieren und lokal direkt aus der IDE heraus verfügbar zu machen.
Anstatt den kompletten Build- und Deploymentzyklus zu durchlaufen, konnte nun nach einer Änderung innerhalb der IDE einfach der Launcher neu gestartet werden. Die Turnaround-Zeit, also die Zeit, die von der Umsetzung der Änderung bis zum Aufruf der Webseite im lokalen Webserver benötigt wird, wurde auf unter eine Minute gesenkt. Das gesamte Entwicklungsteam war daraufhin auf einen Schlag um 20 Prozent produktiver. Zusätzlich konnten – sozusagen als „Abfallprodukt“ – direkt die normalen Debugging- und sonstigen Werkzeuge der IDE besser eingesetzt werden.
Natürlich ließ sich dies nicht ganz ohne zusätzlichen Aufwand erreichen. Die Erstellung des Launcherprojekts sowie die Integration in die Codebasis dauerte – alles in allem – etwa drei Manntage. Verglichen jedoch mit der bereits nach einer Woche gewonnenen Zeit eine mehr als lohnende Investition.
Zusätzlich zu den sofort messbaren zeitlichen Ergebnissen sind allerdings noch zwei weitere Punkte erwähnenswert: Durch die Verwendung des embedded Tomcat-Webservers entfällt für zukünftige Entwickler die Notwendigkeit, initial eine lokale Tomcat-Installation durchzuführen und diese auf dem aktuellen Stand zu halten. Last but not least kann sich der einzelne Entwickler wieder auf seine eigentliche Aufgabe konzentrieren: die Implementierung neuer Features. Kaum etwas trägt so stark zu schlechter Stimmung unter Entwicklern bei wie unproduktive Zeit, in der auf das Beenden eines Skripts oder das Starten eines Application Servers gewartet wird. Sehen wir uns nun einige Ansatzpunkte an, in denen wir die Entwicklungsumgebung optimieren können.
Als Ramp-up wird die initiale Einrichtung der Entwicklungsumgebung bzw. der projektspezifischen Einstellungen bezeichnet. Letzten Endes alles das, was ein Entwickler zu tun hat, um in einem Projekt produktiv einsteigen und an der Implementierung teilnehmen zu können.
Typische Aufgaben, die hierbei erledigt werden müssen, sind die Installation und Konfiguration von IDE, lokalen Application Servern oder Datenbanken.
Häufig existieren hierzu innerhalb einer Organisation oder eines Projektteams vorgefertigte Checklisten, die von neuen Teammitgliedern abgearbeitet werden müssen. Nicht selten ist allerdings dieses Wissen auch nur in den Köpfen der bisherigen Entwickler verfügbar, was einen Spaziergang von Pontius zu Pilatus und wieder zurück erfordert.
Beide Vorgehensweisen (das Befolgen einer Anleitung sowie das Durchfragen) kränkeln häufig daran, dass selbst diese Schritte nicht immer konsistent, geschweige denn aktuell, gehalten werden. Wahrscheinlich jeder hat schon einmal einen Satz gehört wie: „Ach ja, das haben wir ja vor zwei Monaten umgestellt. Stimmt. Also dann müssen wir nochmal anders anfangen.“
Letzten Endes führt auch das wieder zu erhöhtem Aufwand (und damit erhöhtem Zeitbedarf), immer wieder zu Frust beim einzelnen Entwickler oder sogar offenen Konflikten im Team. Doch auch hier gibt es gute und gangbare Alternativen. Projekte wie XAMPP [1] verkürzen die Installationsdauer für den Stack von Webserver, Datenbank und weiteren Entwicklungstools enorm, indem sie vorgefertigte Distributionen zur Verfügung stellen. Zwar bringen einen diese Distrubutionen schon einen großen Teil des Wegs vorwärts, doch haben die meisten Projekte immer noch zusätzliche und projektspezifische Anforderungen. Es empfiehlt sich daher, aufbauend auf bestehenden und vorgefertigten Distributionen, eine eigene Projektdistribution für den Entwicklungstoolstack zu erstellen und vor allem ständig aktuell zu halten.
Da die Distributionsanbieter ständig aktuelle Versionen liefern, wird ein Mechanismus benötigt, der basierend auf der Ausgangsdistribution eine eigene Projektdistribution erstellt. Idealerweise besteht hierzu ein Skript (oder kleines Hilfsprogramm), das aus der Quelldistribution und einem Satz von projektspezifischen Einstellungen und Zusatzinhalten die entsprechende Zieldistribution erstellt. Das Ziel hierbei sollte immer sein, diese Zieldistribution so komplett und angepasst wie möglich bereitzustellen.
Es wird jedoch selten gelingen, einem neuem Teammitglied ein einziges großes Archiv (oder einen einzigen Installer) zur Verfügung zu stellen, in dem alle benötigten Ressourcen und Einstellungen vorhanden sind. Wenn allerding – ganz im Sinne der 80/20-Regel – bereits 80 Prozent der benötigten Ressourcen mit relativ geringem Aufwand bereitgestellt werden können, ist auch hier schon eine Menge gewonnen. Für diejenigen Werkzeuge und Einstellungen, die nicht automatisch bereitgestellt werden können, sollte eine klare Auflistung für alle Teammitglieder (idealerweise in einem Projektwiki) bereitgestellt werden.
Der Schwerpunkt hierbei sollte darauf gelegt werden, lediglich die Abweichungen vom Standard zu erläutern und dem Entwickler zu erklären, welche zusätzlichen Schritte von ihm durchgeführt werden müssen, um an der Codebasis mitarbeiten zu können.
Selbst bei einer ideal vorgefertigten Distribution wird es immer noch Einstellungen und Funktionsschalter geben, die spezifisch für einen einzelnen Entwicklern oder bestimmte Szenarien gehalten werden müssen. Ein Paradebeispiel hierfür ist die Angabe von Datenbankzugangsdaten. So muss in der Regel eine Anwendung auf verschiedenen Stufen und verschiedenen Systemen mit unterschiedlichen Inhalten und Konfigurationen verfügbar sein (z. B. Entwicklung, Test, Abnahme und Produktion). Alle diese unerschiedlichen Systeme verwenden typischerweise ihre eigene Datenbasis und benötigen daher eine eigene Datenbank. Die Zugangsdaten hierzu müssen auf jedem System unterschiedlich gehalten werden können.
Auch jeder einzelne Entwickler stellt in dieser Betrachtungsweise ein eigenes System dar, das (mindestens) eine eigene Konfiguration benötigt. Unterschiedliche Anforderungen können sogar bedingen, dass der einzelne Entwickler eine Vielzahl an wechselbaren Einstellungen vorrätig hält. Bei der Entwicklung einer Anwendung mit Schnittstellen an externe Systeme kann es für einen Entwickler notwendig sein, während seiner Entwicklung sowohl gegen das Test-, als auch das Produktivsystem einer Schnittstelle Tests auszuführen.
Die Anwendung muss daher von vornherein nicht nur in der Lage sein, unterschiedliche Systemkonfigurationen zu unterstützen, sondern es explizit erlauben (wenn nicht sogar fördern), die für ein System abweichenden Konfigurationsparameter anzupassen. Eine Möglichkeit hierzu ist die Verwendung eines dedizierten Konfigurationsprojekts. Aufgabe dieses Projekts ist es, auf der einen Seite alle verfügbaren Konfigurationen an zentraler Stelle zu sammeln und bereitzustellen, aber auch den Wechsel der Konfigurationen selbst schnell und einfach zu ermöglichen.
Angenommen eine Anwendung benötigt zur Konfiguration eine XML-Datei mit dem Namen config.xml, so könnte die Verzeichnisstruktur des Konfigurationsprojekts in etwa wie in Listing 1 aussehen.
Listing 1: Verzeichnisstruktur des Konfigurationsprojekts
project
+ config
+ developers
+ crobert
- config.xml
+ adent
- config.xml
+ systems
+ prod. test.com
- config.xml
+ test1.test.com
- config.xml
+ test2.test.com
- config.xml
...
Aufgabe der Anwendung ist es nun, anhand der aktuellen Umgebung herauszufinden, welche der vorhandenen Einstellungen für die lokal zu startende Anwendung verwendet werden soll. Idealerweise sollte diese Auswahl für den Entwickler möglichst transparent ablaufen und keine zusätzlichen Einstellungen beim Start erfordern, da dieses Setzen wiederum anfällig für Fehler ist.
Eine gute Möglichkeit in Projekten, die Maven als Build-Werkzeug verwenden, ist die Option eines dynamisch definierten Classpaths, der automatisch auf den aktuell angemeldeten Benutzer reagiert (Listing 2).
Listing 2: Maven-Konfiguration
<project>
...
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
</resource>
<resource>
<directory>src/developers/${user.name}/resources/</directory>
</resource>
</resources>
</build>
...
Zusätzlich zum Standard für Ressourcen, die mit einer JAR-Datei eingebunden werden (src/main/resources), wird hier ein zusätzliches für jeden Benutzer verschiedenes Verzeichnis ausgewählt. Der große Vorteil an dieser Lösung ist, dass der Benutzer sich nicht um die Auswahl (s)eines Verzechnisses kümmern muss – das System konfiguriert dieses automatisch – alleine durch die Tatsache, dass er mit seinem Benutzernamen am entsprechenden Rechner angemeldet ist.
Ein weiterer, nicht zu vernachlässigender Vorteil des Vorhandenseins aller verfügbarer Konfigurationen als Dateien innerhalb eines Projekts ist die Tatsache, dass nun Änderungen deutlich besser eingepflegt werden können. Es gibt immer wieder Situationen – gerade bei sich schnell verändernden Projekten –, die es erfordern, bestimmte Konfigurationsparameter und -einstellungen nachträglich komplett zu ändern. So ist es dem Entwickler, der genau diese Änderung durchführt, schnell und einfach möglich, die notwendigen Änderungen auch in die Konfigurationsdateien sowohl aller Umgebungen als auch aller Mitentwickler zu integrieren und sich damit eine Verteilernachricht wie z. B. „Bitte alle Änderungen Y nach dem nächsten Update durchführen“ zu ersparen.
Allgemeines Ziel bezüglich der Konfiguration sollte auch hier sein, eine möglichst einheitliche Konfiguration auf allen Systemen zu erreichen und die tatsächlich abweichenden Einstellungen schnell und einfach änderbar zu machen.
In nahezu jedem Projekt entsteht eine Reihe von Hilfstools und Skripten, die benötigt werden, um bestimmte Entwicklungsartefakte oder Zwischenressourcen zu erzeugen. Ein Beispiel hierfür wäre die Erstellung von Lokaliserungs-Excel-Tabellen. Diese werden von der Entwicklungsabteilung durch ein Skript automatisch aus dem aktuellen Entwicklungsstand erzeugt und an den Kunden gesendet. Dieser füllt die entsprechenden Spalten mit lokalisierten Werten für alle angebotenen Sprachen, leitet die ausgefüllte Excel-Datei zurück an die Entwicklungsabteilung, die mit einem weiteren Skript die lokalisierten Werte in die Anwendung übernimmt. Oftmals sind solche Skripte Ad-hoc-Projekte, die Inselwissen bei einzelnen Entwicklern konzentrieren und diese zu Flaschenhälsen bei Änderungen machen.
Um dieses Szenario zu verbessern, sollte zusätzlich zu den restlichen Modulen der Codebasis ein weiteres Modul, das explizit nur für solche Entwicklungsartefakte zuständig ist, erstellt werden. In diesem Projekt werden dann alle Logiken, Skripte und sonstigen Ressourcen gesammelt, die zwar für die Anwendung bzw. das Entwicklungsprojekt notwendig sind, jedoch bei Auslieferung nicht mit in die eigentliche Anwendung integriert werden. In einer Maven-Struktur könnte dies im Parent-Projekt wie in Listing 3 definiert werden.
Listing 3: Einbindung des Entwicklungsprojekts
<project>
<groupId>com.sapient.example</groupId>
<artifactId>example-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>example-backend</module>
<module>example-frontend</module>
<module>example-persistence</module>
</modules>
<profiles>
<profile>
<id>development</id>
<modules>
<module>example-development</module>
</modules>
</profile>
</profiles>
...
Das Entwicklungsprojekt gehört somit, wie die eigentlichen Projektmodule auch, zur Codebasis, wird jedoch nur aktiviert, wenn explizit das Profil development aktiv ist. Bei einem regulären Deployment ist dieses Profil nicht aktiv, das heißt, das Entwicklungsprojekt wird auch nicht mit in ein Release übernommen.
Durch die Konzentration aller dieser Bestandteile in einem Projekt unterliegen sie den gleichen Richtlinien wie auch die restliche Anwendung, was Versionierung, Architektur oder Konfiguration angeht. Ziel eines solchen Entwicklungsprojekts ist es daher, die Behandlung von nebenbei entstehenden Entwicklungsartefakten als normalen Teil bzw. Ergebnis des Entwicklungsprozesses zu sehen und entsprechend in allen Entwicklungsstufen verfügbar zu haben.
Moderne IDEs sind weit mehr als erweiterte Texteditoren mit Syntax-Highlighting. Sie bieten eine große Anzahl an Integrationen mit diversen Systemen und in diverse Prozesse. Durch Plug-ins (die von allen großen IDEs angeboten werden) lassen sich auch ausgefallenere Systeme oder Prozesse, die nicht bereits von Haus aus unterstützt werden, gut einbinden. Alle Entwickler im Team sollten daher eine gute Vorstellung davon haben, welche Möglichkeiten die eingesetzte IDE bietet und wie diese im aktuellen Projekt eingesetzt werden.
Zusätzlich bietet z. B. Eclipse die Möglichkeit, Konfiguration und allgemeine Einstellungen bzgl. der Konfiguration des lokalen Workspace zu vereinheitlichen. Durch das Plug-in Eclipse Workspace Mechanic [2] lassen sich für Eclipse beispielsweise nahezu alle Einstellungen des lokalen Workspace von einem gemeinsamen Entwicklungsserver beziehen und aktualisieren. Es erlaubt daher dem gesamten Team, einen globalen Satz von Einstellungen und Konfigurationsparametern zu nutzen. Einerseits führt dies zu einem konsistenten Entwicklungsprozess, der unter anderem Fehlerquellen wie unterschiedlich eingestellte Dateikodierungen unterbindet, andererseits zu einer enormen Zeitersparnis bei der Änderung von globalen Einstellungen wie z. B. Kodierungsrichtlinien. Richtig konfiguriert, werden so neue Einstellungen vollkommen unbemerkt und ohne aktives Eingreifen direkt aktualisiert und verwendet.
Es sind oftmals die kleinen Dinge bzw. deren Summe, die den Entwicklungsprozess effizienter gestalten können. Wichtig ist auch hier der erste Schritt: sich über Optimierungspotenzial bewusst zu werden und entsprechend zu handeln. Ganz egal, ob bereits vor dem eigentlichen Projektstart oder im laufenden Projekt – in diese Metaorganisation investierte Ressourcen zahlen sich oftmals bereits nach kürzester Zeit aus und sorgen nicht zuletzt für ein angenehmeres und entspannteres Entwickeln. Schließlich gibt es auf technischer und fachlicher Ebene immer noch genügend Probleme, die gelöst werden wollen – das Drumherum sollte dabei möglichst nicht im Wege stehen.
Christian Robert ist Senior Developer für Mobile-Lösungen bei SapientNitro in Köln. Seit über zehn Jahren beschäftigt er sich mit der Konzeption und Entwicklung von Individualsoftware im Java-Umfeld. Seine aktuellen Schwerpunkte liegen in der Entwicklung von pragmatischen und dennoch (oder gerade deswegen) effizienten Softwarelösungen im mobilen Umfeld. Außerdem interessiert er sich intensiv für die Ideen der Software-Craftsmanship-Bewegung.
[1] http://www.apachefriends.org/de/xampp.html
[2] https://code.google.com/a/eclipselabs.org/p/workspacemechanic/