[KongchangAI]
· 3 min read· 1,974 words

The Java 8 Migration Ledger Is Flipping: Why Staying Put Is the Expensive Option

The Java 8 Migration Ledger Is Flipping: Why Staying Put Is the Expensive Option

With AI-assisted migration tools now mature, staying on Java 8 costs more than upgrading.

The article addresses how engineers can convince management to upgrade Java, noting the cost curve has reversed. Multiple factors keep teams on Java 8—module system encapsulation, API removals, Spring Boot 3's Java 17 requirement, and the javax-to-jakarta namespace migration—while Oracle and Spring keep extending support deadlines. Yet the cost of staying is real: Netflix measured ~20% CPU reduction moving from Java 8 to 17 with no code changes, Spring Boot 2.7 patches require payment, and Kafka 4.0, JUnit 6 already require Java 17. The key variable is OpenRewrite recipes plus AI agents, compressing Amazon's estimated 50 developer-days to hours. The author recommends targeting version 21 or 25 directly.

About one in four Java codebases in production still runs on Java 8. This has long been an inside joke in the industry, but the joke works because it accurately describes reality. A Java developer dedicated a YouTube video to addressing this issue: he wanted to help engineers who are trying to convince management to upgrade by recalculating the assumption that "upgrading isn't worth it." The conclusion is that the cost curve for migration has changed, and staying on Java 8 has actually become the more expensive path.

MCP client-server authorization interaction diagram

Why So Many Companies Are Stuck on Java 8

The Pitfalls Left by the Module System and API Removals

The module system introduced in Java 9 enforced encapsulation of JDK internals. Any library that accesses sun.misc or com.sun via reflection—Mockito, Lombok, Hibernate, Spring proxies—will start throwing errors or warnings. At the same time, RT.jar was removed, and tools that scan the classpath for dependencies stopped working.

By Java 11, a batch of long-standing APIs was outright deleted. JAXB is a typical example: applications using it to map XML to objects simply won't compile after upgrading to 11 unless you manually find replacement libraries on Maven Central. Since everything runs fine on Java 8, many teams choose to "deal with it later"—and later never comes.

