Java 27 and the LTS question
Six-month releases, an LTS every two years, and a decision most teams make by default rather than deliberately.
Java ships every March and September, with a long-term-support release every two years. That cadence has been stable since Java 9 changed everything in 2017, and it presents every team with a choice they usually make by not making it.
the two strategies#
Ride the LTS train. Upgrade only on LTS versions, roughly every two years. Vendors support them for years. This is what most enterprises do.
Ride every release. Upgrade every six months. Each step is small.
The instinct is that LTS is the conservative choice. It is worth examining that, because it is not obviously true.
the case against LTS-only#
Bigger jumps. Two years of change absorbed at once, including four releases' worth of deprecations and removals, all discovered in the same week.
Deprecation surprises. Features are deprecated in one release and removed a few later. On the every-release path you see the warning and have six months. On the LTS path the warning and the removal can arrive in the same upgrade.
You are testing a configuration fewer people ran. Ironically, the LTS jump from N to N+8 is a path exercised by fewer teams than each individual step.
Two years of free performance left on the table. GC and JIT work lands continuously.
the case for it#
Vendor support. For some organisations this is contractual and ends the discussion.
Fewer upgrade events. Each one has fixed overhead — testing, coordination, sign-off. Four small upgrades can cost more total effort than one large one, even if each is easier.
Library ecosystem lag. Frameworks target LTS versions first. On a non-LTS release you can be waiting for a dependency.
That last one is the real constraint, and it is the honest reason most teams stay on LTS.
the strategy that gets the benefit of both#
Run your tests on every release; run production on LTS.
Add the latest JDK to your CI matrix as a non-blocking job. It costs a few minutes per build. When something breaks, you find out six months early, in a build, rather than during the upgrade under a deadline.
This is a small change with a large effect on how the LTS jump feels, and it is the single most useful thing a team on the LTS path can do.
the upgrade checklist#
- Check the removal list first, not the feature list. Removed APIs and changed defaults are what break you.
- Run with
-Xlint:alland--enable-previewoff. Preview features are not a migration target. - Re-benchmark rather than assuming. GC defaults and JIT behaviour change; usually for the better, occasionally not for your allocation pattern.
- Check your agents. Profilers, APM agents and anything doing bytecode instrumentation are the most common source of upgrade breakage, and they break loudly at startup rather than subtly at runtime.
- Look at what virtual threads did to your pool sizing if you have adopted them. Connection pools sized for a thread-pool world are now the bottleneck.
the general point#
Any dependency with a fixed cadence — Java, Go, Rust, Python, Node, Postgres — presents the same question: small steps often, or large steps rarely.
The answer that keeps being right is small steps often, with the large-step path tested continuously in CI. It converts a scary infrequent event into a boring frequent one, which is the same trick that makes deployment safe.
— Dom, September 8, 2026