A developer in a dark hoodie with a lanyard types on a laptop at a desk in a quiet office
Run the old code on the new JDK first, then fix what breaks.

A Java 8 to 17 migration guide comes down to one rule: get the code running on 17 before you improve it. Inventory what you run, scan it with jdeps, upgrade build tools and libraries, compile and test the old code on 17, fix what breaks, and only then start using new language features.

Most of the work isn't the Java version. It's the libraries, build plugins and old workarounds that relied on things Java 9 to 17 removed. This guide covers the steps, the challenges and their errors, the tools, and whether to aim for 17 or 21.

Key takeaways
  • Get the existing code running on Java 17 before changing it. New language features come last.
  • Most breakage traces to a few removals between Java 9 and 17: the Java EE modules, Nashorn, CMS, and access to JDK internals.
  • Run jdeps --jdk-internals early. It reports code and libraries that use internal JDK APIs that Java 17 blocks.
  • Spring Boot 3 needs Java 17 and renames javax packages to jakarta, so plan the two moves together.
01

Java 17 or Java 21: which target?

Both are long-term support releases, and the code changes from Java 8 are largely the same, because the removals below all happened by Java 17. The difference is support time. Oracle's Java SE Support Roadmap lists:

ReleasePremier Support untilExtended Support until
Java 17September 2026 (or later)September 2029 (or later)
Java 21September 2028 (or later)September 2031 (or later)

Checked 27 October 2026 against Oracle's roadmap, updated 20 October 2026. Other vendors publish different dates; we compare them in Java end of life dates by vendor.

Pick 21 for the longer runway, unless a framework, application server or vendor product you depend on supports 17 and not yet 21.

02

The Java 8 to 17 migration guide, step by step

Oracle's own JDK 17 Migration Guide sets out the preparation: download the new JDK, run your program before recompiling, update third-party libraries, compile, and run jdeps. Here's that order expanded into the Java 8 to 17 migration steps we'd follow.

  1. Inventory. List each application's JDK version and vendor, build tool and plugins, framework, application server, and the JVM flags in its startup scripts. Some old flags stop the JVM starting on 17.
  2. Scan with jdeps. Oracle's guide says: "Run the jdeps tool on your application to see what packages and classes your applications and libraries depend on." Add the --jdk-internals option, which the jdeps manual describes this way: "Finds class-level dependences in the JDK internal APIs." Run it over your own JARs and every library JAR.
  3. Upgrade build tools and plugins. Oracle's guide notes: "For every tool and third-party library that you use, you may need to have an updated version that supports the latest JDK release." Compiler, test and packaging plugins written for Java 8 often fail first.
  4. Upgrade dependencies. Move libraries to versions that support 17, and add back the Java EE libraries the JDK no longer ships (step 6 covers which).
  5. Compile and run your tests on 17 with the old code. Change the JDK, not the code (Oracle's step is "Run Your Program Before Recompiling"). Every failure now is a migration issue, not a new bug.
  6. Fix what breaks. Work through the table below. Treat each --add-opens workaround as a ticket to remove later.
  7. Then adopt new features. Once production runs on 17, start using records (final in Java 16) and text blocks (final in Java 15). Earlier, they mix refactoring into the migration and make failures harder to trace.
03

Java 8 to 17 migration challenges

These are the breaks that turn up most, with the JEP or Oracle document behind each.

What breaksWhat you'll seeThe fix
Java EE and CORBA modules removed in Java 11 (JEP 320): JAXB, JAX-WS, javax.annotation, JTA, CORBACode using javax.xml.bind or javax.xml.ws no longer compiles or runs; JEP 320 says these applications "will not compile or run"Add the standalone libraries as dependencies. JEP 320 points to Maven Central. For CORBA, JEP 320 expects no standalone version unless third parties maintain one
Strong encapsulation of JDK internals in Java 17 (JEP 403)Oracle's guide: code reflecting into non-public java.* members "will throw an InaccessibleObjectException"Upgrade the library that does it. As a stopgap, --add-opens for the named package. --illegal-access no longer relaxes this
Use of sun.* and other internal APIsCompile errors; jdeps --jdk-internals lists each useMove to supported APIs. sun.misc.Unsafe stays available, per JEP 403
Nashorn JavaScript engine removed in Java 15 (JEP 372)Imports of jdk.nashorn.* fail, and the jjs tool is goneUse a separately maintained JavaScript engine, or move the scripts out of the JVM. The javax.script API itself is unaffected
CMS garbage collector removed in Java 14 (JEP 363)Warning: "Ignoring option UseConcMarkSweepGC; support was removed in" the named version, then the JVM runs with the default collectorRemove legacy GC flags (Oracle lists combinations that "will cause your application to fail to start") and load-test on G1, the default
Security Manager deprecated for removal in Java 17 (JEP 411)A warning at startup, or when System::setSecurityManager is calledPlan to remove it. Oracle's guide: "There is no replacement for the Security Manager"
New version string (Java 9, JEP 223)Code that parses 1.8 from java.version gets 17 and failsParse with Runtime.Version instead
rt.jar and tools.jar removedBuild scripts and tools that reference them failUpdate the scripts and the tools that expect them
04

Java 8 to Java 17 migration and Spring Boot issues

On Spring Boot 2, the Java move and the framework move tend to merge. Spring's Spring Boot 3.0 Migration Guide says "Spring Boot 3.0 requires Java 17 or later," and "Jakarta EE now uses jakarta packages rather than javax," so servlet, persistence and validation imports all change.

Spring Boot 2.7.18 "is compatible up to and including Java 21," per its docs, so you can split the work: move 2.7 to Java 17 first, then to Spring Boot 3. The rest is in Spring Boot 2 end of life.

05

Java 8 to Java 17 migration tools

  • jdeps ships with the JDK and reports dependencies on JDK internals.
  • OpenRewrite publishes a Migrate to Java 17 recipe that, per its docs, will "update and add dependencies that are no longer directly bundled with the JDK" and "replace deprecated API with equivalents when there is a clear migration strategy."
  • For Spring, Spring's guide points to OpenRewrite recipes, the Spring Boot Migrator and IntelliJ IDEA.

Recipes handle mechanical changes. They can't tell you which --add-opens flag hides a library that should have gone years ago, or which module nobody dares touch. That's part of reducing technical debt. Old Java estates are common legacy system examples, and the risks of end-of-life software don't wait for the migration.

06

Before you size the migration

A migration estimate is only as good as what you know about the code. The 30-day assessment is fixed in scope, at a price agreed before we start. Over 30 days we read the codebase and the architecture around it, map how the application fits together with what it depends on, find the hidden architectural debt, and flag the security and compliance gaps, including every use of JDK internals and every dependency past its support date.

The report says in plain language what is broken and what it would take to fix it, in the order the work should happen. Your team can run the migration from it, or we can do the work as a separate project with its own scope and price. Either way, the report stands on its own.

Book a scoping call to talk through your Java 8 estate.