Spring BootBy Experience Levelintermediate
Updated:

Spring Boot Interview Questions for Experienced Professionals

9 min read

The senior-level Spring Boot questions — transaction boundaries, HikariCP tuning, graceful shutdown, observability and config management — answered the way architects answer them.

TL;DR – Quick Answer

Experienced Spring Boot interviews move past definitions into ownership: where you draw transaction boundaries, how you size a HikariCP pool, how the app drains in-flight requests during a rolling deploy, how you expose health and metrics, and how you manage configuration across environments. Interviewers grade whether you have run Spring Boot in production and can defend your trade-offs, not whether you can recite annotations.

On This Page

Experienced Spring Boot interviews rarely test whether you know what a starter is. They test whether you have owned a service in production — whether you can explain a transaction that didn't roll back, a pool that ran dry under load, or a deploy that dropped requests. The questions below are the ones that separate people who used Spring Boot from people who operated it, and the depth at which you stop answering is exactly what the interviewer is grading.

How do you decide where transaction boundaries go in a service?

The transaction belongs at the service layer, around the unit of business work — not on the repository (too fine, one transaction per query) and not on the controller (too coarse, holding a DB connection while you serialize JSON). One @Transactional service method should map to one atomic business operation.

Putting @Transactional on the repository means each save commits independently, so a multi-step operation can half-succeed. Putting it on the controller keeps the connection checked out during request parsing and response writing, which under load is how you exhaust the pool. The service method is the natural boundary because it is where "do all of this or none of it" is expressed.

@Service
public class OrderService {
    @Transactional
    public Order placeOrder(OrderRequest req) {
        Order order = orderRepo.save(new Order(req));
        inventoryRepo.decrement(req.getSku(), req.getQty());
        paymentRepo.save(new PaymentIntent(order));
        return order; // all three commit together, or none do
    }
}

Interview note: Trap: "your @Transactional method calls another @Transactional method in the same class and the inner propagation is ignored — why?" Because Spring's transaction proxy wraps the bean from outside; a self-invocation (this.otherMethod()) never crosses the proxy, so the inner annotation does nothing. Fix by splitting into two beans or injecting self, not by hoping.

A @Transactional method caught an exception and the transaction still didn't commit. What happened?

By default Spring rolls back only on unchecked (RuntimeException) and Error, not on checked exceptions. But the more common senior-level cause is that once any exception marks the transaction rollback-only, catching it downstream doesn't un-mark it — commit then throws UnexpectedRollbackException.

The default rollback rule surprises people: throw a checked IOException from a @Transactional method and, unless you set rollbackFor, the transaction commits. Separately, if an inner transaction (propagation REQUIRED joins the same physical transaction) fails and you swallow the exception in the outer method, the shared transaction is already poisoned. The commit at the boundary fails even though you "handled" the error.

Interview note: Follow-up: "how do you let an inner failure fail independently without killing the outer transaction?" Use Propagation.REQUIRES_NEW for the inner unit so it runs in its own physical transaction — but know it borrows a second connection from the pool, which matters when you reason about pool sizing.

How do you size a HikariCP connection pool?

Pool size is not "bigger is better" — it's bounded by what the database can serve concurrently. A common starting formula is connections = ((core_count * 2) + effective_spindle_count), and most services need a surprisingly small pool (often 10–20), because connections spend most of their time idle if the app is well-behaved.

The failure mode juniors miss: an oversized pool doesn't add throughput, it adds contention at the database and hides the real bottleneck. The senior instinct is to measure — connection acquisition time and active-connection metrics from Actuator — and to make maximumPoolSize match the database's capacity, not the app's optimism. Equally important is connectionTimeout: when the pool is exhausted, do you want threads to fail fast (short timeout, shed load) or queue (long timeout, cascade the stall)?

spring:
  datasource:
    hikari:
      maximum-pool-size: 15
      minimum-idle: 5
      connection-timeout: 3000   # fail fast under exhaustion
      max-lifetime: 1800000      # recycle below DB/proxy idle timeout
      leak-detection-threshold: 20000

Interview note: Trap: "your service holds a connection for a slow external HTTP call inside a transaction — what's the fix?" Move the external call outside the transaction boundary. Holding a pooled connection across a network call is how a downstream slowdown becomes a pool-exhaustion outage.

