Questions a senior Spring Boot candidate should be able to answer, every one drawn from a real decision or a real bug in the pizza codebase this track is built on rather than from a list of definitions.
The pattern in most of them: the definition is the easy half, and the interviewer is listening for the trade-off.
The container
1. What does @SpringBootApplication actually do?
It combines @SpringBootConfiguration, @EnableAutoConfiguration and
@ComponentScan.
The part worth saying out loud: component scanning starts at that class's package and goes down. A bean in a sibling package is invisible, and the error is "required a bean of type X that could not be found" — which sends you looking for a missing annotation rather than a misplaced package. Keeping the application class in the root package makes the problem impossible.
2. How does auto-configuration decide what to create?
Boot reads candidate configuration classes from its jars and evaluates each one's conditions:
@ConditionalOnClass, @ConditionalOnProperty,
@ConditionalOnMissingBean.
@ConditionalOnMissingBean is the philosophy — auto-configuration is a
default that steps aside the moment you define your own bean. You never disable Boot to take control;
you just define the bean.
Follow-up: --debug prints the condition evaluation report, which answers both "why is
this bean here" and "why is it missing".
3. Why is constructor injection preferred?
Three reasons: fields can be final so the object is never half-built; the class is
constructible in a test with a plain new; and a nine-parameter constructor is
visibly embarrassing, which is useful pressure that field injection removes.
Since Spring 4.3 a single constructor needs no @Autowired, and
@RequiredArgsConstructor removes the boilerplate that made people reach for field
injection in the first place.
4. What is the default bean scope, and what does that imply?
Singleton — one instance shared by every concurrent request. The implication is the answer: a singleton must be stateless. A mutable field is shared across threads, so two customers checking out simultaneously overwrite each other.
Strong follow-up: a prototype injected into a singleton is resolved once, so you get one instance
for the application's life. Use ObjectProvider if you need a genuinely fresh one.
5. How would you break a circular dependency?
Boot 4 refuses to start on one, correctly — it is a design problem. Extract the shared logic into a
third bean; or publish an event, if one service only needs the other to react; or inject an
ObjectProvider to defer resolution.
spring.main.allow-circular-references=true is the answer that ends the interview
early. It injects a half-initialised proxy and keeps the design problem.
The proxy rule
6. Why might @Transactional not work?
The single most valuable question here. Spring wraps the bean in a proxy, and
advice only runs for calls arriving through it. A method calling a sibling on this
bypasses the proxy entirely:
public void a() { b(); } // no transaction
@Transactional public void b() { }What makes this a senior answer is generalising it: the same applies to
@Cacheable, @Async, @Retryable, @PreAuthorize and
every @Aspect, and every one fails silently. With
@PreAuthorize it means no authorization check runs at all.
Also: private and final methods are never advised.
7. When does a transaction roll back?
Automatically for unchecked exceptions, not for checked ones — which is why
application exceptions should extend RuntimeException. A checked exception thrown
mid-transaction commits the partial work.
@Transactional(rollbackFor = Exception.class) overrides it.
Data
8. What is the N+1 problem and how do you find it?
Loading N parents then querying once per parent for a lazy association: 14 products becomes 15 queries. Invisible with test data, fatal with real data.
Fix with JOIN FETCH, an entity graph, or batch fetching. Find it by
setting spring.jpa.show-sql=true — and prevent it by setting
spring.jpa.open-in-view=false, which turns the hidden lazy load into a loud
LazyInitializationException during development instead of a silent query storm in
production.
9. Why not return JPA entities from a controller?
Three reasons, and a good candidate gives all three: lazy collections throw or trigger a query
cascade when Jackson serialises them outside a transaction; the entity's shape is a database concern
that should be free to change; and it leaks — one return user; publishes
passwordHash.
Follow-up worth volunteering: separate request and response DTOs, because reusing one class lets a
client set fields it should not — mass assignment. The strongest version is designing the field out
entirely: a DTO with no role field cannot carry a role.
10. ddl-auto=update — why not?
It never removes anything, so schemas accumulate dead columns; it applies changes in an order it chooses; and it gives you no history and no chance to review the SQL before it runs in production.
The answer that shows experience: let a migration tool own the schema and set
validate, so drift between entities and tables fails at startup with a
message naming the table.
11. A soft-delete trap — what is it?
@SQLRestriction("deleted = false") is applied by Hibernate when it builds a query
from the entity model. SQL you write yourself never goes near the entity model, so a
hand-written report query silently counts deleted rows.
This one is from a real bug: deleted orders were counted as revenue. Nothing threw, nothing logged, and the numbers looked plausible — which is why it survived. The lesson is that the failure mode of a correctness bug is usually plausible output, not an exception.
12. When would you use JdbcTemplate over JPA?
When the result is not an entity — aggregates, reports, projections. JPA's value is mapping rows to
an object graph and tracking changes to it; a SUM needs neither.
Corollary worth stating: aggregate in the database. Loading every order to sum totals in Java works on 18 rows and fails on a real table.
Web and API design
13. Should bad input ever produce a 500?
No. A malformed UUID in a request body throws HttpMessageNotReadableException, and
without an explicit handler the catch-all turns it into a 500 — which makes your error rate
meaningless, because it now measures how often someone typed a bad URL. Real outages hide in
that noise.
Handle HttpMessageNotReadableException and
MethodArgumentTypeMismatchException explicitly as 400s.
14. 403 or 404 for a resource that exists but is not the caller's?
404. 403 confirms the resource exists, so an attacker can enumerate ids by collecting them.
The stronger design removes the question: resolve ownership from the token and put no id in the
path at all — /api/me/orders rather than /api/users/{id}/orders.
15. Why do login failures give one vague message?
Account enumeration. "No account with that email" lets an attacker submit a list and learn which addresses are registered — valuable on its own, and more so combined with a password dump from elsewhere.
Same reasoning applies to token parsing: distinguishing "expired" from "forged" tells an attacker whether their forgery was structurally correct.
Security
16. What are the trade-offs of JWTs?
The benefit and the cost are the same property. Stateless means no session store and any instance can serve any request; it also means a token cannot be revoked.
So: no working "log out everywhere", a fired employee's token stays valid, and demoting an admin does not take effect until expiry. Mitigations, in order of cost: short-lived access tokens plus a revocable refresh token; a denylist in Redis; a per-user token version as a claim.
A candidate who says "JWTs are stateless and scalable" without the second half has not run one in production.
17. Is a JWT encrypted?
No — signed. Anyone holding one can decode the payload. The signature guarantees we issued it and nobody edited it, nothing more. Never put anything secret in the claims.
Follow-up: always verify the signature. An unverified token is a string the client wrote, and the
classic attack is setting alg to none.
18. When is disabling CSRF safe?
When authentication is not cookie-based. CSRF works because browsers attach
cookies automatically; a JWT in an Authorization header is not attached automatically, so
there is nothing to exploit.
The critical caveat: that reasoning collapses if you store the token in a cookie. Then
csrf.disable() is a real vulnerability.
19. What is the hard part of "sign in with Google"?
Not the protocol — Spring handles it. It is reconciling the provider's identity with your user table.
The security answer: match only on a verified email. Matching an unverified address is account takeover — an attacker registers at a provider claiming the victim's address and is handed the existing account. And the role must never come from the provider.
Concurrency and integration
20. How does a ThreadPoolTaskExecutor grow?
Most people say "when threads are busy". It actually grows when the queue is full: core threads first, then queue, then up to max, then the rejection policy.
So a large queue means maxPoolSize is almost never reached, and an
unbounded queue means it is never reached at all — the queue simply grows until you run out
of memory.
CallerRunsPolicy is usually right: the submitter runs the task, which applies natural
backpressure and loses nothing.
21. What happens to an exception in an @Async void method?
It disappears. Nothing is watching the return value, so it goes to the
AsyncUncaughtExceptionHandler — which you must configure, or async failures are entirely
invisible. Returning CompletableFuture surfaces it on get() instead.
Related: a new thread has no SecurityContext, no transaction and no persistence
context.
22. Why @TransactionalEventListener(AFTER_COMMIT)?
A plain @EventListener runs inside the publisher's transaction, before commit. So a
confirmation email fires and then the transaction rolls back, leaving the customer holding an
email about an order that does not exist.
The rule: if the work must be undone on rollback, use a plain listener; if it cannot be undone, use
AFTER_COMMIT.
23. When is it safe to retry?
Only when the operation is idempotent, or you supply an idempotency key. Retrying a payment write after a lost response creates a second charge.
Also retry only what could succeed unchanged — a timeout or a 429, never a 400. And use backoff with jitter, or every client retries in synchronised waves and knocks the recovering service over again.
24. What delivery guarantee does a message broker give you?
At-least-once, not exactly-once. A restart or a slow acknowledgement can redeliver. So consumers must be idempotent — which is the same conclusion as the retry question, reached from the other side.
Also expect: a dead-letter queue, or a poison message is redelivered forever; and publish after commit, because the broker knows nothing about your transaction.
25. What is the dual-write problem?
The database commits, then the broker call fails — so the message is never sent, and the two systems disagree. No amount of ordering fixes it, because there is no transaction across both.
The standard answer is the transactional outbox: write the message to a table in the same transaction, and publish from that table separately. Naming it is what separates a candidate who has read about messaging from one who has run it.
Caching and correctness
26. What is hard about caching?
Not the annotations — eviction. Every write path that could invalidate an entry must evict, and missing one serves stale data indefinitely with nothing failing.
Volunteer the rest: cache only what is read often, identical for every caller and rarely changed —
never per-user data, which usually works in testing and leaks between customers under load. And the
default ConcurrentMapCacheManager is per-JVM, so with two replicas one evicts and the
other keeps serving the old value. A TTL bounds the damage of a missed eviction.
27. Why does the server recompute every price?
Because a field the client can set is a field the client can set to anything. Trusting a client-sent price is the most exploitable e-commerce bug there is, and it takes one HTTP client to exploit.
The pizza API's request DTO carries no prices at all — it chooses which products, and every figure is read from the database.
Build and operations
28. What changed in Spring Boot 4 that breaks a Boot 3 build?
Java 21 is the floor. Artifacts were renamed — spring-boot-starter-aop is now
-aspectj, -web is now -webmvc — and the old names are absent
from the BOM, so the error is "version is missing" rather than anything mentioning a rename.
Autoconfiguration classes moved into per-technology packages. And Framework 7 brings retry into the
core container and deprecates the Jackson 2 types.
The subtle one: depending on a library no longer implies its autoconfiguration.
Plain liquibase-core gives you Liquibase that never runs, and the error surfaces as a
Hibernate "missing table" complaint several layers away.
29. Which test would you write first?
The smallest one that can fail honestly. A plain unit test if there is no Spring involved; a
@WebMvcTest slice for a controller; @SpringBootTest only when the thing you
are testing genuinely crosses layers — authorization rules being the good example, because mocking
the security context would only prove the mock works.
The principle worth stating: compute expected values by hand. A test asserting
total == subtotal + tax passes when the formula is wrong.
30. Behaviour does not match the source. What do you check first?
Stale build output. A NoClassDefFoundError naming an unqualified class is the
signature, and ./mvnw clean compile fixes it.
It is a good closing question because the honest answer is not clever — it is knowing that "impossible" behaviour usually means the build, and checking that before spending an afternoon debugging code that is already correct.
What interviewers are listening for
Across all thirty, the same three things separate a strong answer:
- The trade-off, not the definition. "JWTs are stateless" is half an answer; "and therefore unrevocable" is the other half.
- The silent failure. The valuable knowledge is which mistakes throw and which ones quietly produce plausible, wrong output.
- What you would do differently in production — and knowing when the simple version is genuinely fine.
Back to the start of the track, or the cheat sheet.