Überwinde 20 Jahre mit Migration Engineering

Merlin Bögershausen

Seit drei Jahrzehnten heißt es im Java Umfeld Write once, run Everywhere. Am Kern dieses Slogans hat sich bis heute nichts geändert. 30 Jahre alte Java-Programme sind noch heute lauffähig. Die Art und Weise Programme zu schreiben, hat sich in den letzten 30 Jahren extrem verändert. Die Sprache hat sich weiterentwickelt, die verwendeten Frameworks haben sich gewandelt und der Stil zu programmieren, hat sich grundlegend verändert. All diese Veränderungen führen dazu, dass ein Update von 20 Jahre altem Source Code auf modernes Java äußerst komplex geworden ist – so komplex, dass viele Unternehmen trotz offensichtlicher Nachteile jahrelang zögern und lieber einen massiven Berg technischer Schulden in Kauf nehmen.

Diese technischen Schulden verursachen nicht nur erhebliche Wartungskosten, sondern stellen auch ein Risiko für den ökonomischen Erfolg eines Unternehmens dar. Die größte Gefahr ist die Software nicht zu aktualisieren und auf das Schließen von Sicherheitslücken und die Performancegewinne durch neue Versionen zu verzichten.

In diesem Artikel möchte ich Sie ermutigen und Ihnen die Angst vor Migration nehmen. Ich zeige Ihnen das Werkzeug Migration Engineering, mit dem es möglich ist, 20 Jahre gealterten Sourcecode in einen technisch neuen Zustand zu bringen. Wir werden uns dafür anschauen, was Migration Engineering ist und wie wir die Werkzeuge des Migration-Engineerings nutzen können, um den Source Code zu analysieren und anschließend zu migrieren. Ihre Projekte werden besondere Anforderungen haben und sie werden sehen, wie die vorhandenen Rezepte um die passenden Änderungen erweitert werden. Zum Abschluss werfen wir einen Blick darauf, wie Sie der Migrationswelle stets einen Schritt voraus bleiben – mit kontinuierlich aktuellem Sourcecode und modernen Frameworks. So fördern Sie nicht nur die Freude Ihrer Entwickler:innen an der Arbeit, sondern steigern auch nachhaltig die Produktivität Ihrer Softwareentwicklung.

Was ist Migration Engineering und brauche ich das?

Migration Engineering beschäftigt sich mit einer zentralen Frage: Wie gelangt man von einer Softwareversion zur nächsten?
Vergleicht man diesen Ansatz mit klassischen Ingenieursdisziplinen wie Elektrotechnik, Maschinenbau oder – meinem persönlichen Favoriten – der Luft- und Raumfahrt, wird schnell klar: Migration Engineering ist im Kern nichts anderes als klassisches Engineering – nämlich die gezielte Weiterentwicklung eines Produkts. In der Praxis bedeutet das häufig, bestimmte Komponenten auszutauschen, um das System in einen besseren Zustand zu überführen.

Doch zieht man den Vergleich mit traditionellen Ingenieursdisziplinen weiter, zeigt sich schnell: Der Vergleich hinkt ein wenig. Denn der große Unterschied zwischen einem Luftfahrzeug und unserem Source Code ist: Source Code ist veränderbar. Ein Luftfahrzeug ist nur in geringem Maße veränderbar. Wenn wir eine Migration von einem Testframework in unserem Testcode betrachten, dann ändert sich in der Differenzbetrachtung die komplette Source Code-Datei (siehe Screenshot), welche diesen Test definiert.

Diff einer Migration von JUnit 4 zu JUnit Jupiter

Eine Softwareingenieur:in behauptet, dies ist weiterhin derselbe Test. Das ist ungefähr so als ob man das Triebwerk eines Jumbos von der Mitte des Flügels an die Enden des Flügels verlegt und behauptet, es ist derselbe Jumbo. Auf diesem Level von Wahnsinn und Komplexität befinden sich die Softwareentwicklungen tagtäglich. Wir verändern integrale, fundamentale Bestandteile und behaupten, es ist die exakt gleiche Software wie vorher. Diese Komplexität zu beherrschen, damit beschäftigt sich Migration Engineering. Migration Engineering funktioniert nur mit einer hohen Testabdeckung, denn ansonsten kann nicht sichergestellt werden, dass die Software sich nach der Migration auch wirklich noch so verhält wie vorher.

