Sitemap

Java/Spring Boot/Microservices Interview Questions

12 min readAug 1, 2026

--

Press enter or click to view image in full size

A note on structure: each answer below leads with a one-line definition, then a concrete example, then a “why it matters” close. That’s the SAR shape (Situation → Action → Result) interviewers are listening for — swap in your own project details where you see [your service].

1. Migration & Java Fundamentals

1. Introduce yourself and your recent project. Lead with years of experience, core stack, and one recent project stated as a problem-solution-impact sentence, not a chronology. E.g.: “I have X years in Java/Spring Boot microservices. Most recently I led [project] — we migrated [system] from [old state] to [new state], which reduced [metric] by [%].” Save the full career history for follow-up questions; a rambling intro is the #1 way senior candidates lose control of the first five minutes.

2. What type of migration have you worked on? Name the migration types explicitly and in one sentence each: “Monolith-to-microservices decomposition, Java 8→17 upgrades, and a platform migration (e.g., on-prem to containerized/cloud).” Then pick the most complex one and go one level deeper only if asked.

3. Challenges migrating Java 8 → Java 17?

  • javax → jakarta namespace change: every import touching Servlet/JPA/Bean Validation breaks; needs a systematic find-and-replace plus dependency version bumps (Spring Boot 3.x baseline).
  • Removed/deprecated APIs: e.g., sun.misc.Unsafe usage, deprecated reflection APIs, changes to javax.xml.bind (JAXB) needing separate dependencies.
  • Third-party library compatibility: libraries not yet on Jakarta EE 9+ need version audits before the app can compile.
  • Module system friction (if adopting JPMS): illegal reflective access warnings surfacing as errors on later versions.
  • Build tooling: Maven/Gradle plugin versions and bytecode target levels need alignment. Approach: inventory dependencies first, upgrade in a branch behind CI, fix compilation errors in waves (framework layer → business layer → tests), then performance-test before cutover.

4. Features introduced in Java 17? Java 17 is an LTS release, so it bundles features from 9–17:

  • Sealed classes/interfaces (JEP 409) — restrict which classes can extend/implement a type.
  • Records (JEP 395) — immutable data carriers with generated constructor/equals/hashCode/toString.
  • Pattern matching for instanceof (JEP 394).
  • Text blocks (JEP 378) — multi-line string literals.
  • Enhanced switch expressions (JEP 361) — -> syntax, exhaustiveness.
  • Strong encapsulation of JDK internals (JEP 403).

5. Why use sealed classes? Sealed classes let you control the exact, finite set of subtypes at compile time via permits. This gives you two wins: (1) exhaustive switch pattern matching without a default branch — the compiler verifies you've handled every case, and (2) modeling closed domains accurately (e.g., a PaymentStatus hierarchy that should never be extended by external code). It's the middle ground between a rigid enum and an open interface.

2. Concurrency & JVM

6. How does the Java Memory Model affect visibility across threads in a multi-threaded app? The JMM defines happens-before rules: without proper synchronization, one thread’s writes to shared state aren’t guaranteed to be visible to another thread due to CPU caching and compiler reordering. Practical answer for a shared-data scenario:

  • Use volatile for simple flags/state that needs visibility guarantees without full locking.
  • Use synchronized blocks or java.util.concurrent.locks.ReentrantLock for compound actions (read-modify-write).
  • Prefer java.util.concurrent structures (ConcurrentHashMap, AtomicInteger, CopyOnWriteArrayList) over manual locking — they encode the happens-before guarantees correctly.
  • Thread pools (ExecutorService, or virtual threads in Java 21+) matter for a different reason — they bound concurrency and prevent resource exhaustion — but they don't by themselves solve visibility; you still need proper synchronization within the tasks the pool executes. Naming this distinction is exactly what separates a strong answer from a partial one here.

3. Spring Core / Dependency Injection

7. Difference between @Autowired, @Qualifier, and @Resource?

  • @Autowired (Spring annotation): injects by type first; if multiple beans of the same type exist, Spring throws NoUniqueBeanDefinitionException unless disambiguated.
  • @Qualifier (Spring annotation): used alongside @Autowired to pick a specific bean by name when multiple candidates exist — @Autowired @Qualifier("primaryDataSource").
  • @Resource (Jakarta/JSR-250 annotation, not Spring-specific): injects by name first, falling back to type if no name match is found. Because it's a Java standard annotation, it's more portable across DI frameworks, but less flexible than Spring's own annotations for constructor injection. Interview-safe one-liner: "@Autowired is type-first with @Qualifier as its disambiguator; @Resource is name-first and framework-agnostic."