What happens to in-flight requests during a rolling deploy, and how do you make shutdown graceful?

A SIGTERM normally stops the app immediately and drops active requests. Spring Boot supports graceful shutdown — set server.shutdown=graceful and a timeout — so the server stops accepting new connections but lets in-flight requests finish before the JVM exits.

The complete answer ties this to the orchestrator. In Kubernetes, the pod is removed from the Service endpoints and receives SIGTERM at roughly the same time, so you also need the readiness probe to flip to down first (via lifecycle hooks or a preStop delay) so no new traffic arrives during the drain window. Graceful shutdown without readiness coordination still drops the requests that arrive in the gap.

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

Interview note: Follow-up: "what breaks if the drain timeout is longer than the orchestrator's grace period?" The orchestrator sends SIGKILL and you lose the very requests graceful shutdown was protecting. The app timeout must be shorter than the platform's termination grace period.

How do you make a Spring Boot service observable in production?

Actuator plus Micrometer. Actuator exposes health, metrics, env, loggers and thread/heap dumps; Micrometer is the metrics facade that publishes to Prometheus, and you wire it to distributed tracing so a request can be followed across services.

At a senior level the point isn't listing endpoints — it's what you instrument and why. You expose liveness and readiness separately so the platform restarts a truly dead process but only routes to a ready one. You add custom HealthIndicators for critical dependencies. You watch the meters Spring already provides — HTTP latency percentiles, HikariCP active connections, JVM memory and GC — before adding custom timers for business operations. The trap is exposing everything publicly.

@Component
class PaymentGatewayHealth implements HealthIndicator {
    public Health health() {
        return gateway.ping()
            ? Health.up().build()
            : Health.down().withDetail("gateway", "unreachable").build();
    }
}

Interview note: Trap: "you exposed /actuator/** and it's reachable from the internet — what's the risk?" env, heapdump and loggers leak secrets and let attackers change log levels. Restrict exposure with management.endpoints.web.exposure.include, secure it with Spring Security, or move Actuator to a separate management.server.port on an internal-only interface.

How do you manage configuration and secrets across many environments?

Externalize everything with Spring's property hierarchy: profiles for per-environment values, environment variables and mounted config for deploy-time overrides, and a secrets manager (Vault, cloud KMS, Kubernetes secrets) for credentials — never secrets in application.yml in git.

The senior view is the ordering. Spring resolves properties through a well-defined precedence: command line and environment variables override profile files, which override the packaged defaults. That lets one immutable artifact run in every environment with only the environment supplying the differences — the 12-factor principle. For dozens of services you add a config server or GitOps so config is versioned and auditable, and you bind related keys into @ConfigurationProperties classes so config is type-safe and validated at startup rather than failing on first use.

Interview note: Follow-up: "how do you refresh config without a redeploy?" Spring Cloud Config plus @RefreshScope and the Actuator /refresh endpoint rebind beans on demand — but be honest that many teams prefer an immutable redeploy over live refresh because live refresh is harder to reason about and roll back.

When would you NOT use a Spring Boot feature?

When the abstraction hides a cost you need to control. Examples: spring.jpa.open-in-view is on by default and keeps a database connection open for the whole request to allow lazy loading in the view — convenient, but a connection-hoarding trap in an API service, so you turn it off and load what you need in the service layer.

This question tests judgement over enthusiasm. Other honest "no" answers: don't auto-configure an embedded in-memory database in production; don't lean on @Transactional propagation gymnastics when a redesign is clearer; don't add Spring Cloud's full stack for a three-service system that a load balancer and a config file could serve. Naming a feature you deliberately disabled — and why — is one of the strongest signals of seniority in these interviews.

spring:
  jpa:
    open-in-view: false   # don't hold a DB connection across view rendering

Interview note: Trap: "why is open-in-view on by default if it's a trap?" Because it prevents LazyInitializationException for beginners, which is the more common support burden. Defaults optimize for the newcomer; production optimizes for the operator — knowing the difference is the answer.

How do you debug a Spring Boot service that starts slowly or fails to start?