Die Arbeit eines Migration-Engineers möchte ich kurz anhand meiner ersten Wochen bei Moderne Inc. beschreiben. Ein Tag begann mit einer Nachricht in „Spring Boot 3.4 ist verfügbar, lasst uns auf aktualisieren!“. Die Migrationen von Spring Boot Versionen sind in der Regel durch den veröffentlichten Migration Guide einfach nachvollziehbar. Doch der Migration Guide besteht aus groben Schritten. Verwende folgende neue Klasse und Abhängigkeit. Dieser grobe Schritt muss von einem Migration-Engineer erst einmal weiter zerlegt werden. Bis die atomaren Codeänderungen bekannt sind, welche ein Softwareentwickler:inn manuell durchführt.

  • Füge eine Dependency in die Maven Pom hinzu
  • Füge einen neuen Import hinzu
  • Verwende die neue Klasse anstelle der alten
  • Tausche die Reihenfolge der Aufrufparameter 
  • Lösche den alten Import 
  • Lösche die alte Dependency

Nachdem diese einzelnen Schritte vorhanden sind, werden diese Schritte aggregiert und in die korrekte Reihenfolge gebracht, bis sie die Zielmigration ergeben. Nun kann sie auf alle vorhandenen Projekte angewendet werden. Das hieß bei uns ein Pull Request für jeden Service, den wir betreiben. Insgesamt bedeutete das einen erheblichen Aufwand – bedingt durch die Vielzahl mittelgroßer Pull Requests, die parallel bearbeitet werden mussten. Und natürlich gab es Rückfragen und Änderungswünsche. Einzelne Services mussten erneut migriert werden. Damit musste eine Migration der Migration stattfinden. Es mussten neue Rezepte erstellt und auf die bereits vorhandenen Änderungen angewandt werden.

Die Komponenten sind inzwischen aktualisiert, und ich möchte eine grobe Einschätzung dazu geben, welcher Aufwand das Ganze war. Ich habe Anfang des Jahres 2025 beim Moderne Inc. begonnen, habe grob eine Woche die Migration implementiert. Ungefähr 10 Minuten lang die Migration angewendet und dann mit verschiedenen Personen grob eine Woche daran gearbeitet, die Merge Requests in den Source Code zu übernehmen. Wäre diese Migration von verschiedenen Teams per Hand gemacht worden, wären divergente Anwendungen der neuen Komponenten und eine längere Umsetzungszeit die Konsequenz gewesen.

Offen bleibt die Frage: Braucht jetzt jedes Projekt einen eigenen Migration Engineer, der sich darüber Gedanken macht, wie die Softwarekomponenten von einer Version zur nächsten überführt werden können? – Ich denke nicht, halte es aber für wichtig, dass jede Softwareentwickler:in sich mit der Thematik beschäftigt und Migrationspfade für ihre Komponenten aufzeigt. Gehört ihr Team zu einem Plattformteam und beeinflussen Entscheidungen andere Teams oder wird eine Bibliothek zur weiteren Verwendung bereitgestellt? Dann ist es auf jeden Fall die Aufgabe des Teams, nicht nur zu zeigen, was die Änderungen sind, sondern auch, wie diese Änderungen im Einzelnen durchgeführt werden können. Nur so können Ihre Teams dafür sorgen, dass die verwendeten Stile und Versionen kohärent sind. OpenRewrite stellt hierfür die Technologie dar, um Änderungen zu formulieren und skalierbar anzuwenden.

OpenRewrite zum Migrieren von Source Code

Die Firma Moderne Inc. kümmert sich um die Weiterentwicklung und die Community von OpenRewrite. In dieser Zusammenarbeit sind viele grundlegende Bausteine entstanden, welche zu wertstiftenden Migrationen kombiniert werden. Darunter Migrationen von Majorversionen für Spring Boot, Quarkus, Hibernate, Maven und Gradle, aber auch kleine Helfer wie Anwenden von Best Practices oder Beheben der gängigsten Issues von statischer Codeanalyse. 