8. Have you used @Primary? How does it relate to @Qualifier? @Primary marks one bean as the default choice when multiple candidates exist and no @Qualifier is specified at the injection point. @Qualifier is an explicit override at the call site. Rule of thumb: use @Primary for the "usual" implementation, and @Qualifier at specific injection points that need the non-default one.

4. Transactions & Saga Pattern

9. What does @Transactional do in Spring Boot? It wraps a method in a database transaction boundary using Spring's proxy-based AOP. If the method completes normally, the transaction commits; if a RuntimeException (unchecked, by default) propagates out, it rolls back. Key gotchas worth stating: it only works on public methods called through the Spring proxy (self-invocation from within the same class bypasses it), and checked exceptions don't trigger rollback unless you specify rollbackFor.

10. What does the Saga pattern solve? In a single database, ACID transactions give you atomicity for free. Across microservices with separate databases, you can’t use a single DB transaction — so the Saga pattern replaces one atomic transaction with a sequence of local transactions, each with a compensating action to undo it if a later step fails. Two flavors: choreography (each service publishes events, others react — no central coordinator) and orchestration (a central saga orchestrator tells each service what to do next).

11. Should notification-service failure trigger a saga rollback in an order → payment → inventory → notification flow? No. The test is: is this step a business-critical dependency of the transaction, or a side effect? Order, payment, and inventory are tightly coupled — each depends on the previous succeeding. Notification is not — a failed notification doesn’t invalidate the order. Rolling back a completed order/payment/inventory chain because an email didn’t send would be a correctness bug, not a safety measure.

12. If not saga, how do you handle it? Decouple it with asynchronous, at-least-once messaging: publish an OrderCompletedEvent to Kafka (or SQS/SNS) once the core transaction succeeds; the notification service consumes it independently, with its own retry policy and dead-letter queue for persistent failures. This means notification delivery can lag or retry without ever blocking or reversing the order flow.

Get Meet2sudhakar’s stories in your inbox

Join Medium for free to get updates from this writer.

13. How do you choose transaction propagation level across service calls? Map propagation to ownership of the transaction boundary:

  • REQUIRED (default): join an existing transaction, or start one if none exists — right for most in-service calls that should share atomicity.
  • REQUIRES_NEW: always start a fresh, independent transaction, suspending any existing one — right for audit logging or anything that must persist regardless of whether the outer transaction later rolls back.
  • NESTED: a savepoint within the outer transaction — lets an inner unit fail and roll back without killing the whole outer transaction (JDBC-only, not universally supported).
  • MANDATORY: throws if called outside an existing transaction — enforces that a method must never run standalone.
  • SUPPORTS: joins a transaction if one exists, otherwise runs non-transactionally — right for read-only helper methods. Across service boundaries (different databases), propagation levels don't apply the same way — that's exactly why saga/compensation replaces cross-DB propagation.

14. If a saga step fails, how do you ensure consistency? Give a compensation example. Each forward step has a matching compensating action, and the saga tracks state via a transaction ID + status (e.g., PENDING, COMPLETED, COMPENSATING, FAILED). Concrete example: Order created → Payment deducted → Inventory reserved → Shipping fails. Compensations fire in reverse: Inventory service releases the reserved stock, Payment service issues a refund, Order service marks the order CANCELLED. Until the final step succeeds, the order sits in a pending state rather than a final "success" state — this is what prevents a partially-completed transaction from ever looking done.

5. Multi-Database / Data Architecture