Startup issues are usually bean creation cost or a failed auto-configuration. Turn on the condition evaluation report with --debug to see which auto-configurations applied and why, use the Actuator startup endpoint (buffered ApplicationStartup) to find slow bean initializations, and read the failure analyzer's message on a failed start — Spring often names the exact missing bean or port conflict.

The experienced move is to treat startup as measurable, not mysterious. BufferingApplicationStartup records timing for each initialization step, so you can point at the slow one — often eager initialization of a connection pool, a slow classpath scan, or a bean doing I/O in its constructor. For failed starts, the FailureAnalyzer output is the first thing to read, not the raw stack trace.

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        app.setApplicationStartup(new BufferingApplicationStartup(2048));
        app.run(args);
    }
}

Interview note: Follow-up: "your bean does a network call in its constructor and startup is flaky — what's the fix?" Move the call out of construction into a lazily-invoked or lifecycle-managed step, and add resilience. Constructors should build state, not perform I/O — doing I/O there couples startup to a dependency's availability.

How do you handle thread pools and blocking work in a Spring Boot web app?

Understand which pool serves what: with the default servlet stack, Tomcat's worker threads (server.tomcat.threads.max) serve HTTP requests, and a blocking downstream call ties up a worker for its full duration. Offload slow or fan-out work to a dedicated @Async executor with a bounded queue, and size Tomcat's pool against your latency and pool budget rather than leaving the default.

The senior insight is that thread count, connection pool size and downstream latency are one coupled system. If each request holds a DB connection and you have 200 Tomcat threads but 15 DB connections, 185 threads can pile up waiting. You size the layers together, bound the async queues so a backlog fails fast instead of exhausting memory, and consider the reactive stack (WebFlux) only when the workload is genuinely I/O-bound and you can make the whole chain non-blocking.

Interview note: Trap: "you added @Async but the method ran synchronously — why?" Same proxy rule as @Transactional: @Async works through a proxy, so self-invocation and missing @EnableAsync both silently make it synchronous. The default SimpleAsyncTaskExecutor also creates unbounded threads — define a bounded ThreadPoolTaskExecutor bean instead.

What interviewers really test

At the experienced level, Spring Boot is the vocabulary but production judgement is the subject. Interviewers want to hear that transaction boundaries, pool sizes, shutdown behavior and observability were decisions you made and can defend — ideally with a real incident behind them. Prepare three stories: a rollback that didn't happen, a pool that ran dry, a deploy that dropped traffic. Narrate cause, diagnosis and fix.

To sharpen the framework depth those stories rest on, work through the Spring Boot learning path for auto-configuration and data-access internals, and reinforce the JVM and concurrency fundamentals via the Java track — pool sizing and @Async questions live at that boundary. Then pressure-test your war stories in a mock interview, where the follow-up "and what did that cost in production?" is exactly the question you'll face on the day.

Frequently Asked Questions

How are experienced Spring Boot interviews different from fresher ones?
Freshers are asked what @SpringBootApplication does; experienced candidates are asked why a @Transactional method silently didn't roll back, how they tuned the connection pool under load, and how the service behaves during a Kubernetes rolling restart. The topics overlap but every question has a production consequence attached.
What Spring Boot topics matter most for senior roles?
Transaction management and propagation, connection pool and thread pool sizing, graceful shutdown and readiness probes, observability with Actuator and Micrometer, externalized configuration, and knowing when NOT to reach for a Spring feature. These separate people who used Spring Boot from people who operated it.
Do I need to know Spring internals as an experienced developer?
Enough to debug. You should be able to explain why self-invocation bypasses a proxy, why an auto-configuration didn't apply, and how the bean lifecycle interacts with shutdown. You don't need to recite framework source, but 'it just works' answers fail at this level.
Will I get system design questions in a Spring Boot interview?
Often, framed through Spring: how you'd structure transactions across a multi-step flow, how you'd handle a downstream timeout, how config and secrets flow across environments. The framework is the vocabulary; the design judgement is what's scored.
How do I prepare for a senior Spring Boot interview quickly?
Pick three production incidents you actually handled — a pool exhaustion, a rollback that didn't happen, a slow startup — and be able to narrate cause and fix. Concrete war stories beat textbook definitions at this level, and interviewers can tell the difference immediately.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Check the Java Full Stack training details

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 16 July 2026 LinkedIn
Chat with us