Alle anwendbaren Rezepte sind in dem Recipe Catalog in der OpenRewrite Dokumentation zu finden. Um das passende Rezept für den eigenen Anwendungsfall zu finden, kann die Suche oben links (Kastenkürzel: Strg/CMD+K) verwendet werden. Diese Suche indiziert nicht nur die Namen, sondern auch Teile der Dokumentation einer Migration, um besser zugeschnittene Ergebnisse zu ermöglichen. Noch bessere Ergebnisse liefert die Suche in der Moderne SaaS unter https://app.moderne.io, diese liefert auch passende Ergebnisse bei ungenauen Anfragen. Alternativ sind die Migrationen auch entsprechend ihren Themengebieten getagt und anhand dieser Tags gruppiert. Diese Gruppen ermöglichen eine explorative Suche, die besonders bei den ersten Anwendungen hilfreich sind.

Ein Team könnte so auf die Migration Migrate to Java 21 aufmerksam werden. Die zugehörige Dokumentationsseite ist automatisch erzeugt und für alle Migrationen gleich aufgebaut, siehe auch Abbildung oben. Unter der Überschrift ist der Fully Qualified Name angegeben, dieser ist eindeutig und wird für die weitere Verwendung benötigt. Weiterhin sind die Tags, ein Link zu den Ressourcen sowie die Lizenzinformationen gegeben. Bei den Lizenzen gibt es hauptsächlich drei verschiedene Arten:

  • Apache License 2.0 das Core-Framework ist Apache-lizenziert, damit Framework-Autoren Migrationen für ihre Kunden anbieten können. Einige tun dies, wie Micronaut und Quarkus. In den Fällen, in denen die Framework-Autoren solche Migrationen nicht bereitstellen, gibt es einen Marktplatz für Migrationen von Drittanbietern, darunter auch die von Moderne.
  • Moderne Source Available License sind Rezepte, die frei verfügbar sind, aber nicht als Teil eines anderen Service bereitgestellt werden dürfen. In der Vergangenheit haben große Unternehmen OpenRewrite ausgenutzt, um ihre kommerziellen AI-Migration-Lösungen zuverlässiger zu gestalten. Um Produkte wie Amazon Q, GitHub Copilot und IBM Watson zu einer Kooperation zu bewegen, veröffentlicht Moderne seine Rezepte nun unter der MSAL. So ist es Projekten weiterhin möglich, die Rezepte in vollem Umfang auf sich selbst anzuwenden, jedoch verboten, sie kommerziell weiter zu verwerten.
  • Moderne Proprietary sind Rezepte, die nur mit einer Lizenz von Moderne verwendet werden dürfen. Es handelt sich hier um besonders wertschöpfende Rezepte, die mit großem Investment durch Moderne in Verbindung stehen. Auf Open-Source Projekte sind auch diese Rezepte anwendbar.

Die unterschiedlichen Lizenzen dienen dazu, Dritten zu untersagen, die Rezepte in ihren kommerziellen Services zu verwenden und weiter zu vermarkten, ohne dem Projekt etwas zurückzugeben. Eine genaue und aktuelle Aufstellung findet sich in der Moderne Dokumentation. Zum Zeitpunkt der Veröffentlichung suchen Moderne Inc. und die OpenRewrite Community noch nach einer endgültigen Lösung. Ziel ist es weiterhin, Open-Source-Projekten Zugriff auf die Modernisierungsfähigkeiten von OpenRewrite und den modernen Produkten zu ermöglichen.