15. How do you configure multiple data sources in one Spring Boot app?

  • Define separate DataSource, EntityManagerFactory, and TransactionManager beans per database, each pointed at distinct application.properties/application.yml prefixes (e.g., spring.datasource.mysql.*, spring.datasource.mongo.*).
  • Use @Primary on one DataSource to resolve ambiguity for beans that don't specify which to use.
  • For JPA + a NoSQL store together, mix Spring Data JPA (relational) with Spring Data MongoDB in the same app, each with its own repository package scan (@EnableJpaRepositories(basePackages=...), @EnableMongoRepositories(basePackages=...)) so Spring doesn't try to treat MongoDB repositories as JPA ones.
  • In microservices (vs. a modular monolith), the cleaner pattern is one database per service rather than one service juggling multiple databases — multi-DataSource config is more common at monolith boundaries or for cross-cutting concerns like audit/logging stores.

16. SQL vs NoSQL — how do you decide when the client leaves it open? Decide on data shape and access pattern, not familiarity:

  • Choose SQL/relational when the schema is well-defined and stable, relationships matter (foreign keys, joins), and you need strong consistency/ACID guarantees — e.g., user accounts, financial ledgers, order records.
  • Choose NoSQL (document store) when the schema varies per record, you’re storing nested/variable structures, and horizontal scale or write throughput matters more than joins — e.g., user activity logs, product catalogs with variable attributes, session data.
  • Also factor in query patterns: heavy ad-hoc joins/reporting → relational; primarily key-based lookups → document/key-value store.

17. Can microservices share a database? Not recommended as a default. Shared databases reintroduce tight coupling — a schema change in one service can silently break another, and you lose the ability to deploy/scale services independently, which defeats the core purpose of microservices. It’s sometimes used pragmatically during a migration’s transitional phase (strangler pattern), but the end state should be one database per service with communication via APIs/events, not shared tables.

18. How do you handle distributed transactions in microservices? Avoid two-phase commit (2PC) as a default — it’s tightly coupled, blocks all participants while any one is slow, and doesn’t scale well with service/database count. Instead:

  • Saga pattern with compensating transactions for multi-step business processes.
  • Eventual consistency via event-driven architecture (Kafka/outbox pattern) rather than synchronous cross-service locking.
  • Transactional outbox pattern to atomically persist a state change and the event announcing it, avoiding dual-write inconsistency.

6. CAP Theorem

19. What is CAP theorem? In a distributed data store, you can only guarantee two of three properties simultaneously: Consistency (every read gets the latest write), Availability (every request gets a response, even if not the latest data), and Partition tolerance (the system keeps working despite network partitions between nodes). Since network partitions are a fact of life in distributed systems, the real-world choice is between CP (consistent but may reject requests during a partition — e.g., traditional RDBMS clusters, ZooKeeper) and AP (always responds but may serve stale data — e.g., DynamoDB, Cassandra by default). Worth adding in an interview: CAP is a simplification — in practice, many systems tune consistency per-operation (e.g., DynamoDB’s eventually-consistent vs. strongly-consistent reads) rather than picking one mode globally.

7. Deployment & CI/CD

