This is the only lesson in the course that is not about CicloUrbana, and that is deliberate. The Ribalta network is built, tested, deployed, observed and reviewed; what is left is not another piece of the project but the ability to keep learning once there is no syllabus to follow.
And that has a technique of its own. The internet is full of material about Spring Boot, and a good part of it is out of date in a particularly treacherous way: a Spring Boot 2 tutorial does not look old — the code compiles almost the same — but it teaches javax.persistence instead of jakarta.persistence, WebSecurityConfigurerAdapter instead of SecurityFilterChain and Spring Cloud Sleuth instead of Micrometer Tracing. Knowing how to tell the difference is worth more than any list of links.
This lesson organises the resources that are worth your time, annotated and with judgement: which documentation to read and how, why the source code is the best source, how to keep up to date and plan a major version upgrade, which books are worth it and for whom, where the community is, how to practise properly, which topics are the natural next step from where you are now, and a reasoned roadmap for the next six months.
Contents
- Learning with judgement
- The official documentation and how to read it
- The source code as documentation
- Keeping up to date
- Planning a major version upgrade
- The Java ecosystem
- Books
- Certifications
- Community
- Deliberate practice
- Natural topics for the next step
- How to evaluate a resource
- A six-month roadmap
- Common Mistakes and Tips
- Exercises
- Learning with judgement
A warning before the list, because it decides whether the rest is any use.
The bottleneck is not access to information: it is attention. There is more content about Spring Boot than anyone could consume in a lifetime, and the natural impulse — bookmarking twenty links, subscribing to three newsletters, starting four courses — produces the feeling of learning without anything consolidating. The approach that does work has three traits:
| Trait | What it means | Frequent counter-example |
|---|---|---|
| With a problem in front of you | You learn what you need for something concrete | Watching a whole course "just in case" |
| Writing code | Typing, breaking and fixing | Reading about WebFlux without opening the IDE |
| Spaced out | Coming back to the topic days later | A weekend marathon forgotten within two |
And a hierarchy of sources worth internalising, from most to least reliable: the source code > the reference documentation > the official guides > books by recognised authors > conference talks > blog articles > forum answers > unreviewed generated content. It does not mean always starting at the top: it means that when two sources contradict each other, the one higher up wins.
- The official documentation and how to read it
| Resource | What it is | When to reach for it |
|---|---|---|
| Spring Boot Reference Documentation | The complete manual: autoconfiguration, properties, packaging, Actuator | The daily reference. Its common properties appendix is the canonical list of everything configurable |
| Spring Framework Reference | The core: container, AOP, transactions, MVC, validation | When the question is about the mechanism, not about Boot |
| Spring Data JPA Reference | Repositories, derived queries, projections, Specification, auditing |
When writing any non-trivial query |
| Spring Security Reference | Filter chain, authorisation, OAuth2, method security | Reorganised for Spring Security 6: the examples no longer use the retired adapter |
| spring.io guides | Short tutorials on one concrete task, maintained by the team | To get going with something new in half an hour |
| Release notes in the GitHub wiki | What changes in each minor version, with the breaking changes flagged | Before upgrading, always |
| Migration guides | The route from one major version to the next | When planning a major upgrade (section 5) |
| Javadoc | The exact contract of each class | When the reference says what it does but not with what precision |
How to read the reference documentation without getting lost. Three tactics that change the experience:
- Search it, do not read it top to bottom. It is written to be consulted. The section structure and the built-in search are designed to take you straight to the paragraph that answers your question.
- Start with the properties appendix. When you are unsure whether something is configurable, the answer is usually there, with the default value — which is half the useful information.
- Read the release notes for the version you use. It is half an hour well spent: you find out about features you have spent months reimplementing by hand.
How to read javadoc productively. It is not marketing documentation: it is the contract. What to look for in it are the three things the signature does not tell you: what happens at the edge cases (does it return null or an empty list? does it accept zero?), which exceptions it throws and when, and whether it is thread-safe. That last piece frequently appears in a single sentence of the class javadoc, and it is exactly the one you go looking for when something fails under load.
A concrete worked example, because the technique is better understood in action. Suppose you want to know whether you can limit the size of the async executor's queue and what happens when it fills up:
| Step | Where you look | What you get |
|---|---|---|
| 1 | The properties appendix, searching for task.execution |
pool.queue-capacity and pool.max-size exist, with their default values |
| 2 | TaskExecutionProperties in the source code |
The typed, complete list, with the defaults on the fields |
| 3 | The javadoc of ThreadPoolTaskExecutor |
That the queue fills before the pool grows, and which rejection policy kicks in afterwards |
| 4 | The "Task Execution and Scheduling" section of the reference | How Spring Boot wires it and when your bean replaces theirs |
Four lookups, five minutes, and an answer that does not depend on any article having got it right.
- The source code as documentation
Here is the tip with the best effort-to-benefit ratio in the whole lesson: read Spring's code. It is open, it is on GitHub, it is reasonably commented and it answers questions no documentation answers.
Why it is the best source. The documentation describes the intention; the code describes the behaviour. When something does not work as you expected, the difference between the two is exactly where your problem is. And unlike an article, the code is never out of date with respect to itself.
Three things worth learning to read:
The autoconfiguration classes. Searching for *AutoConfiguration in the spring-boot-autoconfigure tree and reading one all the way through — DataSourceAutoConfiguration, JacksonAutoConfiguration or WebMvcAutoConfiguration — dismantles the feeling of magic in one go. You see which conditions (@ConditionalOnClass, @ConditionalOnMissingBean) activate each bean, and therefore what you have to do to replace it: almost always, declare your own.
The *Properties classes. ServerProperties, JpaProperties or DataSourceProperties are the exact, typed list of what can be configured, with their default values on the fields. It is usually faster than searching the documentation.
The spring.factories and AutoConfiguration.imports files. The file META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports is literally the list of everything Spring Boot can autoconfigure. Reading it once gives you a sense of the framework's scope that nothing else does.
# Search in your own local Maven repository, without leaving the machine
find ~/.m2 -name "spring-boot-autoconfigure-*-sources.jar"
./mvnw dependency:sources # downloads the sources of every dependencyWith the sources downloaded, Ctrl+B on any annotation in the IDE takes you to its definition. That is the habit: when you do not understand why something behaves the way it does, go in.
- Keeping up to date
The release cycle and support
Spring Boot ships one minor version every six months (November and May, roughly) and bug-fix releases every month. What matters is not the calendar but the support policy:
| Type | What it includes | Typical duration |
|---|---|---|
| OSS support | Bug fixes and security fixes, free | ~12 months from the minor version's release |
| Commercial support | Security fixes beyond OSS, paid | Several additional years |
| End of life | Nothing: not even security patches | — |
The practical consequence is harsh and worth accepting: an application in production needs to move up a minor version at least once a year. It is not an optional improvement, it is security maintenance. A project that has stayed on the same minor version for three years is not "stable": it is accumulating known vulnerabilities with no patch.
The healthy strategy is to upgrade early and often: going from 3.3 to 3.4 as soon as it is out and there is time is a few hours' work; going from 2.7 to 3.x four years late is a project of weeks.
Where to find out
| Source | What it gives you | Frequency |
|---|---|---|
| The official Spring blog | Releases, security advisories and articles from the team | The primary source; subscribe |
| This Week in Spring | A weekly digest of the ecosystem from the developer relations team | Weekly, read in five minutes |
| Spring Office Hours | A regular session with the team, with real questions | Video, for the commute |
| InfoQ (Java) | Analysis with perspective, not just announcements | Monthly |
| GitHub release notes | The exact detail, with the breaking changes | Before every upgrade |
| Spring security advisories | The CVEs that affect you | A mandatory subscription on a real project |
The last row is not negotiable: a known vulnerability in such a widespread framework is exploited en masse within hours of being published.
How to read release notes in ten minutes. They always have the same structure and it is worth walking it in this order: first "Breaking Changes", which is the only part that can break you and usually takes up half a screen; then "Deprecations", so you know what you have to start changing even though it still works today; then "Dependency Upgrades", where you see which versions of Hibernate, Jackson or Tomcat come inside — and whether any of them affects you directly; and finally "New and Noteworthy", which is the fun part and the least urgent. Reading them backwards, starting with the new features, is why so many people get caught out.
- Planning a major version upgrade
Moving from one major version to the next — the paradigmatic case being Spring Boot 2.7 → 3.0, with the jump from javax to jakarta — is not a change of number. This is the procedure that works.
flowchart LR
A["0. Test suite<br/>green"] --> B["1. Read the<br/>migration guide"]
B --> C["2. properties-migrator:<br/>fix the YAML"]
C --> D["3. OpenRewrite:<br/>the mechanical part"]
D --> E["4. One minor version<br/>at a time"]
E --> F["5. Review what the<br/>BOM does not manage"]
F --> G["6. Deploy to pre<br/>and measure"]
E -->|"suite red"| E
Step 0: have tests. Without the module 6 suite, a major upgrade is a leap into the void. If the project does not have them, writing them is the first step of the migration, not a separate task.
Step 1: read the migration guide in full, before touching anything. It is in the Spring Boot repository wiki and lists the breaking changes one by one. Half an hour of reading saves days.
Step 2: spring-boot-properties-migrator. A temporary dependency that, at startup, reports the renamed or removed properties and temporarily applies the equivalences:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>You start up, read the report in the log, fix the YAML and remove the dependency. Leaving it in is a classic mistake: it masks the problems instead of solving them.
Step 3: OpenRewrite for the mechanical part. The repetitive migrations — javax.* to jakarta.*, retired annotations, renamed APIs — are done by an automatic recipe:
./mvnw org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3It is a sweeping change, so it goes in a commit of its own, with nothing else mixed in, and is reviewed by reading the diff. OpenRewrite does 80% of the mechanical work; the remaining 20% — the part that requires judgement — is still yours.
Step 4: go up one minor version at a time. You do not jump from 2.5 to 3.3: you go 2.5 → 2.6 → 2.7 → 3.0 → …, with the suite green at every rung. If something breaks, you know exactly which rung broke it.
Step 5: review what the BOM does not manage. The dependencies with their own <version> — in CicloUrbana, JJWT, ShedLock, MapStruct, Resilience4j — are not upgraded by Spring Boot. They are the ones left behind and the ones that produce surprises.
Step 6: deploy to pre and measure. A major upgrade can change performance in either direction. The module 9 k6 baseline exists for exactly this.
- The Java ecosystem
Spring Boot lives on top of Java, and for some years now Java has been moving fast.
LTS versions and what they bring. The extended-support versions come out every two years, and they are the ones used in production:
| Version | Most relevant additions for a Spring application |
|---|---|
| Java 17 | record, sealed, pattern matching in instanceof, text blocks, switch as an expression |
| Java 21 | Virtual threads, pattern matching in switch, record patterns, sequenced collections |
| Java 25 | Consolidation of the above, garbage collector and startup improvements |
Of all the above, what CicloUrbana already uses daily are the records for the DTOs, the text blocks in the JPQL queries, switch as an expression and virtual threads.
Project Loom and virtual threads. It is the most important conceptual change in Java in the last decade: a thread that, when it blocks on I/O, releases the operating system thread instead of occupying it. For a blocking application with a lot of database waiting — which is the vast majority of business applications — it enables high concurrency with no reactive stack.
// Java 21: a million waiting threads, without exhausting the operating system
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i ->
executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }));
} // try-with-resources waits for them all to finish: no shutdown() neededThat fragment with platform threads would exhaust memory long before reaching a million. It is worth understanding thoroughly, including its two traps: pinning with synchronized, and the fact that the real limit becomes the connection pool, as we saw in 09-01.
GraalVM and native images. Compiling the application to a native executable reduces startup from seconds to milliseconds and memory to a fraction. In exchange, compilation takes minutes, reflection needs explicit RuntimeHints and sustained throughput may be somewhat lower than on the JVM. Its clear case is serverless functions and short-lived processes; for a service that starts once a day, it contributes little.
And an underlying idea: Spring Boot is the most visible part of your work, but the part that transfers most is Java. A good grasp of the language, the JVM and concurrency outlives any framework.
- Books
Only widely recognised works, with who each one is for. A book is read in weeks and consulted for years; it is the format with the best long-term return.
| Book | Author | Who it is for and what for |
|---|---|---|
| Spring in Action | Craig Walls | A broad, practical tour of Spring; a good complement to this course, especially for the areas we have not touched |
| Spring Boot: Up and Running | Mark Heckler | A direct focus on Boot, with good explanations of autoconfiguration |
| Effective Java | Joshua Bloch | The book that most improves your Java. Ninety items on API design, generics, equals, immutability and concurrency. If you read only one, this |
| Clean Code | Robert C. Martin | Names, functions and comments; the basis of module 10, though it should be read with your own judgement and not as dogma |
| Refactoring (2nd ed.) | Martin Fowler | The catalogue of safe transformations; used as a reference, not read cover to cover |
| Patterns of Enterprise Application Architecture | Martin Fowler | Explains where the repository, the data mapper, the unit of work and the anaemic model come from |
| Domain-Driven Design | Eric Evans | The original on ubiquitous language, aggregates and bounded contexts. Dense; many people start with the next one |
| Implementing Domain-Driven Design | Vaughn Vernon | The applicable version of DDD, with code |
| Growing Object-Oriented Software, Guided by Tests | Freeman and Pryce | How tests drive the design; the best book on the "why" of TDD |
| Unit Testing: Principles, Practices, and Patterns | Vladimir Khorikov | What to test and what not to, and why verifying interactions produces brittle tests |
| High-Performance Java Persistence | Vlad Mihalcea | The serious reference on JPA and Hibernate: N+1, identifiers, locks, batching, caching |
| Designing Data-Intensive Applications | Martin Kleppmann | Replication, partitioning, consistency and distributed transactions. Essential before splitting a system |
| Building Microservices (2nd ed.) | Sam Newman | Honest about the costs; the book to read before deciding to split |
| Site Reliability Engineering | Various (Google) | SLIs, SLOs, error budgets and on-call. Free online |
| Observability Engineering | Majors, Fong-Jones and Miranda | The conceptual framework behind module 9 |
| Accelerate | Forsgren, Humble and Kim | The research behind module 8's DORA metrics |
| The Pragmatic Programmer | Hunt and Thomas | Craft and attitude, beyond any technology |
If you had to pick three for the next six months, starting from where you are: Effective Java (it makes you a better Java programmer, not a better Spring user), High-Performance Java Persistence (because the database is where the time is) and Designing Data-Intensive Applications (because it is the one that widens the view beyond a single application).
- Certifications
The ecosystem's reference certification is the VMware Spring Professional, which covers the container, AOP, data, MVC, security, Boot and testing: roughly the syllabus of this course.
An honest assessment:
| In favour | Against |
|---|---|
| It gives structure and a deadline, which organises study for many people | It costs money and several weeks of preparation |
| It forces you to cover areas you would otherwise avoid | It rewards memorising details you could look up in a second |
| In some markets and consultancies, it filters CVs | Nobody with experience hires on the basis of a certification |
| The syllabus is well chosen | It expires: the ecosystem moves faster than the exam |
When it is worth it, concretely: if you work in a consultancy where it influences project assignment or your rate; if your employer pays for it and gives you the time; or if you need external structure to study consistently. When it is not: if the goal is to demonstrate that you know your stuff. For that, a well-made public project — with tests, a pipeline and a decent README — says far more, and it is what actually gets looked at in a technical interview.
There are also adjacent certifications that in many contexts are worth more than the Spring one: the Kubernetes ones (CKA and CKAD) and the cloud providers', because they cover an area where demand clearly outstrips supply and where the exam is practical, with a real terminal, rather than a battery of questions. If the goal is employability and you already know Spring Boot, that direction is usually the better investment.
- Community
| Place | What it is for | How to get the most from it |
|---|---|---|
Stack Overflow (tags spring-boot, spring-data-jpa) |
Concrete questions already solved | Check the date and the version of the answers; and sort by votes, not by acceptance |
| The Spring issue tracker (GitHub) | Finding out whether "that weird thing" is a known bug | Search the literal error message before asking anywhere |
| The projects' GitHub Discussions | Design questions answered by the team | Hugely underused |
| SpringOne | The ecosystem's conference; talks from the team | The recordings are free |
| Devoxx, Codemotion, JavaZone | Java and the ecosystem in general | Devoxx publishes almost everything openly |
| Local Java User Groups (JUGs) | Talks and contacts in your city | The real value is in the conversation afterwards, not in the talk |
And the advice about asking, which applies in any of these places: a good question includes the Spring Boot version, a minimal example that reproduces the problem, the complete error message and what you have already tried. Preparing that question solves the problem by itself one time in three, and the phenomenon has a name — rubber duck debugging: being forced to explain the problem precisely makes you see the gap in your own reasoning.
On the issue tracker, which deserves a paragraph of its own because almost nobody uses it: when Spring does something you do not understand, there is a high probability that somebody has already reported it, and that a team member has explained why it works that way. Those answers are frequently better than the documentation, because they answer the exact question you have. Searching the literal error message in quotes is the first move, before writing on any forum.
- Deliberate practice
Reading about programming does not teach you to program, just as reading about swimming does not teach you to swim. Four ways of practising that do work:
Your own projects of increasing difficulty. The usual mistake is starting with "a social network": too big, abandoned in week three. A progression that works:
| Level | Project | What it forces you to solve |
|---|---|---|
| 1 | A CRUD with authentication and tests, deployed | The whole flow, end to end. It sounds like little and it is not |
| 2 | Add an external integration with a circuit breaker and a cache | Partial failures, timeouts, invalidation |
| 3 | Add background work and notifications | Concurrency, idempotency, delivery |
| 4 | Put it through a load test and optimise it up to an SLO | Measuring, profiling, deciding |
| 5 | Extract a part into a separate service, with messaging | Eventual consistency, contracts, tracing |
The seven extensions in 10-04 cover exactly that progression on a project you already know.
Contributing to an open source project. Start with documentation or with an issue labelled good first issue. What you learn is not so much the code as the process: demanding reviews, mandatory tests, design discussions in the open. It is the cheapest way to work with people better than you.
Katas and short exercises. Repeating a small problem while concentrating on one thing at a time — this time with TDD, this time with no if, this time with perfect names — is the closest thing to practising scales. Half an hour a week.
Reading other people's code. The most undervalued habit. Pick a well-regarded Spring Boot project on GitHub and read it the way you read a book: the package structure, one service, its tests, its pipeline. You are going to find decisions different from this course's, and understanding why they took them is more instructive than agreeing.
And a warning about the first three: practice without feedback is not deliberate practice, it is repetition. Writing code nobody reviews consolidates the mistakes as firmly as the successes. The three available sources of feedback, from most to least accessible, are: the tests, which tell you whether it works; the tools from 10-03 — ArchUnit, SpotBugs, static analysis — which tell you whether it is well built; and a person, who is the only one who tells you whether it is well thought out. If you work alone, contributing to an open source project is the cheapest way to get the third.
- Natural topics for the next step
All of these start from something you already know. The third column is the one that matters: it says which lesson left you prepared.
| Topic | What it is | What in the course prepares you |
|---|---|---|
| Spring Modulith | Modules with verified boundaries inside the monolith, with events and per-module tests | The packages-by-feature from 01-04 and the ArchUnit rules from 10-03 |
| Messaging with Kafka or RabbitMQ | Durable asynchronous communication between systems | The limits of @Async in 07-03 and the events from 04-07 |
| Spring Batch | Batch processing with restart, retries and chunking | The scheduled tasks from 07-03 and the TransactionTemplate importer from 04-07 |
| WebFlux and R2DBC | The non-blocking reactive stack | The threading model from 09-01. With a caveat: Java 21's virtual threads now cover a good part of its use case with far less complexity |
| Hexagonal architecture | Ports and adapters; the domain at the centre | The dependency rule from 10-01 and the framework-free domain from 10-03 |
| Tactical DDD | Aggregates, value objects, domain events, repositories | The anaemic model discussion from 10-03 and the events from 02-02 |
| Kubernetes in depth | Operators, service meshes, network policies, GitOps | The manifests and Helm from 08-04 |
| Spring AI | Integration with language models from Spring | The fault-tolerant external client pattern from 07-06 |
| OAuth2 and OpenID Connect | Delegating identity to a provider | The home-made JWT from 05-04, which is the artisanal version of the same problem |
Which to choose first. Not the one that sounds best, but the one that solves a problem you have. If your application loses work when it restarts, messaging. If two teams tread on each other in the same code, Spring Modulith. If the domain is dissolving into your services, tactical DDD. And if nothing hurts yet, the order that best consolidates what you have learnt is Spring Modulith → messaging → tactical DDD.
- How to evaluate a resource
Three questions, in this order, before investing time in an article, a video or a course:
1. When is it from? If there is no visible date, be suspicious. An undated article about a framework that ships two versions a year is not a resource, it is a risk.
2. Which Spring Boot version does it use? This is the decisive question, and it is almost always answered by glancing at the code for ten seconds:
| Signal | What it indicates |
|---|---|
javax.persistence, javax.servlet, javax.validation |
Spring Boot 2: the move to Jakarta EE 9+ renamed all those packages in Boot 3 |
WebSecurityConfigurerAdapter |
Spring Security 5: retired, replaced by SecurityFilterChain |
@EnableGlobalMethodSecurity |
Likewise: today it is @EnableMethodSecurity |
spring-cloud-starter-sleuth |
Discontinued: today it is Micrometer Tracing |
WebMvcConfigurerAdapter, @MockBean |
A retired adapter; @MockBean replaced by @MockitoBean in Boot 3.4+ |
RestTemplate as the recommendation |
It is not retired, but since Boot 3.2 the default choice is RestClient |
application.properties with spring.datasource.initialize |
Boot 1.x properties |
Why a Spring Boot 2 tutorial confuses more than it helps. It is not that it is "a bit dated": it is that the code does not compile in a Boot 3 project — the packages of every persistence, validation and servlet annotation change — and, worse, the conceptual explanation still sounds plausible. A novice reader cannot tell "this no longer exists" from "I typed this wrong", and loses hours. With the table above, those ten seconds of checking save you the afternoon.
3. Who signed it and what is at stake for them? An article from the Spring team, from an author with a track record or from a peer-reviewed project has an incentive to be right. Content optimised for search rankings does not.
And a fourth criterion that applies above all to automatically generated content, which is increasingly abundant: if the code cannot be run as it stands, be suspicious. Examples that mix versions, invent methods that do not exist or call APIs from two different eras are the characteristic signature.
- A six-month roadmap
A concrete, reasoned proposal for whoever finishes this course and wants to consolidate rather than scatter. The principle that orders it: each month has one deliverable, and each deliverable builds on the previous one.
| Month | Focus | Concrete deliverable |
|---|---|---|
| 1 | Consolidate what you learnt. Nothing new | Implement two extensions from 10-04: the operator panel and the reports with export. With tests and deployed |
| 2 | Java, not Spring. Effective Java | Refactor the project applying ten concrete items from the book; write down what improved and what did not |
| 3 | Persistence for real. High-Performance Java Persistence | A load test with realistic data, EXPLAIN ANALYZE on the five most expensive queries, indexes and a measured p95 improvement |
| 4 | Messaging. Kafka or RabbitMQ with Spring | Extract the notifications into a message consumer, with the outbox pattern, idempotency and traces that cross the boundary |
| 5 | Design. Spring Modulith and tactical DDD | Reorganise the project into modules with verified boundaries and events between them; move into the domain the rules that today live in services |
| 6 | Real production. SRE and observability | Define SLOs with an error budget, alerts on symptoms, a dashboard and a timed incident drill |
Why this order and not another. Month 1 deliberately learns nothing new: what you have just seen consolidates by being used, not by piling more topics on top. Month 2 is Java and not Spring because it is what transfers most and expires least. Month 3 attacks where the time really is. Months 4 and 5 are the two conceptual leaps — asynchronous communication and domain design — and they come after the base is solid. And month 6 closes the cycle, because operating what you build is what turns a programmer into an engineer.
How to make it realistic. Four or five hours a week, with a visible deliverable each month. A twenty-hours-a-week plan does not get followed and produces guilt; a five-hour one sustained for six months produces real change. And if some month does not work out, you do not skip it: you delay it. The sequence matters more than the calendar.
And if your situation is different, adapt it with this criterion instead of copying it. If you are job-hunting, move month 6 to the start: a deployed, observable project is what you show in an interview. If your team is going to split the monolith next quarter, bring month 4 forward and add Designing Data-Intensive Applications. If you have just joined a legacy project, months 1 and 2 are replaced by writing characterisation tests and upgrading the version, which is what unblocks everything else. The right roadmap is the one that attacks the problem you have today, not the one that covers the most topics.
Common Mistakes and Tips
Collecting resources instead of using them. Fifty open tabs and three started courses produce the feeling of learning and no learning. One resource at a time, with a problem in front of you.
Following a tutorial without checking the version. It is the number one cause of hours lost with Spring. Ten seconds looking at whether it says javax or jakarta save them for you.
Learning the new before mastering the current. WebFlux, GraalVM or Spring AI are interesting; if you are not yet clear on how the @Transactional proxy works, they are not your next step.
Copying configuration without understanding it. An application.yml copied from a blog brings properties you do not need, some of them dangerous — ddl-auto: update, an open Actuator, show-sql on — and no explanation at all.
Confusing "I have read about this" with "I know how to do this". The test is simple and merciless: open the IDE and do it without looking.
Accepting an assistant's suggested code without verifying it. Language models are trained on everything that has been published, and what has been published about Spring Boot is mostly from the 2.x era: you will see WebSecurityConfigurerAdapter, javax.persistence and @MockBean delivered with absolute confidence. They are excellent tools for exploring and for writing the repetitive parts, and exactly the same criteria from section 12 apply: check the version, run it and do not paste it if you do not understand it.
Only studying what you already enjoy. It is comfortable to go deeper into what you are good at and avoid what feels foreign — for many programmers, operations and the database. And that is precisely where the biggest gap between what you know and what the project needs usually sits.
Tip: write down what you learn. An article, an internal note or a docs/decisions/ file in your project. Explaining something forces you to genuinely understand it, and it uncovers the gaps that passive reading hides.
Tip: keep a living project. A repository of your own that you come back to every few weeks is worth more than ten courses. It is where you try each new thing and where you check whether it works in your hands and not only in the author's.
Tip: learn to read code before writing it. You will spend most of your career reading: your team's code, a framework's, that of somebody who left three years ago. It is a skill that can be trained, and almost nobody trains it on purpose.
Tip: when something surprises you, stop and go in. That instant of "oh, I didn't know it did that" is the best learning signal available, and it is wasted almost every time because you are in a hurry. Ten minutes reading the class that caused it are worth more than two hours of a course on something that has never surprised you.
Tip: the best way to study a new topic is to write the smallest possible example. Not a project: a Maven module with three classes that does one thing. A Kafka consumer that prints messages. An @Observed that produces a span. It is quick to write, quick to throw away, and it answers questions no article answers.
Exercises
Exercise 1: evaluate three resources
Search for three articles or tutorials about "Spring Boot JWT authentication" in any search engine. For each one, apply the criteria from section 12 and write a card with: the date, the Spring Boot version you deduced and from which concrete signal you deduced it, whether the code would compile today, three things you would do differently based on module 5, and your final verdict. It is very likely at least one is out of date; the exercise is to spot it in under a minute.
Exercise 2: read an autoconfiguration class
Download the sources with ./mvnw dependency:sources and open DataSourceAutoConfiguration (or JacksonAutoConfiguration, if you prefer something shorter). Answer: which conditions must be met for it to activate? which beans does it declare? which annotation makes your own bean win over theirs? which *Properties class does it read its configuration from and what defaults does it have? And finally: what would you have to do to replace its DataSource entirely with one of your own?
Exercise 3: your own roadmap
Adapt the roadmap from section 13 to your real situation. Start from three questions: what problem do you have today at work or in your project that Spring Boot has not solved for you yet? how many hours a week can you genuinely sustain for six months? and what visible deliverable would mark each month? Write the result with one deliverable per month and an objective criterion for knowing whether you have met it.
Solutions
Solution 1
An example of a well-made card, on a very common case:
Article A — No visible date (first alarm signal; the blog footer says 2021). Deduced version: Spring Boot 2.5. The signals, in order of conclusiveness: it extends
WebSecurityConfigurerAdapterand overridesconfigure(HttpSecurity), retired in Spring Security 5.7; it importsjavax.servlet.FilterChain; it usesantMatchers(...), replaced byrequestMatchers(...); andio.jsonwebtoken0.9.1, whoseJwts.parser().setSigningKey(String)API no longer exists in 0.12. Would it compile today? No. It would fail on thejavax.*imports and on the retired base class, and would not even reach the JJWT API errors. Three things I would do differently based on module 5: (1) aSecurityFilterChainbean instead of the adapter, withauthorizeHttpRequestsending inanyRequest().denyAll(); (2) the secret from an environment variable and 256 bits long, not a"secret"constant in the code; (3) a fifteen-minute access token with rotating refresh, instead of the article's ten hours. Verdict: discard. Not because it is old, but because the mistake it teaches is a security one, which is the worst category to copy without understanding.
The three signals that settle 90% of cases in ten seconds: javax versus jakarta, WebSecurityConfigurerAdapter, and spring-cloud-starter-sleuth. If any of the three appears, the resource is from the Spring Boot 2 era.
And the nuance that keeps you from being unfair: an old resource can still be conceptually correct. The explanation of what a JWT is, why it is signed and why its payload is readable has not changed. What you cannot copy is the code.
Solution 2
On DataSourceAutoConfiguration, the answers and — more importantly — what each one teaches:
Which conditions activate it? @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}), that is, only if those classes are on the classpath. Hence the rule that explains all of autoconfiguration: adding a data starter does not magically "switch" anything on; it simply puts classes on the classpath and the conditions are met.
Which beans does it declare? A DataSource, resolved in nested configurations: if it detects HikariCP it uses it (@ConditionalOnClass(HikariDataSource.class)), and if not it tries Tomcat JDBC or DBCP2, in that order. It also declares DataSourceInitializer and the properties beans.
What makes your bean win? @ConditionalOnMissingBean. It is the key annotation of the whole of Spring Boot: autoconfiguration only acts if you have not decided anything. Declaring your own @Bean DataSource disables it without needing to exclude anything.
Where does it read from? From DataSourceProperties (prefix spring.datasource), which holds url, username, password, driverClassName, generate-unique-name and the rest, with their default values written on the fields — which is faster to consult than the documentation.
How would you replace it entirely? Three ways, from best to worst: declare your own @Bean DataSource, which wins thanks to @ConditionalOnMissingBean; exclude it with @SpringBootApplication(exclude = DataSourceAutoConfiguration.class); or remove the dependency, so the class condition stops being met.
What the exercise really teaches is not this class but the pattern: @ConditionalOnClass + @ConditionalOnMissingBean + a *Properties class. With that you understand the remaining two hundred autoconfigurations, including the one for the custom starter we wrote in 02-06. And it confirms the claim in section 3: when you read the code, the magic disappears.
Solution 3
There is no single solution, but there is a recognisable way of having done the exercise well. An example:
Situation: I work in a team of four with a Spring Boot 2.7 monolith nobody dares upgrade. I can sustain four hours a week. The real problem is not technical: it is that there are no tests, and that is why nobody touches anything.
Month Focus Deliverable Objective criterion 1 Characterisation tests for the three critical flows A suite that runs in CI ./mvnw verifygreen in the pipeline, not on my laptop2 Upgrade to Spring Boot 3, rung by rung A branch with 2.7 → 3.0 → 3.3The month 1 suite green at every rung 3 Effective Java + refactoring one module A clean billing module Ten items applied, documented in docs/decisions/4 Performance: k6, EXPLAIN ANALYZE, indexesA baseline and a measured improvement The worst endpoint's p95 below 300 ms 5 Observability: metrics, logs and traces A dashboard and two alerts Provoke an incident and diagnose it in under ten minutes 6 Spring Modulith Boundaries between billing and orders ArchUnit rules that fail if somebody crosses them
What makes this roadmap a good one: month 1 is not "learn something", it is removing the real blocker — without tests nothing else can be done; every month has an objective, verifiable criterion, not "understand X"; the order respects the dependencies between topics; and the hours are the ones the person can genuinely sustain, not the ones they would like.
The typical mistake in this exercise is writing a list of technologies that sound good — Kafka, Kubernetes, WebFlux, GraalVM — with no problem behind them. A roadmap with no problem to solve gets abandoned in week five.
And a final check worth doing: if in six months you completed the whole plan, what would you know how to do that you do not today? If the answer can be phrased with verbs — "upgrade a legacy project without fear", "diagnose a high p99 in ten minutes" — the plan is good. If it can only be phrased with nouns — "Kafka", "Kubernetes" — it is still a list of topics and not a roadmap.
Conclusion
The course ends here, and it is worth looking at the whole route before closing it.
We started in 01-03 with a nine-line @RestController that returned four hand-written stations and a curl that answered on localhost:8080. We finish with CicloUrbana: an application with a versioned, documented REST contract, with its DTOs, its validation and its errors in RFC 7807; with persistence on PostgreSQL governed by nine Flyway migrations and transactions that guarantee no Ribalta bike is ever left half locked; with JWT authentication, a role hierarchy and security rules that depend on the data and not only on the route; with a test suite that runs from the one-millisecond unit test to the complete flow against a real, ephemeral PostgreSQL; with scheduled tasks coordinated across replicas and asynchronous work that does not block the citizen; packaged in a multi-stage image, deployed to Kubernetes by a pipeline that builds it, scans it and promotes it on its own; and observed end to end, where the metric detects, the trace locates and the log explains, joined by a single traceId. And with the judgement, at the end, to know why it is built that way and where each rule would have to be broken.
What matters is that almost none of that is specific to Spring Boot. Inversion of control, the separation between domain and contract, the atomicity of a use case, deny by default, the testing pyramid, measuring before optimising, percentiles instead of the mean, backward-compatible migrations, one artefact for every environment, secrets out of the repository, structured and correlated logs, technical debt recorded with its cost: all of that travels with you to any framework, any language and any team. Spring Boot has been the vehicle; what you have learnt to drive is bigger than that.
There are things left to know, and there always will be. It is the uncomfortable part of this craft and also the best: nobody ever finishes learning it, and the difference between somebody with two years' experience and somebody with fifteen is not the number of frameworks they know, but the quality of the questions they ask before deciding. This course has tried, above all, to teach you those questions: what problem does this solve? what does it cost me? how will I know it works? what happens when it fails? will the next person who reads it understand it?
Now it is your turn. Take CicloUrbana somewhere: add the dynamic fares, extract the billing, connect it to a map. Or start something of your own, smaller and more real, and do it properly from beginning to end. What does not work is waiting until you know everything before you build: you learn by building, measuring what you build and fixing what breaks.
The Ribalta network is up and running. Thank you for making it this far, and safe travels.
Spring Boot Course
Module 1: Introduction to Spring Boot
- What Is Spring Boot?
- Setting Up Your Development Environment
- Building Your First Spring Boot Application
- Understanding the Project Structure
- Application Startup and Lifecycle
Module 2: Spring Boot Core Concepts
- Spring Boot Annotations
- Dependency Injection in Spring Boot
- Bean Scope and Lifecycle
- Spring Boot Configuration
- Spring Boot Properties
- Auto-Configuration and Starters from the Inside
Module 3: Building RESTful Web Services
- Introduction to RESTful Web Services
- Creating REST Controllers
- Handling HTTP Methods
- Validating Input Data
- DTOs and Mapping Between Layers
- Exception Handling in REST
- Documenting the API with OpenAPI
Module 4: Data Access with Spring Boot
- Introduction to Spring Data JPA
- Configuring Data Sources
- Creating JPA Entities
- Relationships Between Entities
- Using Spring Data Repositories
- Query Methods in Spring Data JPA
- Transactions and Persistence Management
- Schema Migrations with Flyway
Module 5: Security in Spring Boot
- Introduction to Spring Security
- Configuring Spring Security
- User Authentication and Authorization
- Implementing JWT Authentication
- Method-Level Security and API Hardening
Module 6: Testing in Spring Boot
- Introduction to Testing
- Unit Testing with JUnit
- Mocking with Mockito
- Integration Testing
- Testing with Testcontainers
Module 7: Advanced Spring Boot Features
- Spring Boot Actuator
- Spring Boot Profiles
- Scheduled Tasks and Asynchronous Execution
- Spring Boot with Docker
- Spring Boot and Microservices
- Service Communication and Fault Tolerance
Module 8: Deploying Spring Boot Applications
- Introduction to Deployment
- Deploying to Heroku
- Deploying to AWS
- Deploying to Kubernetes
- Continuous Integration and Delivery
Module 9: Performance and Monitoring
- Performance Tuning
- Caching with Spring Cache
- Monitoring with Spring Boot Actuator
- Using Prometheus and Grafana
- Logging and Log Management
- Distributed Tracing