Nach den rechtlichen Anmerkungen führt die Dokumentation die durchzuführenden Migrationsschritte auf. Jeder Schritt ist ebenfalls eine Migration mit einer gleich aufgebauten Dokumentationsseite. In einem späteren Absatz wird behandelt, wie solche Migrationen für die eigenen Bedürfnisse erstellt und über YAML-Dateien konfiguriert werden können. In der Dokumentation folgt eine Aufzählung der Möglichkeiten, wie diese Migration angewendet werden kann. Migrationen sammeln neben Codeanpassungen auch Daten zur späteren Analyse, die DataTables. Gängige Informationen sind zu Laufzeiten, geänderte Dateien oder Analyseinformationen zur Verwendung von Konstrukten. Nun soll die Migration angewandt werden. Nach der Entscheidung für eine Migration kann der oben erwähnte Absatz Usage verwendet werden, um die Anwendung eines Rezeptes zu studieren. Grundsätzlich gibt es drei Möglichkeiten, eine Migration zur Ausführung zu bringen. 

  • Maven, über die CLI oder das Rewrite Maven Plugin
  • Gradle, Rewrite Plugin oder ein Init Script
  • Moderne CLI, das standalone Power-User-Tool
  • Moderne SaaS, auf OpenRewrite Webanwendung

Über die Maven CLI kann eine Liste von OpenRewrite Rezepten angegeben werden, welche mit dem Rewrite Maven Plugin ausgeführt werden. Dieser Modus eignet sich in erster Linie für die Verwendung von einmal Migrationen wie Framework Updates, dasselbe gilt für das Gradle Init Script. Es empfiehlt sich bei dieser Art der Anwendung die Beispiele aus der Dokumentation zu kopieren. Für unsere JUnit Migrate to Java 21 Migration ist der Maven CLI Aufruf im Listing unten gegeben. Es wird das Rewrite Maven Plugin Goal run aufgerufen und mit rewrite.activeRecipes die Migration UpgradeToJava21 angegeben. Da dieses nicht in OpenRewrite integriert ist, müssen die Artefaktkoordinaten für rewrite-migrate-java unter rewrite.recipeArtifactCoordinates angegeben werden.

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run 
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:RELEASE 
  -Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava21

Im Fall der Migrate to Java 21 Migration ergibt es Sinn, das Rezept gelegentlich erneut auszuführen. Denn durch Unachtsamkeit könnte eine Entwickler:inn alte Java API verwenden, welche erneut migriert werden müssen. Hierzu bietet es sich an, das Rezept als optionales Plugin im Build einzusetzen. Dazu wird wie in Listing unten das rewrite-mave-plugin eingebunden, das Rezept UpgradeToJava21 aktiviert und das Migrationsartefakt als Dependency hinzugefügt. Durch ein einfaches mvn rewrite:run kommt das Plugin zur Ausführung, führt die konfigurierten Migrationen aus und wendet die notwendigen Änderungen auf den Code an. Bei jedem Durchlauf der Rewrite Plugins meldet das Plugin, welche Migration in welcher Datei eine Änderung vorgenommen hat. Zusätzlich erstellt es eine grobe Schätzung, wie viel Aufwand eine manuelle Bearbeitung bedeutet hätte. Von hier aus muss nur noch committed, gepusht und gereviewt werden.

<project>
  <build>
    <plugins>
      <plugin>
        <groupId>org.openrewrite.maven</groupId>
        <artifactId>rewrite-maven-plugin</artifactId>
        <version>6.3.1</version>
        <configuration>
          <exportDatatables>true</exportDatatables>
          <activeRecipes>
            <recipe>org.openrewrite.java.migrate.UpgradeToJava21</recipe>
          </activeRecipes>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.openrewrite.recipe</groupId>
            <artifactId>rewrite-migrate-java</artifactId>
            <version>3.4.0</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>

Als letzte Option bleibt noch die Moderne CLI als Powertool basierend auf OpenRewrite. Die Moderne CLI ist kostenlos einsetzbar. Für Projekte in öffentlichen Repositorys, für Closed-Source Projekte ist eine Lizenzierung notwendig. Im Gegensatz zu den Rewrite Plugins kann die Moderne CLI auf mehrere Projekte gleichzeitig und parallel angewendet werden. Weiterhin kann die Moderne CLI die erzeugten Metadaten persistieren und weiterverwenden. Dadurch werden nicht bei jedem Durchlauf die kompletten Metadaten erzeugt und die Laufzeit verbessert sich drastisch. Die Moderne CLI wird als native Applikation installiert, für alle gängigen Betriebssysteme existieren Executables und über Maven Central wird zusätzlich eine reine JAR Variante bereitgestellt. Nach der Installation ist die CLI im Terminal verfügbar.

