Two dates decide your next few weeks of Spring work:
- 30 June 2026 — Spring Boot 3.5 stopped getting free patches. 3.5.16 was the last one.
- 31 December 2026 — Spring Boot 4.0 stops getting them too.
So if your migration plan says "move to 4.0", you're planning to do this twice. Go to 4.1, which has free support until July 2027.
Here's what the move actually breaks.
The support calendar
| Line | Released | Free support ends | Commercial |
|---|---|---|---|
| 3.4 | Nov 2024 | Dec 2025 (gone) | Dec 2026 |
| 3.5 | May 2025 | Jun 2026 (gone) | Jun 2032 |
| 4.0 | Nov 2025 | Dec 2026 | Dec 2027 |
| 4.1 | Jun 2026 | Jul 2027 | Jul 2028 |
Dates from endoflife.date. Note 3.5's commercial tail runs to 2032 — if your problem is the calendar rather than the code, paying for support is a legitimate plan.
Baselines first
Java 17+ (use 21), Kotlin 2.2+, Jakarta EE 11 / Servlet 6.1, Spring Framework 7.x, GraalVM native-image 25+.
And the boring step that saves the most time: upgrade to the latest 3.5.x and clear every deprecation warning before you touch 4.x. Everything deprecated in 3.5 was deleted in 4.0. Each warning you skip becomes a compile error later, inside a much bigger diff.
1. The starters were renamed and split
| Old | New |
|---|---|
spring-boot-starter-web |
spring-boot-starter-webmvc |
spring-boot-starter-web-services |
spring-boot-starter-webservices |
spring-boot-starter-aop |
spring-boot-starter-aspectj |
spring-boot-starter-oauth2-client |
spring-boot-starter-security-oauth2-client |
spring-boot-starter-oauth2-resource-server |
spring-boot-starter-security-oauth2-resource-server |
Packages moved to org.springframework.boot.<technology> as well, so expect an import sweep.
The one that actually hurts: some technologies now need a starter that used to come along for free — spring-boot-starter-flyway and spring-boot-starter-liquibase among them. A missing migration tool doesn't fail at compile time. It fails when the schema isn't there.
For the first pass, spring-boot-starter-classic and spring-boot-starter-test-classic restore the old groupings so you can get compiling with a small diff. Unpick them afterwards.
2. Jackson 3 is where the compile errors live
Coordinates changed: com.fasterxml.jackson → tools.jackson (except jackson-annotations, which keeps its old coordinates).
| Old | New |
|---|---|
@JsonComponent |
@JacksonComponent |
@JsonMixin |
@JacksonMixin |
JsonObjectSerializer |
ObjectValueSerializer |
Jackson2ObjectMapperBuilderCustomizer |
JsonMapperBuilderCustomizer |
The dangerous one isn't in that table. Defining an ObjectMapper bean no longer replaces the mapper — you need a JsonMapper (or XmlMapper) bean. Nothing fails. Your customisation is just ignored, and if you'd configured date formats or null handling, your API responses quietly change shape.
Properties moved too: spring.jackson.read.* and spring.jackson.write.* now live under spring.jackson.json.*.
Escape hatches: spring.jackson.use-jackson2-defaults, and a deprecated spring-boot-jackson2 module.
3. Your tests break before your app does
| Change | Fix |
|---|---|
@MockBean removed |
@MockitoBean |
@SpyBean removed |
@MockitoSpyBean |
MockitoTestExecutionListener removed |
Use Mockito's MockitoExtension if @Mock/@Captor stop working |
@SpringBootTest no longer provides MockMvc |
Add @AutoConfigureMockMvc
|
@SpringBootTest no longer provides TestRestTemplate/WebClient
|
Add @AutoConfigureTestRestTemplate or @AutoConfigureRestTestClient
|
Catch: @MockitoBean works on test class fields but not in @Configuration classes — which is exactly where a lot of codebases keep shared mocks. Move them to the test class; the annotation takes several types at once:
@MockitoBean(types = {OrderService.class, UserService.class})
4. JSpecify can fail a build on code you didn't touch
Boot 4.0 adopts JSpecify null-safety annotations. If you run a null checker or build Kotlin, previously unannotated types are now explicitly nullable or non-nullable, and the build can fail on untouched code.
Migrate off org.springframework.lang annotations, and on actuator endpoint parameters swap org.springframework.lang.Nullable for org.jspecify.annotations.Nullable.
Removed outright — check before you plan
- Undertow — starter and embedded support both gone (no Servlet 6.1 support). You're changing server, not just version.
- Spock integration — Spock doesn't support Groovy 5.
- Spring Session Hazelcast and Spring Session MongoDB.
- Embedded launch scripts for fully executable jars.
-
The classic uber-jar loader (drop
loaderImplementation). - Spring Retry dependency management — specify the version yourself.
- Reactive Pulsar auto-configuration.
Grep for renamed properties too: spring.dao.exceptiontranslation.enabled → spring.persistence.exceptiontranslation.enabled, management.tracing.enabled → management.tracing.export.enabled, spring.session.redis → spring.session.data.redis.
What you actually gain
-
API versioning, auto-configured —
spring.mvc.apiversion.*andspring.webflux.apiversion.*, withApiVersionResolver,ApiVersionParserandApiVersionDeprecationHandlerbeans for the hard cases. - HTTP Service Clients — annotate a plain Java interface, Spring generates the implementation.
-
spring-boot-starter-opentelemetryfor OTLP metrics and traces. -
RestTestClientin tests. - Spring Framework 7, Security 7, Data 2025.1 underneath.
A migration order that stays reviewable
- Latest 3.5.x, clear every deprecation warning. Ship it.
- Raise baselines — Java 21, Kotlin 2.2+. Ship it.
- Bump to 4.1 with
spring-boot-starter-classic, smallest possible diff. - Fix Jackson 3. Diff a real API response against a pre-migration capture.
- Fix the tests — mechanical renames.
- Unpick the classic starters, one at a time.
- Verify the quiet things: migrations ran, serialization unchanged, properties still read.
Six small commits you can bisect beat one branch touching four hundred files.
The summary: this isn't a version bump with some renames attached. It's a dependency-coordinates migration — Jackson and the test layer are the real work, and the failures that will cost you most are the silent ones, not the compile errors.
Check your version and the date it stopped getting patches. If it's 3.4 or earlier, you're a year past it already.
I write about backend engineering and payments at feezankhattak.com. The longer version of this post has the full gotchas list, and I build free in-browser developer tools — no sign-up, nothing uploaded.
Top comments (0)