Core Java

OpenRewrite: The Automated Migration Tool That’s Quietly Changing How Teams Upgrade Java

It started at Netflix, migrated the Jakarta EE TCK, and is now the dominant automation tool for Java modernization according to InfoQ’s 2025 trends report. Most teams still haven’t heard of it.

1. Where OpenRewrite Came From

OpenRewrite did not originate in a startup or a standards committee. It started inside Netflix’s engineering tools team, where the problem was a very specific and very relatable one: a central platform team needed to deprecate an internal logging library that had been replaced by SLF4J — and despite being deprecated for six years, it still had countless references scattered across Netflix’s entire codebase. The response from product engineers when asked to migrate was blunt and honest: “I don’t have time for this, but if you do it for me, I’ll merge the changes.”

That last sentence is the founding insight behind OpenRewrite. Jonathan Schneider, who was on that engineering tools team, took the request literally. Rather than waiting for teams to migrate on their own schedule, he built a tool that could make the changes automatically — precisely, safely, and in a format that engineers could review in a normal pull request diff. The concept spread from logging to framework migrations, from API replacements to security vulnerability patching, and eventually became an open-source project under the Apache 2.0 license.

Where it stands in 2026

According to InfoQ’s Java Trends Report for 2025, OpenRewrite emerged as the dominant automation tool for Java modernization — notable uses include the javax to jakarta namespace migration and the upgrade of the outdated Jakarta EE Technology Compatibility Kit itself. It is Apache 2.0 licensed, maintained by Moderne, and actively extended by a large open-source community.

2. The Technology Underneath: LST vs AST

To understand why OpenRewrite produces better results than simpler text-based find-and-replace tools — or even standard refactoring tools in IDEs — you need to understand the data structure it works on. Most refactoring tools operate on an Abstract Syntax Tree, or AST, which captures the logical structure of code but strips out formatting details like whitespace and comments. OpenRewrite, by contrast, uses what it calls a Lossless Semantic Tree (LST).

The difference matters in three concrete ways that show up directly in output quality.

How the LST differs from a traditional AST

Type-attributed

Every node in the tree carries full type information — not just the name of a variable or method, but its fully-qualified type, even if that type is defined in a different file or a transitive dependency. This is what allows OpenRewrite to find every reference to javax.persistence.Entity across an entire codebase without false positives, even when the import statement is implicit or aliased.

Format-preserving

Whitespace, indentation, comments, and line breaks are preserved in the tree, so the output code is formatted exactly as the surrounding code is formatted. A recipe that inserts a new import does so in the correct alphabetical position and with the team’s existing indentation style — not with whatever default the tool prefers.

Zero false positives

Because the tree is type-attributed, OpenRewrite can distinguish between two classes that happen to share a name. A recipe targeting javax.servlet.http.HttpServletRequest will not accidentally touch a custom class named HttpServletRequest in a local package — the fully-qualified types are different, and the LST reflects that.

In practice, this means you get pull request diffs that look like a skilled engineer made them: imports in the right place, formatting consistent with the rest of the file, and no collateral changes to unrelated code. That output quality is a significant part of why adoption has accelerated — reviewers trust the output enough to merge it without extensive line-by-line inspection.

3. How Recipes Work — and How They Compose

OpenRewrite’s unit of work is called a recipe. A recipe is a program that defines a set of search and transformation operations to apply to a codebase’s LST. Simple recipes do one thing — rename a type, change a method signature, update a dependency version. More powerful recipes compose many simpler recipes together into a single automated migration pipeline.

The flow of running a recipe is consistent regardless of complexity:

The composable nature of recipes is where the real leverage comes from. The recipe UpgradeToJava25, for example, is a composite recipe that internally chains UpgradeToJava21, which in turn chains UpgradeToJava17, and so on back through the version history. Each layer handles the API removals, deprecations, and build file changes relevant to that specific Java version jump. Running a single recipe to go from Java 8 to Java 25 therefore triggers a precisely ordered, cumulative set of transformations across every relevant file.

Dry-run mode

OpenRewrite supports a dry-run mode that shows you exactly what would change — without touching any files — via mvn rewrite:dryRun or gradle rewriteDryRun. The output is a patch file you can inspect in any diff tool before deciding whether to apply the changes. This makes the tool genuinely safe to run on production codebases: preview first, then apply.

4. The javax → jakarta Migration in Practice

If there is a single migration that put OpenRewrite on the map for the broader Java community, it is the javax.* to jakarta.* namespace rename. This change, which came with Jakarta EE 9 and was required for Spring Boot 3.0, is deceptively large. Every import statement, every annotation, every XML configuration file that references a Java EE API needed to change its package prefix from javax to jakarta. For a real enterprise application, that can mean thousands of changes spread across hundreds of files.

Before OpenRewrite, the most common approach was a combination of global find-and-replace and hours of manual verification. The problem with find-and-replace is that javax appears in many contexts that have nothing to do with Jakarta EE — javax.crypto and javax.net.ssl, for instance, are standard JDK packages that were intentionally not renamed. A naive find-and-replace would corrupt those imports. OpenRewrite’s type-attributed LST sidesteps this completely: it knows which javax.* packages are Jakarta EE namespace changes and which are JDK-owned packages, and it only touches the former.

