Playbook
Java 8 → 21: The Six-Sprint Upgrade Playbook
How we take a decade-old Spring Boot service from Java 8 to 21 without an outage: the order of work, what breaks, and what the test backfill really costs.

We did this on a fourteen-year-old Spring Boot core for a lender: forty-plus open CVEs, a release process nobody wanted to touch, and a business that could not take downtime. Six sprints, zero outages. This is the order that made it possible.
Sprint 1 — Tests before anything moves
The instinct is to start upgrading. Do not. Write characterisation tests around the behaviour you cannot afford to change, especially money paths. You are not testing for correctness — you are recording what the system does today so you can prove it still does it tomorrow.
Sprint 2 — Build and dependencies, still on 8
Get the build reproducible and dependencies current within Java 8. Half the eventual pain is old libraries, not the language. Doing this first separates two kinds of breakage instead of mixing them.
Sprint 3 — Java 11
The big one: the module system, removed Java EE packages (JAXB, JAX-WS, annotations), and reflection warnings that become errors. Most projects lose the most time here.
Sprint 4 — Java 17
Comparatively calm. Strong encapsulation bites anything reaching into internals — usually an old mocking or serialisation library.
Sprint 5 — Java 21 and the framework
Spring Boot 3 moves javax.* to jakarta.*. Mechanical, wide-reaching, and best done as its own sprint so the diff stays reviewable.
Sprint 6 — Cutover
Run old and new side by side, shift traffic gradually, keep the rollback one command away. Nobody should need an outage window.
| What usually bites | Where |
|---|---|
| Removed Java EE modules (JAXB, JAX-WS) | Java 11 |
| Reflection into JDK internals | Java 17 |
| javax → jakarta across the codebase | Spring Boot 3 |
| Date/time behaviour in edge cases | Everywhere; tests catch it |
| A library with no maintained release | Usually one; plan a replacement |
Related: modernising without downtime.
Questions
How long does a Java upgrade take?
Typically four to eight sprints depending on dependency age and test coverage. Upgrades start at $799 and are quoted fixed-price after we read the build.
Can it be done without downtime?
Yes, in almost every case — run both versions in parallel and shift traffic gradually. We have never needed an outage window for an upgrade.