Vor der Ausführung einer Migration müssen die Metadaten erzeugt werden. Hierzu wird das Kommando mod build . genutzt. Dieses sucht im aktuellen Verzeichnis nach Projekten und erzeugt die Metadaten. Zusätzlich muss mit mod sync die Liste der verfügbaren Migrationen heruntergeladen werden. Um ein Rezept auszuführen, wird mit dem mod run. Kommando und dem parameter —recipe  die auszuführende Migration gestartet. Nach der Migration könnten die Änderungen angewandt oder studiert werden.

Grundsätzlich wird der Prozess verständlich durch die CLI geleitet. Hierzu meldet sie nach jedem Durchlauf, welche nächsten Schritte sinnvoll wären. Eine Schritt-für-Schritt-Anleitung findet sich in der Dokumentation. Jede Ausführung eines Rezepts liefert neben Codeanpassungen auch Datatables. Die Datatables sind die Basis für alle Analysemöglichkeiten mit der OpenRewrite Technologie. Die Maven und Gradle Integration produziert Datatables für ein Projekt. Im Gegensatz liefern die CLI und die Moderne SaaS Daten für mehrere Projekte gleichzeitig.

In der Moderne SaaS sind einige Standardvisualisierungen vorhanden, die es Migration Engineers ermöglichen, die Verwendung von unterschiedlichen Framework-Versionen oder der Verteilung von Änderungen zu analysieren. In dem Screenshot unten ist das DevCenter zu sehen, in welchem für eine beispielhafte Organisation Informationen über die enthaltenen Repositorys enthalten sind. Zusätzlich sind in überschaubarer Art die Erreichung von strategischen Unternehmenszielen wie Spring Boot 3 oder Java 21 Migrationen gezeigt.

Screenshot des DevCenter für eine OpenSource Organization in der Moderne SaaS

Nicht selten haben Projekte in Unternehmen besondere Anforderungen an die Verwendung von bestimmten Patterns oder internen Bibliotheken. In diesem Fall müssen die vorhandenen OpenRewrite Rezepte erweitert werden.

Angepasste OpenRewrite Rezepte

In der OpenRewrite Technologie werden Migrationen als Rezepte definiert. Ein Rezept instruiert das OpenRewrite Werkzeug, wie der vorhandene Code angepasst wird, um ein Ziel zu erreichen. Rezepte können auf 3 verschiedenen Wegen mit steigender Komplexität und Funktionsumfang definiert werden:

  • Deklarative YML Rezepte
  • Refaster Templates in Java-Rezepten
  • Imperative Java Rezepte

In der Tabelle unten sind die einzelnen Eigenschaften der Rezepte aufgeführt.  Den einfachsten Einstieg bieten die deklarativen Rezepte, diese folgen einem YAML-Schema und definieren neben den notwendigen Eigenschaften die Liste der auszuführenden Rezepte mit ihren Konfigurationen. Diese Rezepte werden verwendet, um andere Rezepte zu aggregieren. Refaster Rezepte verwenden Refaster Templates, wie sie das Google Error Prone Projekt definiert. Diese Templates werden für typsicheres Suchen und Ersetzen von Methodenaufrufen verwendet. Werden komplexere Manipulationen benötigt, welche nicht durch die Basisbausteine abgebildet werden können, ist es notwendig, ein imperatives Rezept zu formulieren. Hier ist der komplette Umfang von Java als Programmiersprache verwendbar und viele Utilitys von OpenRewrite verfügbar, um die Migrationslogik umzusetzen.

Deklaratives RezeptRefaster RezeptImperatives Rezept
YAMLRefaster Templates in JavaPure Java
Aggregiert RezepteSuchen & ErsetztenHochflexibel
Konfiguriert RezepteTypsicherVisitor-Pattern
EinfachBegrenzte EinsetzbarkeitViele Utilities
Arten von Rezepten und ihre Eigenschaften