JPMS (Java Platform Module System) is the modularization infrastructure introduced in Java 9, centered on the module-info.java descriptor and platform modules like java.base. It not only splits the JDK itself into roughly 70 modules but also, through strong encapsulation, by default prohibits deep reflective access to sun.misc.Unsafe, internal fields of java.lang.reflect, and more, unless you explicitly add --add-opens or --add-exports startup parameters. This is precisely why libraries that rely on bytecode manipulation or reflection, such as Mockito and Lombok, suddenly throw InaccessibleObjectException on Java 9+. Meanwhile, RT.jar was removed, and classpath-scanning tools (like older versions of Spring's component scanning optimization) lost their enumerable class index and need to switch to the jrt:/ filesystem or ClassLoader.getSystemResources. Understanding JPMS's encapsulation boundaries is key to determining whether an upgrade requires code changes or just JVM parameter adjustments.

The Compound Effect of Upgrade Barriers

Postponing upgrades brings snowballing debt. Spring Boot 3.0 requires Java 17, which means teams staying on Java 8 are also trapped on Spring Boot 2.7. And once you decide to upgrade, you have to simultaneously handle JDK breaking changes, framework migration, and the full namespace migration from javax.* to jakarta.*.

The root of the namespace problem lies in the fact that when Oracle handed Java EE over to the Eclipse Foundation, it retained the trademark rights, and Eclipse could not modify anything within the javax package. In May 2019, Eclipse announced that negotiations had broken down, and all APIs were renamed to Jakarta. According to Amazon's estimates, upgrading from Java 8 to 17 averages 50 developer-days per application—a number that will come up again later.

Jakarta EE namespace migration is the most underestimated hard bone on the upgrade path. In 2017, Oracle handed Java EE over to the Eclipse Foundation but retained the "Java" trademark, preventing Eclipse from continuing to use javax.* package names in new specification versions. After negotiations broke down in 2019, all API package names were changed to jakarta.*, completed in one fell swoop in Jakarta EE 9. For applications, this means every import javax.servlet.*, javax.persistence.*, javax.validation.* in the source code must be changed to jakarta.*, and third-party libraries must be upgraded to compatible versions simultaneously, otherwise you'll get ClassCastException from the same class being loaded by two class loaders. Spring Boot 3.0 uses Jakarta EE 9+ as its baseline, making it the point where two barriers—mandatory Java 17 and mandatory jakarta namespace—arrive simultaneously. This is the fundamental reason why upgrade workload spikes dramatically at Spring Boot 3.

Java 8 Just Won't Die

Oracle's commercial support for Java 8 was originally supposed to end in 2019, then was pushed to the end of 2030. Spring Boot 2.7's commercial support went from February 2025 to August, then to the end of 2026, and now it's June 2029. Oracle's roadmap even has a tier called "sustaining support" with an end date listed as "indefinite"—the company explicitly states that support duration depends on customer adoption. The more companies stay on 8, the longer support for 8 lasts.

Historical screenshot of the JavaX namespace negotiation breakdown

The Real Cost of Staying Put

Performance and Cost: Data from Netflix

In 2023, Netflix publicly acknowledged that at the beginning of the year, most of their services were still running on Java 8. If one of the world's largest Java users was in this situation, it shows that being stuck on an old version is by no means simply laziness or stinginess.

Their case study migrating from Java 8 to 17 provides key data: with no code changes whatsoever, CPU usage for the same services dropped by about 20%, purely due to improvements in the G1 garbage collector. When Netflix's Paul Baker was asked at QCon how to convince management to sign off, he answered: "That number is real." At Netflix's scale, a 20% CPU savings means significant cost reduction.

Paul Baker sharing Netflix migration data at QCon

This shows that the path to convincing management is actually quite clear: find a number that saves money. And this is just the gap from Java 8 to 17. The compact object headers introduced in Java 25 LTS can reduce heap memory by 22% and CPU by 8% under specific workloads, and it only requires adjusting a single JVM flag.

The G1 garbage collector was introduced in Java 7 and became the default GC starting in Java 9, but in Java 8 it was still only an experimental option, with Parallel GC remaining the default. Between Java 8 and 17, G1 underwent numerous improvements to pause prediction, concurrent marking cycles, and region reclamation efficiency. The roughly 20% CPU reduction Netflix observed mainly came from reduced GC thread overhead and fewer full GCs enabled by these improvements. Additionally, Compact Object Headers (JEP 450) became a product feature in Java 25, compressing each object's header from 96 bits to 64 bits, saving about 22% heap memory and reducing CPU by about 8% under object-intensive workloads, requiring only -XX:+UseCompactObjectHeaders. These two data points show that performance gains from JDK versions often require no changes to business code, making them the most direct arguments when persuading management.

Patch Costs and Ecosystem Disconnection

Performance gains are only noticeable at scale, but there's one cost that applies almost equally to all teams: Spring Boot 2.7 and Spring Framework 5.3 in the Java 8 ecosystem have fallen out of free support, and security patches require paid access.

What's more troublesome is that the ecosystem is actively decoupling. Kafka 4.0 and JUnit 6 both require Java 17, with the latter's reasoning being that "important projects we depend on may abandon Java 8 before we do." This means teams staying on old versions will find more and more toolchains becoming unavailable.

How AI Tools Are Rewriting the Migration Economics

This issue has seen substantive changes in the past year. OpenRewrite provides composite recipes for each upgrade hop (8→11→17→21→25) as well as Spring Boot 2→3: automatically restoring JAXB and JAXWS dependencies, replacing JDK internal calls with supported APIs, and rewriting javax to jakarta across the entire codebase. You just add a Maven or Gradle plugin, run it, and get a reviewable diff.

The real new variable is AI tools starting to wrap these recipes. After a recipe generates a diff, an agent automatically runs the build, reads errors, fixes issues, and loops until the build passes. GitHub's Java modernization agent and Amazon Q both offer this capability. For Amazon, what previously took 50 developer-days per application was shortened to a few hours using AI tools internally.

AI tools compressing migration time from dozens of days to hours

It's worth noting that the data published by GitHub and Amazon also serves their purpose of selling tools, so they likely showcase best-case scenarios. Netflix is different—they don't sell development tools, yet they used Claude Code in headless batch mode to migrate 3,000 to 4,000 services from Spring Boot 3 to Spring Boot 4. Paul Baker said the agent outperformed their deterministic tools.

OpenRewrite is an automated refactoring framework based on source AST that encodes upgrade knowledge into composable "recipes." For example, UpgradeToJava17 chains together JDK internal API replacement, dependency version updates, and syntax adjustments, while SpringBoot2To3 additionally completes the javax to jakarta rewrite. After a recipe runs, it outputs a standard diff that can go through code review, so it doesn't bypass team governance. The new development is the agentic loop: after a recipe produces a diff, AI tools automatically trigger builds, parse compilation or test errors, and generate fix patches, repeating until it passes. GitHub's Java modernization agent and Amazon Q both adopt this pattern. It's important to distinguish that OpenRewrite handles deterministic mechanical rewriting, while AI agents handle residual issues that recipes can't anticipate—they are complementary, not substitutes.

What Kind of Persuasion Logic Works Now

A few years ago, when management said "no," they saw estimates like "50 dedicated developer-days, all other work paused," with returns being performance improvements that only materialize at scale. Refusal was reasonable.

The ledger now needs recalculation. On the cost side: the same migration might take only hours to weeks, with most of the time spent by agents running in the background, requiring developers to intervene only at checkpoints, so new feature development doesn't have to come to a complete halt. On the benefit side: significant performance gains, no more paying extra for security patches, maintaining toolchain compatibility, and no longer having to track lifecycle deadlines that might continue to be postponed.

Of course, there are real exceptions. Someone runs Spring 2.7 and Java 11 on a telecom system serving 40 million users per month, where the risk assessment for migration is entirely different; some teams' WebSphere versions limit which JDKs they can use; and an application for a Dutch municipality runs on servers maintained by an external contractor, with neither the willingness to upgrade nor the budget for the city to pressure them.

Java champion Johan Vos has a saying that encapsulates this trade-off: migrating to a new version has a small but immediate cost; staying on an old version has a huge, catastrophic but not immediate cost. Now that AI-assisted migration tools have matured, the interest on this deferred bill is clearly higher than the transaction fee for early repayment. For those considering action, the recommended target version is to jump straight to 21 or 25—free support for 17 has ended, and there's no point staying there.

One-line Summary

With AI-assisted migration tools now mature, staying on Java 8 costs more than upgrading.

Paragraph Summary

The article responds to the old question of how engineers can convince management to upgrade Java, pointing out that the cost curve has reversed. The reasons for being stuck on Java 8 are multiple: Java 9's module system encapsulating internal APIs, Java 11 removing APIs like JAXB, Spring Boot 3 mandating Java 17, and the full namespace migration from javax to jakarta—all four combined create snowballing upgrade barriers. Meanwhile, Oracle and Spring keep extending support deadlines, making "deal with it later" a habit. But the cost of staying put is real: Netflix measured that migrating from Java 8 to 17 reduced CPU by about 20% with no code changes, Spring Boot 2.7's security patches require payment, and Kafka 4.0 and JUnit 6 already require Java 17. The key variable is the migration approach combining OpenRewrite recipes with AI agents, compressing Amazon's estimated 50 developer-days to a few hours, and Netflix also used Claude Code to batch-migrate thousands of services. The author recommends jumping directly to target version 21 or 25.

#Java 8 migration#Java 17 upgrade#AI-assisted migration#OpenRewrite#Netflix Java performance#Jakarta EE namespace#Spring Boot 3
Share:

Related articles