Skip to content
article

Spring Boot 2.7 to 3.3 on a European bank project: what actually broke (it was not just javax)

6 weeks, 41 modules, 1 very nervous client, and a checklist you can steal

Omkar Joshi@omkar_java

September 7, 2026 · 3 min read

Our client, a mid-size bank in Europe, got a finding from their security team: Spring Boot 2.7, open source support ended in November 2023, upgrade. So our team of 7 in Pune got the job. 41 Maven modules, Oracle database, a partner integration in Frankfurt, and a change advisory board that meets once a week. Here is what broke, in the order we found it.

0. Java 17 first, separately

Boot 3 needs Java 17. We moved from 11 to 17 while still on Boot 2.7, as its own release. If something breaks you know whether it was the JDK or Spring. One variable at a time, bhau.

1. javax to jakarta

The famous one, and honestly the easiest, because OpenRewrite does it for you:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \
  -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3

It rewrote imports, properties and dependency versions across all modules. Review the diff, but it saved us maybe two weeks.

2. Spring Security 6

WebSecurityConfigurerAdapter is gone. antMatchers is gone. Everything is a SecurityFilterChain bean with the lambda DSL:

@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
    return http
        .securityMatcher("/api/**")
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/health").permitAll()
            .requestMatchers(HttpMethod.POST, "/api/payments/**").hasRole("PAYMENTS")
            .anyRequest().authenticated())
        .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
        .csrf(csrf -> csrf.disable())
        .build();
}

We had 6 security configs across modules. Rewriting them was easy. Proving to the bank's security team that the new rules were identical to the old ones took longer than the code. We wrote a test per endpoint per role. Worth it.

3. Trailing slash, the one that reached production

Spring Framework 6 no longer matches /api/accounts/ to a mapping for /api/accounts. Trailing slash matching is off by default. Our tests all called the URLs without a slash. The partner in Frankfurt called them with a slash. First morning after go-live, 404s. We added explicit mappings for the partner routes the same day and a redirect filter later. Read the migration guide fully, not only the first page. Lesson learnt the hard way.

4. Hibernate 6 sequences, the one we caught in UAT

With GenerationType.AUTO, Hibernate 6 expects a sequence per entity (account_seq and so on) instead of the single hibernate_sequence. On Oracle: ORA-02289: sequence does not exist on every insert. One property brings back the old behaviour while you plan a proper fix:

spring.jpa.properties.hibernate.id.db_structure_naming_strategy=legacy

Also check allocationSize against the INCREMENT BY of your existing sequences. A mismatch there gives you duplicate keys or startup errors depending on the version.

5. Tracing

Spring Cloud Sleuth does not support Boot 3. Micrometer Tracing replaces it. The log correlation fields changed format and the bank's Splunk dashboards went blank. Nobody's code broke, but the ops team's mood did. Tell your ops team before, not after.

6. The helper nobody uses

Add spring-boot-properties-migrator as a runtime dependency during the upgrade. On startup it prints every property that was renamed or removed. Remove it again after, people forget it in production.

Numbers

6 weeks, 41 modules, 1 production issue (trailing slash, fixed same day), 1 UAT issue (sequences). The client sent a thank you mail to our onsite coordinator, which in service company terms is basically a national award.

And no, this is not "just a config change". It is a config change in 41 modules with a CAB, a security sign-off and a partner in another country. Koi doubt ho toh comment karo, as Rohit bhai says.

Written by

Omkar Joshi

Java/Spring dev, 6 yrs, service company, Pune. Yes I write real code. Yes it goes to production. Yes for a bank in Europe. No I will not apologise.

Get the best of #legacy-code-archaeology weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

11

Sign in to join the conversation.

Sign in to comment
Ankit Khurana@faang_or_bust#5 in Legacy Code Archaeology
6 weeks for a version bump lol. at my company this is one sprint for one guy. service company velocity
3 net votes
Omkar Joshi@omkar_java#1 in Legacy Code Archaeology
41 modules, a bank's change advisory board, a partner integration in Frankfurt, and UAT sign-off from people who take 3 days to answer an email. tell me which sprint your one guy does the CAB meeting in. I'll wait bhau
6 net votes
Ankit Khurana@faang_or_bust#5 in Legacy Code Archaeology
cope. process is a skill issue too
2 net votes
Rohit Verma@rohit_codes#7 in Legacy Code Archaeology
dekho bhai, process skill issue nahi hai, bank ka paisa hai. ek galat deploy aur kisi ki salary credit nahi hoti. chill karo 🙏
2 net votes
Kunal Bhatia@kunal_dsa#7 in Legacy Code Archaeology
bhai you have never seen a change freeze. product company mein deploy button dabate ho, yahan ek release ke liye 4 signatures chahiye. respect the guy
3 net votes
Davide Rinaldi@davide_actually#7 in Legacy Code Archaeology
The trailing slash!! Same for us in Turin, a client integration broke because nobody read the migration guide until the end. Spring Boot upgrades are my personal hell, anyway good post
3 net votes
Rodel Bautista@rodel_java#7 in Legacy Code Archaeology
The Hibernate sequence one bit us on Oracle too. db_structure_naming_strategy=legacy is the line. Saved this for our next upgrade.
4 net votes
Agnieszka Zielińska@agnieszka_z#7 in Legacy Code Archaeology
properties-migrator is underrated. And yes, remove it after, I found it in production at two companies.
4 net votes
Andrei Mureșan@andrei_cluj#7 in Legacy Code Archaeology
German client forced this upgrade on us also. One more for the list: RestTemplate with Apache HttpClient moves to HttpClient 5 and the timeout API changed. Our timeouts silently became infinite. Found it in a load test, it is a pity nobody writes this down.
2 net votes
Meenal Deshpande@meenal_restarts#7 in Legacy Code Archaeology
Omkar, I was at a service company till 2018, on a Spring 4 project with XML config. Reading this I understood maybe 60%. Kindly suggest, should I learn Boot 3 directly or start from the basics again? Thank you.
3 net votes
Omkar Joshi@omkar_java#1 in Legacy Code Archaeology
Boot 3 directly, Meenal. The core hasn't changed: DI, beans, configuration. Skip XML entirely. Build one small REST + JPA project on Java 21 and you will be surprised how much you remember. Ping me anytime.
0 net votes