Übersicht über die unterschiedlichen Arten von Rezepten

Es ist empfehlenswert, mit einem deklarativen Rezept zu starten und zu versuchen, einen möglichst großen Umfang der Migration durch die Konfiguration von vorhandenen Rezepten abzubilden. Doch bevor ein Rezept erstellt werden kann, muss ein entsprechendes Projekt aufgesetzt und die Akzeptanztests definiert werden.

Deklarative Rezepte werden verwendet, um vorhandene Rezepte zu konfigurieren und gemeinsam auszuführen. Im ersten Schritt wird im Open Rewrite Rezeptkatalog das benötigte Rezept identifiziert und der vollqualifizierte Name identifiziert. Der vollqualifizierte Name ist direkt unter der Überschrift der Dokumentation angegeben. Sollen Rezepte aus dem eigenen Bestand verwendet werden, so wird auch in diesem Fall der qualifizierende Name verwendet. Um etwa eine Klassenreferenz auszutauschen, bietet sich das Rezept org.openrewrite.java.ChangeType an. Im moderneinc/rewrite-recipe-starter GitHub Projekt wird dieses Rezept in resources/META-INF/rewrite/stringutils.yml, siehe auch Listing unten, konfiguriert, um die Korrekten StringUtils Klasse zu verwenden.

type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.UseApacheStringUtils
displayName: Use Apache `StringUtils`
description: Replace Spring string utilities with Apache string utilities.
recipeList:
  - org.openrewrite.java.ChangeType:
    oldFullyQualifiedTypeName: org.springframework.util.StringUtils
    newFullyQualifiedTypeName: org.apache.commons.lang3.StringUtils

Durch den type wird es als Rezept für OpenRewrite Migrationen ausgewiesen und ist über den qualifizierenden Namen hinter name ansprechbar. Der displayName wird vor allem bei dem automatisiert erstellten Rezeptkatalog verwendet. In der recipeList werden die auszuführenden Rezepte angegeben. Die möglichen Konfigurationen sind auf den entsprechenden Rezeptseiten angegeben. Reichen die vorhandenen Basisrezepte im Rezeptkatalog nicht aus, müssen neue Rezepte geschrieben werden.

Ein mögliches Beispiel wäre das Vereinfachen eines ternären Operators zu einem konstanten Ausdruck. Hier bietet sich die Anwendung von Refaster Templates aus dem Error Prone Projekt an. Mit Error Prone Refaster können typischer musterbasierte Veränderungen durchgeführt werden. OpenRewrite unterstützt die Templates von Refaster und bietet somit eine passende Abstraktion für die Anpassung von Methodenaufrufen. Ein Refaster Template Rezept wird durch die @RecipeDesciptor als ein Rezept markiert, mit einem Namen für die Dokumentation und einer Beschreibung versehen. Die auszutauschende Codepassage wird in der @BeforeTemplate markierten Methode angegeben. Hierbei werden in der Methode jeweils die Expressionen typsicher verwendet. Die mit @AfterTemplate markierte Methode definiert den Zielzustand und verwendet die Typsicherheit, um den neuen Ausdruck zu erstellen. Refaster Templates können nur Änderungen innerhalb eines Codeblocks durchführen.

@RecipeDescriptor(
  name = "Replace `booleanExpression ? true : false` with `booleanExpression`",
  description = "Replace ternary expressions like `booleanExpression ? true :   	false` with `booleanExpression`.")
public static class SimplifyTernaryTrueFalse {
    @BeforeTemplate
    boolean before(boolean expr) {
        return expr ? true : false;
    }
    @AfterTemplate
    boolean after(boolean expr) {
        return expr;
    }
}

Sind Anpassungen darüber hinaus notwendig und diese nicht durch die Kombination aus vorhandenen Rezepten möglich, kann ein imperatives Rezept erstellt werden. Diese imperativen Rezepte erweitern die abstrakte Klasse org.openrewrite.Recipe, siehe Listing unten. 

