Engineering Practitioner Brief / 18 May 2026
Java and Spring Legacy Cost
Java has unusually long-lived production deployments and Spring is the framework most of them run on. That combination makes Spring upgrades the most common single-framework cost line in enterprise tech-debt conversations. This page collects the per-step cost across the JDK ladder (8 to 21), the Spring ladder (3 to 6), and the Java EE to Jakarta EE rename that wraps both. Numbers assume US fully-loaded engineer rates of around $85 per hour.
The JDK Ladder
Java versions matter for Spring because each Spring major has a JDK floor. Spring 5 requires Java 8. Spring 6 requires Java 17. The JDK ladder is therefore a prerequisite for the Spring ladder, and the cost of moving each step is roughly stable per application.
| From | To | Major Breaking Changes | Hours / 100K LOC |
|---|---|---|---|
| JDK 8 | JDK 11 | Module system, removed APIs (com.sun.*), CMS GC removal | 40 to 80 |
| JDK 11 | JDK 17 | Sealed classes, pattern matching, fewer breaking changes | 20 to 40 |
| JDK 17 | JDK 21 | Virtual threads, sequenced collections, mostly additive | 12 to 30 |
| JDK 8 | JDK 21 (full ladder) | Cumulative of above | 70 to 150 |
The JDK 8 to 11 step is the most expensive single hop because of the Java Platform Module System and the removal of internal com.sun.* APIs that many libraries depended on. The JEP 320 removal of CORBA and Java EE modules from the JDK (JDK 11) also forces affected applications to add explicit dependencies they had previously been getting for free. The official Oracle JDK Migration Guide enumerates the changes per release.
The Spring Ladder
Spring releases maintain backward compatibility well enough that single-major-version upgrades are usually straightforward. Multi-major jumps compound the deprecation work and often pull in transitive library upgrades that have their own breaking changes.
| From | To | Notable Changes | Hours / 100K LOC |
|---|---|---|---|
| Spring 3 | Spring 4 | Java config preferred, JDK 6+ baseline | 40 to 100 |
| Spring 4 | Spring 5 | JDK 8+, reactive stack added, JUnit 5 support | 60 to 140 |
| Spring 5 | Spring 6 | JDK 17+, javax to jakarta rename, removed deprecated APIs | 120 to 280 |
| Spring Boot 1 | Spring Boot 2 | Reactive support, actuator endpoint changes | 50 to 120 |
| Spring Boot 2 | Spring Boot 3 | Spring 6 baseline, javax to jakarta, observability | 80 to 200 |
The Spring 5 to 6 step is the most expensive single hop in the modern era because of the javax-to-jakarta rename. Spring Boot 3 inherits the same constraint. Most teams treat the JDK upgrade, the Spring upgrade, and the Jakarta rename as a single multi-month project rather than three sequential ones, because the test and deploy overhead is dominated by the cutover and is paid once.
The Java EE to Jakarta EE Rename
When Oracle transferred Java EE stewardship to the Eclipse Foundation in 2017, the Eclipse Foundation was prohibited from continuing to use the javax package namespace for new versions. The result was the Jakarta EE 9 release in 2020, which renamed every javax.* package to jakarta.*. Spring 6 and Spring Boot 3 require Jakarta-namespace APIs.
The mechanical part is well-handled by the OpenRewrite migration recipes: import statements, fully-qualified names in strings, and most third-party library references get rewritten automatically. The harder part is libraries that have not yet published Jakarta-namespace versions. As of 2026, the major libraries (Hibernate, Tomcat, Jetty, Jersey, Bean Validation implementations) all have Jakarta-namespace releases. Smaller libraries and internal forks often do not, and have to be either upgraded, patched locally, or replaced.
Reference timing: a 200K-line Spring application with a typical library footprint takes 100 to 250 engineer-hours for the namespace rename alone, of which 60 to 80 percent is handled by OpenRewrite. The remaining hours are manual repairs for libraries without Jakarta releases.
The Legacy CI Tax (Hudson and Ancient Jenkins)
Hudson was renamed Jenkins in 2011. Some enterprise installations are still running Hudson, or running Jenkins versions from before 2018. The Jenkins security advisory feed publishes dozens of CVEs per year, some of which affect old core versions that are no longer eligible for patches. The decision to leave legacy Jenkins running is the decision to accept that risk.
The cost of upgrading a 10-year-old Jenkins is dominated by plugin recreation. Modern Jenkins plugins often have different configuration interfaces than their old counterparts. Pipeline scripts written for freestyle jobs do not run on declarative pipelines without rewriting. Some plugins are abandoned and have no modern equivalent, forcing a substitute plugin choice or a custom build step.
Typical sizing: 80 to 300 engineer-hours per Jenkins instance, plus 4 to 12 hours per build pipeline rewrite. For an enterprise running 50 build pipelines on a legacy Jenkins, the full migration is a quarter-long project. Migration to a managed CI like GitHub Actions, GitLab CI, or CircleCI is usually a similar magnitude of work and reduces the long-term maintenance overhead. See the sister site cicdcost.com for the comparative-cost arithmetic.
The Dependency Layer Beneath Spring
Spring rarely runs alone. The libraries that show up most often in Spring tech-debt audits are also the ones that block upgrades:
- Hibernate. Major upgrades roughly track Spring majors. Hibernate 5 to 6 was the javax-to-jakarta hop; Hibernate 7 (2025) brought further breaking changes. Most JPA-heavy applications spend 40 to 100 hours per major Hibernate upgrade on subtle query-semantics differences.
- Jackson. Stable interface, occasional breaking changes around polymorphic deserialisation and Kotlin support. Upgrade cost typically low (under 20 hours per app per major) but worth keeping current because of regular CVE flow.
- JUnit. JUnit 4 to JUnit 5 is a substantial test rewrite. For a large test suite, 40 to 150 hours of test refactoring, partly automated by IntelliJ's inspection.
- Lombok. Lombok occasionally breaks when newer JDKs change internal compiler APIs. Keeping Lombok current with the JDK is a small but recurring tax.
- Tomcat or Jetty. Embedded servlet container choice propagates Spring constraints. Tomcat 10+ is Jakarta-namespace; Tomcat 9 is the last javax line. Switching servlet container is rare but happens when one drops support for the target JDK.
Recommended Sequence for a Spring 3 / JDK 8 Starting Point
A pragmatic ordering when starting from a Spring 3, JDK 8, javax-namespace baseline:
- JDK 8 to JDK 11. The biggest single-hop JDK cost; pay it first while still on a stable Spring 4 or 5.
- Spring 3 to Spring 5 in two steps (3 to 4, then 4 to 5). Build pipelines and tests should still run cleanly on JDK 11 throughout.
- JDK 11 to JDK 17. Smaller hop, sets up Spring 6 eligibility.
- Apply the OpenRewrite javax-to-jakarta recipe on the Spring 5 codebase. Verify in a long-running branch.
- Spring 5 to Spring 6, with the Jakarta namespace already in place. The Spring 6 upgrade becomes mostly mechanical.
- JDK 17 to JDK 21. Additive for most applications; virtual threads become available for new code.
Total elapsed time for a 200K-line Spring application: typically 12 to 18 months of effort, with shipping continuing throughout via the ladder pattern. See framework migration cost for the per-LOC summary across multiple stacks.
Related Reading
- Framework migration cost
- Dependency debt cost
- .NET Framework legacy cost
- JavaScript and Node legacy cost
- Legacy code refactoring cost
Frequently Asked Questions
How much does it cost to upgrade Spring 3 to Spring 6?
For a 100K-line Spring application, plan $120K to $280K across 6 to 14 months. The cost is dominated by the JDK upgrade (Java 8 to 17 minimum, 21 preferred), the Java EE to Jakarta EE namespace migration, and the deprecation cleanup across three major Spring releases (3 to 4 to 5 to 6).
What is the hardest part of the Spring migration?
The javax.* to jakarta.* package rename forced by Spring 6 and Spring Boot 3. Every import statement that touches Servlet API, JPA, JAX-RS, or Bean Validation has to be rewritten. OpenRewrite recipes handle the mechanical part well. The harder part is third-party libraries that have not published Jakarta-namespace versions yet.
Do I need to upgrade JDK at the same time?
Yes. Spring 6 requires Java 17 as a minimum. If you are on JDK 8 or 11, the JDK upgrade is a prerequisite. The JDK upgrade alone (JDK 8 to JDK 21) is typically 20 to 50 engineer-hours per application for the basic compile-and-run work, plus more if the app uses internal JDK APIs that were removed in later versions.
Is the OpenRewrite recipe pack worth using?
Yes. The OpenRewrite community recipes for Spring Boot upgrades, the javax-to-jakarta rename, and the Jakarta EE 9 to 10 migration cover the mechanical work reliably. A team that used to spend 2 to 3 days on the mechanical part of a Spring Boot major upgrade now spends 2 to 3 hours. The architectural work that comes after still has to be done by humans.
What about Spring Boot 2 to Spring Boot 3?
Spring Boot 3 brought the same javax-to-jakarta rename and a Spring 6 baseline. The official Spring Boot 3 migration guide enumerates the breaking changes; the OpenRewrite recipe handles the bulk of the mechanical work. Plan 80 to 200 engineer-hours per service for a typical Spring Boot 2 to 3 upgrade with full test verification.
What does legacy Jenkins or Hudson cost?
A legacy Jenkins or Hudson installation that has not been upgraded in years carries security risk (the Jenkins security advisory feed is long) and maintenance risk (plugins drop support for old core versions). A typical migration from a 10-year-old Jenkins to a modern Jenkins or to GitHub Actions costs 80 to 300 engineer-hours, dominated by recreating plugin pipelines in the new system.