Spring Boot 4.0 Migration Guide for Production Teams: What Actually Breaks and How to Upgrade Safely
Spring Boot 4.0 is not a routine version bump. For most Java teams, it is a platform migration touching Java baseline, Jakarta alignment, dependency graph, packaging behavior, test setup, and especially JSON handling with Jackson 3.
This guide summarizes the official migration notes into a practical, production-focused playbook.
TL;DR
- Upgrade to the latest 3.5.x first, remove deprecations, then move to 4.0.
- The biggest migration risks are module/starter changes and Jackson 3 behavior shifts.
- Use a staged rollout: stabilize with compatibility options, then converge to native Boot 4 patterns.
Why this migration is bigger than usual
Spring Boot 4.0 upgrades major foundations at once:
- Java baseline: 17+ (latest LTS recommended)
- Servlet baseline: 6.1
- Jakarta EE: 11
- Spring Framework: 7.x
- Preferred JSON engine: Jackson 3
That means many apps compile but still fail at runtime (serialization contracts, missing transitive dependencies, test infra drift, startup assumptions).
Step 0: Before touching Boot 4
1) Move to latest Boot 3.5.x
Do this first so you are already on the newest dependency line and can clean warnings progressively.
2) Remove 3.x deprecations
Anything deprecated in Boot 3.x is a removal candidate in 4.0.
3) Audit external BOMs and managed libs
If you pin versions outside Boot management (for example Spring Cloud), validate Boot 4 compatibility before upgrading.
4) Re-check environment requirements
- Java 17+
- Kotlin 2.2+ (if used)
- GraalVM 25+ for native-image
Features removed in Boot 4 you must account for
From the migration guide, notable removals include:
- Undertow support dropped (Servlet 6.1 baseline incompatibility)
- Pulsar reactive auto-configuration removed
- Embedded launch scripts for fully executable jars removed
- Spock integration removed (Groovy 5 gap)
- Direct Boot support removed for Spring Session Hazelcast/MongoDB integrations
If your runtime or packaging relies on these features, plan alternatives before switching versions.
The biggest structural change: modularization
Boot 4 introduces smaller focused modules and a more consistent starter model.
What changes in practice
- More technologies now have dedicated starters.
- Test infra is also modularized with companion test starters.
- If your app relied on implicit transitive deps, you may now miss required pieces.
Typical example
If you used Flyway/Liquibase only via third-party dependency before, Boot 4 expects explicit Boot starter usage:
spring-boot-starter-flywayspring-boot-starter-liquibase
Test side impact
You should depend on technology-specific test starters, not just one global test starter.
For example, security test behavior now expects proper security test starter alignment.
Transition path: classic starters
For teams upgrading a large legacy codebase, Boot 4 provides a temporary bridge:
spring-boot-starter-classicspring-boot-starter-test-classic
Use this to restore broad classpath coverage, fix imports/configs, then progressively move to focused starters.
Treat this as transitional, not the end state.
Jackson 3 migration: the highest-risk area
Boot 4 prefers Jackson 3 and introduces major ecosystem changes.
Key changes
- Group/package migration from
com.fasterxml.jacksontotools.jackson(with exceptions) - Boot integration class renames for consistency
- Property namespace changes for JSON-specific read/write settings
- Automatic registration of all classpath Jackson modules by default
Immediate risks
- API payload differences (date/time formatting, null/default handling)
- stricter or different deserialization outcomes
- polymorphic payload regressions
- subtle contract breakage across clients
Useful compatibility controls
Boot 4 adds transitional options:
-
spring.jackson.use-jackson2-defaults=true(closer behavior to Boot 3/Jackson 2 defaults) -
spring-boot-jackson2module as temporary stop-gap
Use these as migration aids, not long-term architecture.
Other migration changes teams miss
- JSpecify nullability annotations can affect Kotlin/null-check tooling
- Logback default charset behavior now aligned with UTF-8 expectations
-
BootstrapRegistry/EnvironmentPostProcessorpackage moves can break deep integrations -
PropertyMapperno longer maps null source values by default (usealways()when needed) - DevTools LiveReload now disabled by default
- Optional Maven dependencies no longer included by default in uber jars
Recommended production migration strategy
Phase 1 — Stabilize
- Upgrade to latest 3.5.x
- Remove deprecations
- Upgrade to Boot 4 with classic starters
- Ensure app starts and core flows run
Phase 2 — Correctness
- Add contract tests for JSON payloads
- Verify Jackson behavior against real fixtures
- Fix dependency graph explicitly (main + test)
- Validate startup/runtime with production-like config
Phase 3 — Convergence
- Replace classic starters with focused starters
- Remove compatibility flags one by one
- Re-check observability and actuator behavior
- Final cleanup and docs update
CI/CD checks you should add before rollout
- Dependency diff gate (3.5 vs 4.0)
- Contract test stage for API payload compatibility
- Integration tests with representative fixtures
- Canary rollout + error budget guardrails
- Fast rollback trigger on error spike
Practical checklist
- [ ] Latest 3.5.x in production first
- [ ] No known 3.x deprecations in code paths
- [ ] Environment meets Java/Jakarta/Servlet requirements
- [ ] Removed features audited (Undertow, etc.)
- [ ] Jackson migration strategy selected (native vs transitional)
- [ ] Starter and test-starter dependencies reviewed
- [ ] Contract/integration tests green
- [ ] Canary + rollback plan validated
Final takeaway
Spring Boot 4.0 gives a cleaner long-term model, but migration quality depends on discipline:
- explicit dependencies,
- explicit JSON behavior,
- explicit rollout strategy.
If you treat it as a platform migration instead of a “version bump”, the upgrade is predictable and safe.
Top comments (2)
Thanks for the detailed article.
Few points-
1) 'date/time formatting, null/default handling' - the upgrade should be backward compatible, why would this be an issue post the upgrade?
2) Performance impact - what is the impact on memory usage, start up time, response times
3) Many a times in Spring/Boot upgrades, existing components do not get loaded and we need (or needed) to write additional @Config classes to load those components
4) A major impact of the upgrade should be reduction in security vulnerabilities, hope that was seen too
Yes 👍 super points, thanks!
1) You’re right: upgrade is mostly backward-compatible, but issues usually come from app-specific mapper config + contract assumptions, not Boot alone.
2) Perf impact depends on each app; I recommend before/after measurement (startup, memory, p95 latency) rather than generic numbers.
3) Agreed — with modularization, some components may need explicit starters/config to load correctly.
4) Yes, security posture should improve with newer dependencies, but it’s best validated with a CVE scan before/after.