Alle Beiträge, jStage Platform, Technologien & Entwicklung

Technologieupgrade am Beispiel von Java 8

Es gibt viele gute Gründe, eine Java Anwendung auf die aktuelle Sprachversion zu aktualisieren. In größeren Softwareprojekten kann allerdings jedes Technologieupgrade an trivialen Dingen scheitern – Der Spruch “Never touch a running System” ist nicht umsonst so populär. Vor gut einem Jahr haben wir den jStage eShop, unsere E-Commerce-Lösung, auf Java 8 umgestellt. Dieser Erfahrungsbericht zeigt auf, worauf man achten sollte, damit der Wechsel  erfolgreich über die Bühne geht.

Java: Sprache und Laufzeitumgebung

Mit Java wird sowohl die Programmiersprache Java als auch die Laufzeitumgebung bezeichnet.

Mit einer bestimmten Sprachversion entwickelte Anwendungen können nur auf einer Laufzeitumgebung mindestens der gleichen Version betrieben werden. Umgekehrt laufen dagegen ältere Anwendungen fast immer problemlos auf einer neueren Laufzeitumgebung. Dazu gleich mehr.

Für uns ergibt sich dadurch die Reihenfolge der Umstellung: Zuerst wird die Laufzeitumgebung aktualisiert, dann die Entwicklung der Anwendung auf Java 8 umgestellt.

Umstellung der Laufzeitumgebung

Probleme macht in der Regel nicht die eigene Anwendung, sondern die verwendeten Softwarebibliotheken. Kritische Kandidaten sind unter anderem solche, die den Bytecode manipulieren, wie z. B. Javassist (Java Programming Assistant).

Hier zahlt es sich aus, wenn die Aktualisierung der Bibliotheken im Entwicklungsprozess verankert ist. Wer dagegen noch nicht einmal ein Werkzeug für das Dependency Management wie beispielsweise Maven oder Ivy einsetzt, hat an dieser Stelle viel Arbeit vor sich. Am Lesen der Dokumentation kommt man allerdings so oder so nicht vorbei.
Wesentlich reduzieren lässt sich dieser Aufwand durch Meta-Frameworks wie Spring Boot. Da dieses alle Abhängigkeiten für die Anwendung kapselt, genügt es die passende Version auszuwählen und deren Angaben zur Kompatibilität zu studieren.

Ähnliches gilt für den Application Server. Hier haben die Open Source Vertreter eindeutig die Nase vorn: Der von jStage verwendete Apache Tomcat Server war schon fit für Java 8, bevor die Laufzeitumgebung überhaupt offiziell freigegeben wurde. Kunden proprietärer Systeme müssen hier in der Regel einige Zeit auf die Freigabe des Herstellers warten.

Der richtige Zeitpunkt

Ein Ausfall des Webshops bedeutet immer Verluste für den Betreiber. Unmittelbar durch entgangene Verkäufe und längerfristig durch verärgerte Kunden. Jede neue Technologie sollte also erst zum Einsatz kommen, wenn diese entsprechend ausgereift und damit stabil ist.

Der jStage eShop läuft verteilt über mehrere Server, also als Cluster. Deswegen konnten wir einzelne Server gefahrlos mit der Java 8 Laufzeitumgebung laufen lassen, um die Stabilität zu testen ohne dadurch den Betrieb des Webshops zu gefährden.

Im Gegensatz zu früheren Umstellungen lief die Java 8 Laufzeitumgebung schon früh (Update 11) genauso stabil wie die damals aktuelle Java 7 Laufzeitumgebung (Update 65). Offensichtlich war die Entscheidung (damals noch durch Sun Microsystems), die Java Laufzeitumgebung ab Version 7 als Open Source Projekt weiter zu entwickeln, auch in dieser Hinsicht richtig. Die Version 6 z. B. benötigte eine deutlich längere Reifezeit bis sie produktiv eingesetzt werden konnte.

Nach einem kurzen Testzeitraum von wenigen Wochen konnte mit gutem Gefühl der gesamte produktive Shopcluster auf die neue Java Laufzeitumgebung umgestellt werden.

Das machte sich dann auch unmittelbar bezahlt: Die Neuerungen in der Speicherverwaltung erlauben eine wesentlich bessere Anpassung an die Anforderungen einer Webanwendung unter hoher Last. Auch lässt sich der verfügbare Speicher effizienter nutzen, die durch die automatische Speicherbereinigung verursachten Latenzen ließen sich sogar komplett eliminieren. Die Antwortzeiten verbesserten sich spür- und messbar (siehe dazu: Performance Monitoring mit stagemonitor).

Entwicklungsprozess: Technik

Schwieriger gestaltet sich die Umstellung des Entwicklungsprozess. Damit die Sprache Java 8 verwendet werden kann, müssen die eingesetzten Werkzeuge damit umgehen können:

  • Die Integrierte Entwicklungsumgebung (z. B. Eclipse oder IDEA)
  • Der Buildprozess (z. B. Gradle)
  • Der Continuous Integration Server (z. B. Jenkins)
  • Werkzeuge zur Codeanalyse (z. B. SonarCube)
  • Die Laufzeitumgebung muss für alle eingesetzten Betriebssysteme verfügbar sein

Alle diese Werkzeuge müssen die neue Sprache unterstützen. Zudem sind nun nicht mehr nur ein paar gleichartige Server betroffen, sondern viele unterschiedliche Systeme. Die Unterstützung neuer Sprachfeatures in einer IDE ist eine komplexe Angelegenheit. Auch hier zahlt es sich aus, wenn man grundsätzlich versucht aktuell zu bleiben und nicht erst dann ein Upgrade macht, wenn man dazu gezwungen ist.