The Spring Boot 3.0 migration recipe, for example, automatically handles:

What changesExample — beforeExample — afterHandled automatically?
JPA annotationsimport javax.persistence.Entityimport jakarta.persistence.EntityYes
Validation annotationsimport javax.validation.constraints.NotNullimport jakarta.validation.constraints.NotNullYes
Servlet APIimport javax.servlet.http.HttpServletRequestimport jakarta.servlet.http.HttpServletRequestYes
JAXB annotationsimport javax.xml.bind.annotation.XmlElementimport jakarta.xml.bind.annotation.XmlElementYes
pom.xml Spring Boot version2.7.x3.0.xYes
pom.xml Java version property<java.version>1.8</java.version><java.version>17</java.version>Yes
JDK standard packagesimport javax.crypto.CipherUnchanged (not a Jakarta EE class)Yes — left alone
Spring Security config classExtends WebSecurityConfigurerAdapterBean-based security configYes
Third-party libs without Jakarta variantse.g. Ehcache 2 → Ehcache 3No recipe availableManual
Custom business logic relying on removed APIsBespoke use of deprecated internalsCase-by-caseManual

5. JDK Version Upgrades: Java 8 to 25 in One Command

The javax-to-jakarta story is the one most people know. The JDK version upgrade capability is arguably more broadly applicable — and even less well-known. OpenRewrite’s rewrite-migrate-java module provides composite upgrade recipes for every major Java LTS version, each one building on the previous.

Upgrade to Java 25 — Maven (pom.xml plugin configuration)

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>6.35.0</version>
  <configuration>
    <activeRecipes>
      <recipe>org.openrewrite.java.migrate.UpgradeToJava25</recipe>
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-migrate-java</artifactId>
      <version>RELEASE</version>
    </dependency>
  </dependencies>
</plugin>

Run a dry-run first to preview all changes

mvn rewrite:dryRun

Apply changes when satisfied with the preview

mvn rewrite:run

Under the hood, UpgradeToJava25 chains all the intermediate upgrade recipes in the correct order — it is not a single monolithic transformation. When upgrading from Java 8, the chain includes: replacing removed J2EE javax.* classes that were dropped from the JDK in Java 11, migrating deprecated Locale and URL constructors deprecated in Java 17, adopting SequencedCollection methods introduced in Java 21, and handling API changes relevant to Java 25. Additionally, it updates build file target version properties and upgrades build plugins to versions compatible with the new Java release — automatically.

Gradle support

OpenRewrite works equally well with Gradle projects. Add the plugin with id("org.openrewrite.rewrite") version("latest.release") in your build.gradle.kts, configure the active recipe under the rewrite block, add the rewrite-migrate-java dependency, then run gradle rewriteRun. The same dry-run safety valve is available via gradle rewriteDryRun.

6. What OpenRewrite Handles Automatically

Beyond the javax-to-jakarta and JDK version migrations, the recipe catalog covers a broader surface than most engineers realize. The chart below maps the major migration categories and how complete the automated coverage is for each.

OpenRewrite automated coverage by migration category

Estimated percentage of typical migration work handled automatically — remainder requires manual effort. Based on OpenRewrite recipe catalog and community adoption reports

The recipe catalog also extends well beyond Java source code. OpenRewrite can update pom.xml and build.gradle files, application.properties and application.yml configuration, logging configuration files, and even XML-based Spring configuration. When a Spring Boot upgrade moves or renames a configuration property — which happens regularly between minor versions — the recipe updates the property key automatically.

Security patches are another significant category. OpenRewrite has recipes that update specific vulnerable dependency versions — effectively giving teams an automated path to apply CVE patches across a large number of repositories without a separate manual PR-per-repository process. This is particularly valuable for platform teams that manage many services using shared libraries.

7. Where It Stops: The Honest Limits

OpenRewrite is genuinely powerful, but overstating what it does automatically is the fastest way to produce a frustrating experience. There are clear, predictable categories of changes that sit outside what recipe-based automation handles well.

OpenRewrite handles automatically

  • Namespace renames (javax → jakarta)
  • Deprecated API replacements with clear mappings
  • Build file version bumps and plugin upgrades
  • Configuration property key renames
  • JUnit 4 → JUnit 5 annotation and assertion migration
  • Log4j → SLF4J / Logback migration
  • Spring Security config adapter removal
  • Known CVE dependency version patches
  • Import cleanup and organization
  • Third-party libs with official Jakarta EE variants

Requires manual work

  • Third-party libraries with no Jakarta EE variant (e.g. Ehcache 2 → Ehcache 3)
  • Bespoke framework integrations without a recipe
  • Complex business logic refactors that change behavior
  • Architecture-level changes (reactive → virtual threads)
  • Custom annotations that wrap Jakarta EE annotations
  • Database schema or query migration
  • Infrastructure and deployment configuration
  • Semantic behavioral verification (tests still required)

 Tests still matter — critically