20. What deployment strategy do you use? What’s your downtime? Answer with your actual pipeline and a number, then name the strategy: “We use [rolling update/recreate], averaging [X seconds] of downtime — mainly container restart time, since we run health checks before routing traffic.” If downtime is non-zero, be ready for the natural follow-up (#22).

21. Walk through your CI/CD pipeline. Standard shape to describe: feature branch → PR → automated tests run in CI → merge to main triggers a workflow → Docker image built and pushed to a registry (ECR/Docker Hub) → deployment to target environment (ECS/EKS/EC2) → health check gates traffic cutover → rollback trigger if health checks fail. Name the actual tools you use (GitHub Actions, ArgoCD, etc.) rather than describing it generically — that’s what makes this answer feel senior rather than textbook.

22. Have you implemented blue-green deployment or canary release? If you haven’t, don’t just say no — show you understand when you’d reach for each:

  • Blue-green: run two full environments (blue = current, green = new); switch traffic all at once via load balancer/DNS once green passes health checks. Near-zero downtime, easy instant rollback (just switch back), but doubles infrastructure cost during the cutover window.
  • Canary: gradually shift a small percentage of traffic (1% → 10% → 50% → 100%) to the new version, monitoring error rates/latency at each step before proceeding. Lower blast radius for bad releases, but needs good metrics/observability to detect problems early.

23. How would you roll out a feature to 1–3% of users? Canary release, implemented via one of: (a) a service mesh (Istio/Linkerd) doing weighted traffic splitting at the routing layer, (b) a feature flag system (LaunchDarkly, Unleash, or a custom flag) gating the code path by user cohort, or © load balancer weighted target groups (e.g., ALB with two target groups at 97/3 split). Combine with metrics dashboards and automated rollback triggers on error-rate thresholds — the release should be reversible in seconds, not manually diagnosed after the fact.

8. Git

24. How do you resolve Git conflicts? Pull the latest target branch, attempt the merge/rebase locally, and Git marks conflicting sections with <<<<<<<, =======, >>>>>>> markers in the file. Resolve by reviewing both versions' intent (not just picking one blindly — talk to the other author if the conflict is non-trivial), edit the file to the correct combined state, remove the markers, git add the resolved files, then complete the merge/rebase commit. For frequent conflicts in a shared file, consider whether the team should split it into smaller modules.

25. Have you used Git merge tools? If yes, name it (VS Code’s built-in diff view, TortoiseGit, git mergetool with Beyond Compare/KDiff3, or IntelliJ's merge UI) and briefly why — visual three-way merge views make it much easier to see base/theirs/yours side by side than resolving raw conflict markers in a text editor.

26. What does git cherry-pick do? git cherry-pick <commit-hash> applies the changes from a specific commit onto your current branch, creating a new commit with the same changes (different hash). Common use case: you need one bug fix from a feature branch to go to production immediately, without merging the entire (still-in-progress) branch — you cherry-pick just that one commit onto the release/hotfix branch.

27. Production bug post-deploy: rollback vs. fix-forward — which and why? Default to rollback first, root-cause after when the bug materially impacts users or business operations — restore service, then investigate calmly rather than debugging under pressure with live traffic hitting a broken path. The decision hinges on blast radius: business-critical/customer-facing → rollback immediately; internal-only/low-impact → it’s reasonable to fix forward if you’ve already identified the root cause, since a full rollback might reintroduce other issues the deploy fixed.

28. Does the answer change if the bug isn’t business-critical? Yes — this is the follow-up that tests judgment, not just knowledge of the two options. For a low-impact bug affecting internal tooling or a non-critical path, fixing forward in a small, reviewed branch is often faster and safer than a rollback (which could also revert unrelated fixes bundled in the same deploy). The general principle: rollback minimizes risk when impact is high and diagnosis takes time; fix-forward minimizes disruption when impact is low and the fix is already understood.

9. Design Patterns

29. What design patterns are you familiar with? Give a categorized list, not a random one — it signals structured thinking:

  • Creational: Singleton, Factory/Abstract Factory, Builder.
  • Structural: Adapter, Decorator, Facade, Proxy.
  • Behavioral: Strategy, Observer, Template Method, Chain of Responsibility.
  • Architectural (worth mentioning at senior level): Saga, CQRS, Circuit Breaker, Outbox.

30. Where have you implemented Singleton? Give a real example: a shared, stateless service class (e.g., a TicketService or a configuration/cache holder) where you want exactly one instance managing shared resources — Spring beans are singleton-scoped by default, so in a Spring app you often get this for free via @Service/@Component, which is itself worth mentioning as the "modern" way this pattern shows up.

31. Which Singleton implementation style did you use? Name the specific mechanism:

  • Enum singleton: enum Singleton { INSTANCE; } — thread-safe by JVM guarantee, serialization-safe, and the generally recommended approach in plain Java.
  • Double-checked locking: lazy initialization with a volatile instance field and synchronized block only on first creation — avoids synchronization overhead on every access after initialization.
  • Eager initialization: instance created at class-load time — simplest, but no lazy-loading benefit.
  • Spring-managed singleton: in Spring apps, you typically don’t hand-roll this at all — the container manages a single shared instance of any default-scoped bean, which is the answer worth leading with if that’s how you actually did it.

Closing Note for Your Own Answers

Where the mock-interview candidate lost points wasn’t technical knowledge — it was structure. Every answer above follows the same shape: definition → concrete scenario → why it matters. When you adapt these for your own interviews, swap the generic examples (order/payment/inventory) for your actual project (Rabobank platform, ECS/EKS, golden pipeline governance, etc.) — real numbers and real service names always beat textbook phrasing, as long as they're delivered in that same tight structure.

--

--