Entwicklungsprozess: Menschen

Man braucht sowohl Enthusiasmus und Forschergeist um neue Möglichkeiten ausloten. Allerdings lässt man sich allzu leicht dazu verleiten, diese um ihrer selbst willen einzusetzen oder in der Begeisterung zu weit zu gehen. Der Gegenpol dazu ist Erfahrung und Bedächtigkeit, um mögliche Fallstricke zu erkennen und auf dem Boden zu bleiben.

Die Lösung liegt in einem heterogenen Team und einem Entwicklungsprozess, der die Potentiale der unterschiedlichen Charaktere synergistisch zur Entfaltung bringt. Wir setzen dazu konsequent auf Code Reviews (siehe auch: Doppelt hält besser – Code Reviews mit Gerrit):
Jede Änderung an der Anwendung wird von mindestens einem zweiten Entwickler begutachtet bevor sie in das Software Repository übernommen wird.
Neben der Qualitätssicherung dient das vor Allem auch dem Wissenstransfer. Bei jStage haben engagierte Entwickler sofort, nachdem der Buildprozess auf Java 8 umgestellt wurde, Patch Sets eingereicht, die zahlreiche Codestellen durch solche in Java 8 Syntax ersetzen. Mit jeder neuen Änderung wird nach und nach die Anwendung migriert.

Wozu das Ganze?

Wie bei jedem Technologieupgrade stellt sich die Frage, wozu das gut sein soll. Und, vor Allem im kommerziellen Umfeld: Rechnet sich der Aufwand unter dem Strich?

Für den Einsatz von Java 8 ist die Frage einfach zu beantworten: Sinnvoll genutzt kann man mit den neuen Sprachfeatures viele Aufgaben einfacher verständlich lösen, und, vor Allem so umsetzen wie es die fachliche Anforderung nahelegt.

Sehen wir uns das an einem einfachen Beispiel an:

Die Anforderung besteht darin, ein Suchergebnis auf für den Benutzer erlaubte Artikel einzuschränken und die Ergebnisliste in der Größe zu beschränken:

Bei diesem Ansatz werden die Artikel des Suchergebnisses in einer Schleife durchlaufen. Dabei wird für jeden Artikel geprüft, ob dieser dem Benutzer erlaubt ist, und dann der Ergebnisliste hinzugefügt. Anschließend wird geprüft, ob die maximal gewünschte Größe der Ergebnisliste erreicht ist und gegebenenfalls die Schleife abgebrochen.

Dieser Ansatz macht technisch gesehen das gleiche, allerdings ist die Umsetzung ganz anders:
Das Suchergebnis wird in einen Stream umgewandelt, dieser wird auf erlaubte Artikel gefiltert und auf die maximale Ergebnisgröße limitiert. Dann wird der Stream zu einer Liste zusammengefasst. Hat man das “Stream” Konzept einmal verinnerlicht, dann ist es sehr viel einfacher nachzuvollziehen was hier passiert.

Das ist ein sehr einfaches Beispiel, trotzdem ist der Java 7 Quelltext schon dreifach geschachtelt. Die Erfahrung zeigt dass die Fehlerquote überproportional zur Schachtelungstiefe steigt. Auch das an sich harmlos erscheinende Konstrukt, mit break den Schleifendurchlauf abzubrechen, ist eine potentielle Fehlerquelle. Spätestens wenn ein paar Klammern tiefer das zweite break kommt, können wahrscheinlich nur noch Simultanschachspieler erfassen was der Code macht.
Oder, und da liegt der Hase im Pfeffer, machen könnte. Z. B. wenn dann doch mal irgendwelche Werte ganz anders sind als man sich so vorstellt.
So liefert die Java 8 Lösung für maxArticleListSize = 0 das was man erwarten würde, nämlich eine leere Ergebnisliste. Die Java 7 Lösung liefert in diesem Fall alle erlaubten Artikel. Einfach zu beheben, aber das Konstrukt wird damit noch komplizierter. Womit wiederum die Wahrscheinlichkeit steigt weitere Fehler einzubauen…

Ein anderes Beispiel ist die generische Klasse “Optional”. Wird diese für Rückgabewerte verwendet ist dem Aufrufer einer Methode unmittelbar klar, dass er nicht mit einem Wert rechnen kann – der Quelltext entspricht der fachlichen Realität. Zum Beispiel bei einer abweichenden Lieferadresse.

Und damit kommen wir endlich zum Punkt: Je verständlicher und nachvollziehbarer ein Stück Software ist, desto einfacher lässt es sich anpassen. Und, noch wichtiger, desto geringer ist die Wahrscheinlichkeit, dass eine Anpassung unerwünschten Seiteneffekte zur Folge hat.

Fazit

Ein erfolgreicher Webshop muss laufend mit den steigenden Erwartungen der Benutzer weiter entwickelt werden um gegenüber der Konkurrenz die Nase vorn zu haben. Deshalb können wir als Entwickler des jStage eShops die Frage nach dem Nutzen eindeutig positiv beantworten. Die Anpassbarkeit des Systems ist am Ende des Tages eine ganz entscheidende Eigenschaft für den ambitionierten Betreiber eines Webshops.
Wie waren Ihre Erfahrungen?

Sollten Sie Fragen haben oder Ihre Erfahrungen austauschen wollen, dann schreiben Sie bitte einen Kommentar oder auch gerne eine E-Mail.