OpenRewrite is deterministic and accurate, but it is a refactoring tool, not a verification tool. The changes it produces should always pass through your standard CI pipeline including your test suite. A recipe correctly migrates the syntax — whether the migrated code behaves identically to the original under all conditions is something only tests can confirm. Teams that reduced their test suite “to save time on the migration” discovered this the hard way.

8. OpenRewrite vs Moderne: The Open-Source / Commercial Split

Understanding the relationship between OpenRewrite and Moderne is important for teams evaluating the tool, because the licensing structure has changed and is easy to misread.

OpenRewrite is the open-source core: the refactoring engine, the LST model, and a large catalog of community recipes, all Apache 2.0 licensed. It runs locally against a single repository at a time, with the LST held in memory. For projects small enough to fit in memory, this is fully functional and costs nothing.

Moderne is the commercial platform built on top of OpenRewrite. Its key capability is running recipes across thousands of repositories simultaneously — serializing LSTs to disk, caching them between runs, and providing a collaborative UI for platform teams managing organization-wide migrations. Moderne also provides some enterprise-grade recipes under its own license (MSAL — Moderne Source Available License) that are freely usable by individuals but cannot be incorporated into commercial services by third parties without a Moderne agreement.

CapabilityOpenRewrite (OSS)Moderne (commercial)
LicenseApache 2.0Commercial / MSAL for premium recipes
Repositories at onceOne at a timeThousands simultaneously
LST storageIn-memory (lost after run)Serialized to disk, shared across runs
Very large monoreposLST must fit in memoryChunked, no memory limit
Recipe catalogFull community catalogCommunity + proprietary premium recipes
CI integrationMaven / Gradle pluginManaged pipeline + CLI
Best forIndividual teams, single-repo migrationsPlatform teams, org-wide modernization

For the vast majority of teams — those migrating one or a small number of services — the open-source OpenRewrite tool is entirely sufficient. The Moderne platform becomes compelling once you are a platform engineering team responsible for migrating fifty or more repositories, or when your monorepo is large enough that the LST does not fit comfortably in local memory.

9. Getting Started in Five Minutes

The fastest way to see what OpenRewrite would change in your project is a dry-run against a specific recipe. The example below runs the Spring Boot 3.5 migration recipe in preview mode — no files are changed, and the output is a patch file you can inspect:

Spring Boot 3.5 migration — Maven one-liner (no config changes needed)

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \
  -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
  -Drewrite.exportDatatables=true

Java 25 upgrade — Maven one-liner (no config changes needed)

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

These one-liners require no pom.xml modifications — the plugin is pulled in at runtime via the -U flag and the recipe artifact is resolved at runtime too. This makes it straightforward to try on any Maven project without committing any tooling changes to your repository.

Once you have run the dry-run and are satisfied with the output, commit the configuration to your build file for reproducibility, and add the run step to your CI pipeline. A common pattern is to run a specific recipe as a nightly or weekly job that creates an automated PR whenever it finds changes to make — essentially turning OpenRewrite into a continuous migration safety net rather than a one-time tool.

Browse the recipe catalog first

Before running anything, spend five minutes browsing docs.openrewrite.org/recipes to find the specific recipe that matches your situation. The catalog is large — Spring Boot versions, Quarkus, Micronaut, JUnit, Mockito, logging frameworks, security libraries — and finding the right composite recipe saves significant time versus assembling your own chain of primitives.

OpenRewrite recipe catalog growth over time

Approximate number of available recipes — community and Moderne contributions combined. Estimated from OpenRewrite GitHub releases and community reports. Exact counts vary as recipes are merged and refactored.

10. What We Have Learned

OpenRewrite solves a problem that every Java team eventually faces — the upgrade backlog — with an approach that is fundamentally more reliable than find-and-replace and fundamentally more scalable than manual refactoring. The Lossless Semantic Tree is the key insight: it preserves type information and formatting, which means the output code is accurate and reads like a human wrote it. That trustworthiness is what drives adoption, because it means reviewers can merge the changes with confidence rather than spending hours on line-by-line verification.

The javax-to-jakarta migration is the most famous use case, and it genuinely is excellent at it — handling thousands of import changes across a large codebase in minutes, without touching the JDK-owned javax packages that should remain unchanged. But the JDK version upgrade recipes are arguably the more broadly applicable tool: a single command that chains every intermediate migration step from Java 8 all the way to Java 25, including build file updates and deprecated API replacements, is the kind of automation that turns a weeks-long upgrade project into an afternoon.

The limits are predictable and honest. OpenRewrite cannot handle third-party library migrations that lack a recipe, cannot reason about behavioral correctness, and cannot replace your test suite. It handles the deterministic, mechanical part of migration automation well — the part that does not require judgment. Everything that does require judgment still requires an engineer. That is the right division of labor, and teams that understand it get the most out of the tool.

If your team has been putting off a Spring Boot 3 migration or a JDK upgrade because the manual effort felt daunting, OpenRewrite is the specific answer to that specific problem. The recipe catalog exists, it works, and it takes less time to try than most teams expect.

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
jake
jake
29 days ago

Thank you. The article gave me more clarity around the non-commercial vs commercial offering.

Back to top button