public class TestRecipe extends org.openrewrite.Recipe {
  @Override
  public String getDisplayName() {
    return "An example Recipe";
  }
  @Override
  public String getDescription() {
    return "This Recipe is an example.";
  }
  @Override
  public TreeVisitor<?, ExecutionContext> getVisitor() {
    return new JavaIsoVisitor<ExecutionContext>() {
      @Override
      public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration m, ExecutionContext ctx) {
        J.MethodDeclaration m2 = super.visitMethodDeclaration(m, ctx);
        return m2.getName().getSimpleName().equals("foo") ?
               m2.withName(m2.getName().withName("bar")) :
               m2;
      }
    };
  }
}

Die Methode getDisplayName liefert den Anzeigenamen und getDescription die Beschreibung für die automatisiert erstellte Dokumentation. Die getVisitor Methode liefert eine Instanz eines TreeVisitors. Ein TreeVisitor traversiert den Lossless Semantic Tree (LST) und manipuliert einzelne Knoten in diesem Baum. Der LST ist die OpenRewrite Repräsentation des kompletten, Sprachen und Technologie überspannenden Source-Codes innerhalb des Projekts. Der LST wird beim Start von Open Rewrite erzeugt und wird nach der Durchführung aller Rezepte verwendet, um die Änderungen wieder in Source Code zu überführen. Für jede unterstützte Sprache gibt es eine eigene LST-Implementierung und angepasste Visitoren.

Um Java-Source-Code zu manipulieren, wird beispielsweise der org.openrewrite.java.JavaIsoVisitor verwendet. Dieser enthält eine Visit-Methode für jede Art von Elementen im LST. Diese liefern das geänderte Element zurück. In diesem Beispiel werden alle Methoden Deklarationen besucht und die J.MethodDeclaration gesucht, welche den Namen „foo“ hat, um sie nach „bar“ umzubenennen. Wenn die aktuell betrachtete Methode beibehalten werden soll, wird sie unterwandert zurückgeliefert. Wird null zurückgeliefert, so wird die Methodendefinition gelöscht. Weitere vollständige Implementierung von Rezepten findet sich im moderneinc/rewrite-recipe-starter. Unteranderem in der Klasse NoGuavaListsNewArrayList, dieses Rezept migriert die Verwendung von Guave zu JDK enthaltenen Methoden zum Umgang mit ArrayLists. Ein ausführlicher Schritt-für-Schritt-Workshop für das Homeoffice ist auf Moderne.io vorhanden und behandelt neben den hier angeschnittenen Techniken weitere wichtige Werkzeuge.

Vor den Migrationen bleiben

Migrationen werden in den kommenden Monaten und Jahren weiterhin auf uns Entwickler:innen einprasseln. Mit der OpenRewrite Technologie und dem hier beschriebenen Ansatz des Migration-Engineerings können Organisationen sich selbst in die Lage versetzen, wieder planvoll zu agieren, anstatt weiterhin nur zu reagieren. Einen guten Startpunkt bieten hierbei die OpenRewrite Step-by-Step-Guides rund um Aktualisierung von Java 5 bis Java 21 oder die Migration von Java EE zu Jakarta EE 10.0. Durch die Anwendung dieser beiden Guides können Teams lernen, wie viel Zeit bei der Migration von 20 Jahren alten Anwendungen einsparbar ist. Das gelernte Wissen kann dann für interne Migrationen und Bibliotheken angewandt werden, um die Organisation für weitere 30 Jahre Java fit zu halten.

Neugierig geworden?
Merlin Bögershausen ist Speaker der JCON!
Dieser Artikel behandelt das Thema seiner JCON-Session. Du konntest nicht live dabei sein? Kein Problem – das Video wird nach der JCON verfügbar sein. Anschauen lohnt sich!

Total
0
Shares
Previous Post

Schluss mit ORM-Problemen: EclipseStore Hands-On mit dem Leiter des Open-Source-Projekts

Next Post

JUnit 5: Acht Jahre Innovation – Viele Java-Entwickler verpassen den Anschluss

Related Posts