Creational, structural and behavioral patterns as used in Java backends and Spring, plus dependency injection, repository/DTO, CQRS, outbox, saga, circuit breaker, idempotency, anti-patterns and functional alternatives.
Theory
Q1
What is a design pattern and why do interviewers ask about them?
basic
A design pattern is a named, reusable solution to a recurring design problem, described by intent, structure and trade-offs rather than concrete code. It gives teams a shared vocabulary ("wrap it in a decorator") and captures known consequences.
GoF patterns fall into creational, structural and behavioral groups.
In backend work the value is recognizing the pattern inside frameworks (Spring, JDK) and picking one deliberately instead of by habit.
⚠ Follow-up traps
Is a pattern a library you import? No, it is a design idea; you implement it per context.
Should every problem use a pattern? No, forcing patterns onto simple code adds indirection without benefit.
#fundamentals#patterns
Q2
How do you implement a thread-safe Singleton?
intermediate
Make the constructor private and expose one instance created safely. The common options are eager initialization, double-checked locking with a volatile field, the initialization-on-demand holder, or a single-element enum.
public final class Config { private static volatile Config instance; private Config() {} public static Config get() { Config c = instance; if (c == null) { synchronized (Config.class) { c = instance; if (c == null) instance = c = new Config(); } } return c; }}
Without volatile, another thread may see a non-null but partially constructed object because of instruction reordering.
Eager static final initialization is simplest when construction is cheap.
⚠ Follow-up traps
Is synchronized on get() wrong? Correct but takes a lock on every call; double-checked locking avoids that.
Does volatile matter in Java 5+? Yes, it prevents unsafe publication in double-checked locking.
#singleton#concurrency
Q3
What is the initialization-on-demand holder idiom?
intermediate
A private static nested class holds the instance in a static final field. The JVM initializes the holder class lazily, on first access of getInstance(), and class initialization is guaranteed thread-safe by the JLS.
public final class Registry { private Registry() {} private static class Holder { static final Registry INSTANCE = new Registry(); } public static Registry getInstance() { return Holder.INSTANCE; }}
No explicit locking and no volatile, yet lazy.
Cannot take constructor arguments and cannot recover from an exception in the constructor (the class becomes unusable with NoClassDefFoundError).
⚠ Follow-up traps
Is the holder loaded when Registry is loaded? No, only when Holder is first referenced.
What if the constructor throws? The holder class is in an erroneous state and later calls fail.
#singleton#classloading
Q4
Why is an enum Singleton considered the most robust?
intermediate
enum Singleton { INSTANCE; } gets thread-safe lazy-on-class-load creation, built-in serialization safety, and protection against reflection instantiation, all from the language.
Normal singletons break on deserialization unless readResolve() returns the instance.
Constructor.newInstance on an enum throws IllegalArgumentException.
Limitations: cannot extend another class (can implement interfaces) and is created at enum class load, not lazily on demand.
⚠ Follow-up traps
Can an enum singleton be lazy? Only as lazy as class loading; it is initialized when first used, not on JVM start.
Can reflection break a classic singleton? Yes, via setAccessible(true) on the private constructor.
#singleton#enum#serialization
Q5
Why is the classic Singleton often called an anti-pattern, and what replaces it in Spring?
intermediate
Static singletons are hidden global state: hard to mock, order-dependent in tests, and tightly coupled to callers. Spring replaces them with singleton-scoped beans, where the container manages one instance per ApplicationContext and injects it.
Spring's "singleton" means one instance per container per bean definition, not per JVM or per class.
The bean must be stateless or thread-safe because many request threads share it.
⚠ Follow-up traps
Is a Spring singleton bean thread-safe by default? No, Spring does not synchronize anything; mutable fields are your responsibility.
Two contexts in one JVM? Each creates its own instance.
#singleton#spring#testing
Q6
What is the Factory Method pattern?
basic
Define a method that creates an object, letting callers depend on an interface while subclasses or the factory decide the concrete type. It centralizes creation logic and hides constructors.
interface Notifier { void send(String msg); }final class NotifierFactory { static Notifier of(String channel) { return switch (channel) { case "email" -> new EmailNotifier(); case "sms" -> new SmsNotifier(); default -> throw new IllegalArgumentException(channel); }; }}
Static factory methods also give names, caching and subtype return, unlike constructors.
⚠ Follow-up traps
Is a static factory the same as GoF Factory Method? Not strictly; GoF uses an overridable creator method, but the intent is similar and static factories are the common idiom.
Open/closed issue in the switch? Adding a channel edits the factory; use a registry map or Spring injection of all implementations to avoid that.
#factory#creational
Q7
How does Abstract Factory differ from Factory Method?
intermediate
Factory Method creates one product; Abstract Factory creates a family of related products behind one interface so they stay consistent.
Example: a DatabaseDialectFactory returning matching QueryBuilder, TypeMapper and Migrator for PostgreSQL or MySQL.
JDK example: DocumentBuilderFactory, and JDBC Connection creating Statement, PreparedStatement for one driver.
Adding a new product type forces changes in every factory; adding a new family is easy.
⚠ Follow-up traps
Main downside? Adding a new product kind changes the factory interface and all implementations.
How does Spring express families? Profiles or @ConditionalOnProperty select a coherent set of beans.
#abstract-factory#creational
Q8
When should you use the Builder pattern and how is it written in Java?
basic
Use Builder when an object has many optional parameters or must be immutable but validated at construction. It avoids telescoping constructors and unreadable positional arguments.
public final class HttpCall { private final String url; private final int timeoutMs; private HttpCall(Builder b) { url = b.url; timeoutMs = b.timeoutMs; } public static Builder builder(String url) { return new Builder(url); } public static final class Builder { private final String url; private int timeoutMs = 3000; private Builder(String url) { this.url = url; } public Builder timeoutMs(int t) { timeoutMs = t; return this; } public HttpCall build() { return new HttpCall(this); } }}
Is a builder thread-safe? Usually not; treat it as single-thread, but the built object is immutable and shareable.
Records instead of builders? Records give immutability but not named optional params; a builder or static factory is still useful for many fields.
#builder#immutability
Q9
What is the Prototype pattern and how does Java's `clone()` relate?
intermediate
Create new objects by copying an existing instance instead of constructing from scratch. It is useful when construction is expensive or the concrete type is unknown at the call site.
Cloneable/Object.clone() does a shallow field copy and is considered flawed: no constructor call, checked exception, and a marker interface with no methods.
Prefer copy constructors or copy factories, or deep copy via serialization or mapping libraries.
Spring's prototype scope returns a fresh bean instance per injection or getBean call.
⚠ Follow-up traps
Does clone() copy nested objects? Not by default; mutable fields are shared unless you copy them manually.
Is a prototype-scoped bean destroyed by Spring? No, the container does not call destroy callbacks for prototypes.
#prototype#clone
Q10
What is the Adapter pattern and where does it show up?
basic
Adapter converts one interface into another that clients expect, so incompatible classes can collaborate without changing either.
Typical backend use: wrapping a third-party payment SDK behind your own PaymentGateway interface.
JDK: Arrays.asList (array to List), InputStreamReader (bytes to chars), Collections.enumeration.
Adapter vs Facade? Adapter changes one interface to match another; Facade simplifies a subsystem with a new, smaller interface.
Is a mapper from entity to DTO an adapter? Conceptually similar but usually just a mapping layer.
#adapter#structural
Q11
Explain the Decorator pattern.
basic
Decorator wraps an object implementing the same interface to add behavior before or after delegating, composable at runtime without subclass explosion.
interface PriceService { double price(String sku); }class CachingPriceService implements PriceService { private final PriceService delegate; private final Map<String, Double> cache = new ConcurrentHashMap<>(); CachingPriceService(PriceService d) { delegate = d; } public double price(String sku) { return cache.computeIfAbsent(sku, delegate::price); }}
Decorator vs inheritance? Decorators compose at runtime and stack in any order; inheritance fixes behavior at compile time.
Decorator vs Proxy? Decorator adds responsibilities chosen by the client; Proxy controls access and is usually transparent to the client.
#decorator#structural
Q12
What is the Proxy pattern and what kinds exist?
intermediate
A proxy is a stand-in with the same interface that controls access to the real object. Kinds: virtual (lazy loading), protection (authorization), remote (RPC stubs), and smart (caching, logging, transactions).
Hibernate lazy entities and getReference() return virtual proxies.
Spring AOP uses proxies to apply @Transactional, @Cacheable, @Async, @Retryable.
Feign/Retrofit clients are remote proxies generated from interfaces.
⚠ Follow-up traps
Is a proxy always a subclass? No, JDK proxies implement interfaces; CGLIB generates a subclass.
Does the caller know it's a proxy? Normally no, which is why this == proxy surprises appear.
#proxy#structural
Q13
Compare JDK dynamic proxies and CGLIB proxies.
intermediate
JDK proxies (java.lang.reflect.Proxy) implement the target's interfaces and route calls through an InvocationHandler. CGLIB (repackaged in Spring, using ByteBuddy-like subclass generation) creates a subclass of the target class and overrides its methods.
Spring Boot 2+ defaults to CGLIB (spring.aop.proxy-target-class=true), so interfaces are not required.
CGLIB cannot proxy final classes or final/private methods; they run unadvised on the proxy instance.
JDK proxies can only expose interface methods; casting to the concrete class fails.
Runnable r = (Runnable) Proxy.newProxyInstance( Runnable.class.getClassLoader(), new Class<?>[]{Runnable.class}, (proxy, m, args) -> { System.out.println("before"); return null; });
⚠ Follow-up traps
Why does @Transactional on a final class method not work? CGLIB cannot override it, so no interception.
Does CGLIB call the constructor twice? Older versions did; Spring 4+ uses Objenesis to avoid invoking the constructor.
#proxy#jdk-proxy#cglib#spring-aop
Q14
What is the Facade pattern?
basic
Facade provides a single simplified interface to a complex subsystem, reducing coupling between clients and many internal classes.
Backend example: an OrderFacade.placeOrder() orchestrating inventory, payment, and shipping services so the controller makes one call.
JDK/Spring: JdbcTemplate, RestTemplate/RestClient, and SLF4J are facades over lower-level APIs.
Risk: a facade that accumulates business logic becomes a god class.
⚠ Follow-up traps
Does a facade hide the subsystem completely? No, advanced clients can still use subsystem classes directly.
Facade vs service layer? A service layer often is a facade; the distinction is intent, not structure.
#facade#structural
Q15
Explain the Composite pattern.
intermediate
Composite lets you treat individual objects and groups of objects uniformly through a common interface, forming tree structures.
Where should child-management methods live? Safe design puts them only on composite; transparent design puts them on the interface at the cost of leaf errors.
Recursion risk? Cycles in the graph cause stack overflow; validate on construction.
#composite#structural
Q16
What is the Strategy pattern and where does Spring use it?
basic
Strategy encapsulates interchangeable algorithms behind an interface so the algorithm can be selected at runtime without conditionals in the client.
Spring examples: PasswordEncoder, HandlerMapping, ViewResolver, TransactionManager, CacheResolver, AuthenticationProvider, RetryPolicy.
Explain the Observer pattern and its Java/Spring forms.
basic
Observer defines a one-to-many dependency: when a subject changes state, registered observers are notified automatically, decoupling publisher from subscribers.
JDK: java.util.Observable/Observer are deprecated since Java 9; use PropertyChangeListener, Flow (reactive streams) or your own listener interfaces.
Spring: ApplicationEventPublisher plus @EventListener; @TransactionalEventListener fires after commit.
Listeners are synchronous on the publishing thread by default, so a slow or failing listener affects the publisher unless @Async is used.
⚠ Follow-up traps
Are Spring events asynchronous by default? No, they run in the caller thread and in its transaction.
Observer vs message broker? A broker adds durability, process isolation and retries; in-process observers lose events on crash.
#observer#events#behavioral
Q18
What is the Template Method pattern?
basic
A base class defines the skeleton of an algorithm in a final method and defers certain steps to subclasses through abstract or hook methods.
abstract class ReportJob { public final void run() { var data = load(); var out = format(data); publish(out); } protected abstract List<String> load(); protected abstract String format(List<String> d); protected void publish(String s) { System.out.println(s); }}
Spring examples: JdbcTemplate, TransactionTemplate, AbstractController, OncePerRequestFilter.doFilterInternal.
Callback-based variants (JdbcTemplate.query(sql, rowMapper)) replace inheritance with lambdas.
⚠ Follow-up traps
Why make the template method final? So subclasses cannot change the algorithm order.
Downside versus Strategy? Inheritance couples you to the base class and allows only one variation axis.
#template-method#behavioral#inheritance
Q19
Explain Chain of Responsibility with backend examples.
intermediate
A request passes along a chain of handlers; each handler processes it, modifies it, or passes it on, so the sender does not know which handler acts.
Servlet Filter chain and Spring Security's SecurityFilterChain; HandlerInterceptor chain.
Logging frameworks: appenders and log levels; Netty ChannelPipeline.
Each handler decides whether to continue (chain.doFilter(req, res)); forgetting the call silently stops the request.
⚠ Follow-up traps
Is it guaranteed that some handler handles the request? No, a pure chain can end unhandled; add a terminal handler.
Order matters? Yes; for example authentication filters must precede authorization.
#chain-of-responsibility#behavioral#filters
Q20
What is the Command pattern?
intermediate
Command wraps a request as an object with an execute method (and possibly undo), so requests can be queued, logged, retried, scheduled or sent across process boundaries.
Runnable/Callable submitted to an ExecutorService are commands.
Serializable commands enable audit logs and replay.
⚠ Follow-up traps
Command vs Strategy? Command encapsulates a specific request to run later; Strategy encapsulates an algorithm choice.
How do you make commands idempotent? Include an id and track processed ids.
#command#behavioral
Q21
Explain the State pattern.
intermediate
State lets an object alter its behavior when its internal state changes by delegating to a state object, replacing large if/switch blocks on a status field.
Enums with behavior are the lightweight Java form; Spring Statemachine is a library for complex flows.
Persist only the state name; reconstruct behavior from it.
⚠ Follow-up traps
Who triggers transitions? Either the context or the states themselves; keep a legal-transition table to prevent illegal jumps.
State vs Strategy? Same structure, different intent: states know about each other and change on events.
#state#behavioral#fsm
Q22
What is the Visitor pattern and what replaces it in modern Java?
advanced
Visitor separates an operation from the object structure via double dispatch: elements expose accept(visitor) and call visitor.visit(this). It makes adding operations easy but adding element types hard.
Java 17+ sealed interfaces with pattern matching switch give exhaustive dispatch without boilerplate.
JDK examples: Files.walkFileTree with FileVisitor, ElementVisitor in annotation processing.
sealed interface Expr permits Num, Add {}record Num(int v) implements Expr {}record Add(Expr l, Expr r) implements Expr {}static int eval(Expr e) { return switch (e) { case Num n -> n.v(); case Add a -> eval(a.l()) + eval(a.r()); };}
⚠ Follow-up traps
Why double dispatch? Java dispatches only on the receiver at runtime; the visitor call picks the overload on the element's static type.
Downside of visitor? A new element type forces changes in every visitor.
#visitor#behavioral#sealed
Q23
What is Dependency Injection and Inversion of Control?
basic
IoC means a framework controls object creation and wiring instead of your code. Dependency Injection is the technique: collaborators are supplied to an object rather than looked up or constructed by it.
Types: constructor, setter, field injection; constructor injection is preferred for immutability, required dependencies and easy unit testing.
Benefits: loose coupling to interfaces, easy substitution with mocks, central lifecycle management.
Spring's container (ApplicationContext) resolves types, creates beans and applies proxies.
⚠ Follow-up traps
Is DI the same as the Service Locator? No, a locator makes the class pull dependencies; DI pushes them in.
Why avoid field injection? It hides dependencies, prevents final fields, and requires reflection or the container in tests.
#di#ioc#spring
Q24
How does Spring resolve circular dependencies and why avoid them?
advanced
Constructor-injected cycles fail with BeanCurrentlyInCreationException. Setter/field cycles for singletons used to be resolved through early references (three-level cache), but Spring Boot 2.6+ prohibits circular references by default.
A cycle signals a design problem; break it by extracting a third component, using events, or @Lazy on one injection point.
spring.main.allow-circular-references=true re-enables the old behavior; use it only as a stopgap.
⚠ Follow-up traps
Do prototype cycles work? No, Spring cannot resolve them.
Does @Lazy fix the design? It injects a lazy proxy and defers the problem.
#di#spring#circular-dependency
Q25
What are the Spring bean scopes and how do they relate to patterns?
basic
singleton (default, one per container), prototype (new per request for the bean), and web scopes request, session, application, websocket.
Singleton scope is the container-managed Singleton; prototype is Prototype.
Injecting a prototype into a singleton yields one instance created at injection time; use ObjectProvider, @Lookup or a scoped proxy for a fresh one per use.
⚠ Follow-up traps
Does a request-scoped bean injected into a singleton work? Only via a scoped proxy (proxyMode = TARGET_CLASS).
Singleton bean and mutable state? Shared across threads; races are likely.
#spring#scopes#singleton#prototype
Q26
Where does Spring use the Factory pattern?
intermediate
BeanFactory/ApplicationContext are factories for beans. FactoryBean<T> customizes creation of a bean, and @Bean methods are factory methods on configuration classes.
Injecting &myFactoryBean returns the factory itself, while myFactoryBean returns the product.
ObjectProvider<T> and Supplier beans offer lazy, repeatable creation.
⚠ Follow-up traps
BeanFactory vs ApplicationContext? ApplicationContext adds events, i18n, resource loading and eager singleton initialization.
Is @Bean method called each time it is referenced? In @Configuration (full mode) CGLIB intercepts calls and returns the singleton; in lite mode it runs each time.
#spring#factory#beanfactory
Q27
Summarize where Spring uses Template Method, Proxy, Observer, Strategy, Adapter and Singleton.
Chain of Responsibility: security filter chain, interceptors.
Front Controller: DispatcherServlet.
⚠ Follow-up traps
Is DispatcherServlet a facade? It is the Front Controller, routing to handlers; some call it a mediator.
Which pattern implements @Transactional? Proxy with Around advice (Decorator-like) delegating to a transaction manager (Strategy).
#spring#patterns#summary
Q28
Why do self-invocations bypass Spring proxies?
advanced
Proxy-based AOP intercepts calls entering through the proxy object. A call to this.otherMethod() inside the target goes straight to the real object, so @Transactional, @Cacheable and @Async on the callee are skipped.
Fixes: move the method to another bean, inject the bean into itself via ObjectProvider/@Lazy, use AopContext.currentProxy(), or switch to AspectJ weaving.
Same applies to private, final and (for JDK proxies) non-interface methods.
⚠ Follow-up traps
Does @Transactional on a protected method work? With class-based proxies in Spring 6 yes; earlier versions required public.
Does AspectJ weaving have the problem? No, advice is woven into bytecode.
#proxy#spring-aop#pitfalls
Q29
What is the Repository pattern and how does it differ from DAO?
intermediate
A Repository gives collection-like access to aggregate roots in domain terms (orders.findById, orders.add), hiding persistence. A DAO is a lower-level table-oriented gateway exposing CRUD operations on a data source.
Repository comes from DDD: one per aggregate root, returns domain objects, speaks the ubiquitous language.
DAO maps closely to tables or entities and is a data-access abstraction.
Spring Data JpaRepository is a repository abstraction generated via proxies; a plain EntityManager wrapper is closer to DAO.
⚠ Follow-up traps
Repository per entity? In DDD it is per aggregate root, not per table.
Should repositories contain business logic? No, only query and persistence concerns.
#repository#dao#ddd
Q30
What is a DTO and why separate it from the entity?
basic
A DTO (Data Transfer Object) is a plain data carrier shaped for a boundary such as a REST response. Separating it from the JPA entity decouples the API contract from the database schema and avoids leaking lazy associations, internal fields and bidirectional cycles.
Use records (Java 16+) for immutable DTOs.
Map with MapStruct or manual mappers; Spring Data supports interface/class projections to load only needed columns.
Return DTOs from controllers to avoid LazyInitializationException and accidental over-exposure (for example password hashes).
⚠ Follow-up traps
Is DTO the same as VO? In Java usage a value object is immutable and compared by value; a DTO is a transfer shape with no behavior.
Costs? Extra mapping code; justified at public API boundaries.
#dto#layering#api
Q31
What is the Unit of Work pattern and how does JPA implement it?
advanced
Unit of Work tracks changes to objects during a business transaction and writes them together at commit. The JPA persistence context does this: managed entities are tracked and dirty ones are flushed as SQL on commit or flush.
Along with an identity map: within one persistence context the same row yields the same instance.
@Transactional defines the unit's boundary; EntityManager.flush() forces writes early.
⚠ Follow-up traps
Do you need to call save for a managed entity change? No, dirty checking flushes it at commit.
What breaks the identity map? Detached entities or different persistence contexts.
#unit-of-work#jpa#persistence-context
Q32
What is CQRS?
intermediate
Command Query Responsibility Segregation separates the write model (commands that change state) from the read model (queries returning data), allowing each to be optimized and scaled independently.
Simple form: separate command and query services sharing one database.
Advanced form: separate stores, with read models (denormalized tables, Elasticsearch, Redis) updated from events asynchronously.
Cost: eventual consistency between sides, more code, and duplicated models.
⚠ Follow-up traps
Does CQRS require event sourcing? No; they pair well but are independent.
Is read-your-writes guaranteed? Not with an asynchronous read side.
#cqrs#architecture
Q33
What is Event Sourcing and how does it relate to CQRS?
advanced
Event Sourcing stores state as an append-only sequence of domain events; current state is derived by replaying events (optionally from snapshots). Projections build read models for the query side of CQRS.
Gives a complete audit trail and temporal queries.
Challenges: event schema evolution (upcasting), replay time, GDPR deletion, and eventual consistency.
⚠ Follow-up traps
Can you update an event? No, events are immutable; correct with compensating events.
When is it a bad fit? Simple CRUD domains with no audit or temporal needs.
#event-sourcing#cqrs
Q34
What is the Transactional Outbox pattern?
advanced
Outbox solves the dual-write problem: instead of updating the database and publishing to a broker separately, write the business change and an outbox row in one local transaction; a relay later publishes outbox rows to the broker.
Relay options: polling publisher, or CDC (Debezium) tailing the DB log.
Delivery is at-least-once, so consumers must be idempotent.
Table typically has id, aggregate id, type, payload, created_at, published flag.
⚠ Follow-up traps
Why not publish inside the transaction? If the commit fails after publishing (or vice versa) the two systems diverge.
Does outbox give exactly-once? No, at-least-once plus idempotent consumers.
#outbox#messaging#consistency
Q35
What is the Saga pattern?
advanced
A saga is a sequence of local transactions across services, where each step has a compensating action to undo it if a later step fails. It replaces distributed 2PC with eventual consistency.
Choreography: services react to each other's events; simple but hard to follow with many steps.
Orchestration: a central coordinator issues commands and tracks state; clearer, but a coordinator to maintain.
Compensations are semantic undo (refund, release stock), not rollbacks, and must be idempotent and retriable.
⚠ Follow-up traps
Is a saga isolated? No, intermediate states are visible; use semantic locks or pending statuses.
What if a compensation fails? Retry until success or escalate to manual handling.
#saga#distributed-transactions
Q36
How does the Circuit Breaker pattern work?
intermediate
A circuit breaker wraps calls to a remote dependency and tracks failures. States: CLOSED (calls flow), OPEN (calls fail fast), HALF_OPEN (a few trial calls decide whether to close again).
Opens when failure or slow-call rate crosses a threshold in a sliding window; waits waitDurationInOpenState before half-open.
Libraries: Resilience4j (@CircuitBreaker), Spring Cloud CircuitBreaker; Hystrix is retired.
Combine with timeouts, retries with backoff, bulkheads and fallbacks.
⚠ Follow-up traps
Does a breaker replace timeouts? No, without timeouts calls hang before they even count as failures.
Should business errors (HTTP 400) trip it? Usually not; configure ignored exceptions.
#circuit-breaker#resilience
Q37
What are Bulkhead and Retry patterns, and how do they interact with circuit breakers?
intermediate
Bulkhead isolates resources per dependency (separate thread pools or semaphores) so one slow service cannot exhaust all threads. Retry repeats transient failures, ideally with exponential backoff and jitter.
Order of decorators in Resilience4j aspects: Retry( CircuitBreaker( RateLimiter( TimeLimiter( Bulkhead( fn ))))).
Retry only idempotent operations, or use idempotency keys.
Retry storms multiply load on a struggling service; cap attempts and add jitter.
⚠ Follow-up traps
Should you retry non-idempotent POSTs? Only with an idempotency key.
Is retry inside or outside the breaker? Retry wraps the breaker so that an open circuit short-circuits retries quickly.
#bulkhead#retry#resilience
Q38
What is idempotency and how do you implement idempotent APIs?
intermediate
An operation is idempotent if repeating it has the same effect as doing it once. GET, PUT, DELETE are idempotent by HTTP semantics; POST is not, so APIs accept an Idempotency-Key header.
Store key plus request fingerprint and response in a table with a unique constraint; on replay return the stored response.
Use INSERT ... ON CONFLICT DO NOTHING or unique index to make the check atomic.
Expire keys after a retention window.
⚠ Follow-up traps
Same key with a different body? Reject with 409/422; it signals a client bug.
Is a check-then-insert in application code safe? No, race conditions; rely on a unique constraint.
#idempotency#api#reliability
Q39
What is the Specification pattern?
intermediate
A Specification encapsulates a business rule as an object that answers isSatisfiedBy(candidate) and can be combined with and/or/not. It keeps query and validation criteria reusable.
Spring Data JPA Specification<T> builds dynamic Criteria predicates and composes with and()/or().
Useful for search filters with optional parameters.
⚠ Follow-up traps
Why not stack optional if clauses in a JPQL string? Hard to compose and test and prone to injection or syntax errors.
Does it avoid N+1? No, fetch strategy is separate.
#specification#query#ddd
Q40
What is the Null Object pattern?
basic
A Null Object implements the interface with neutral do-nothing behavior so callers need not check for null.
Examples: Collections.emptyList(), Optional.empty(), NoOpCacheManager, a NoopMetrics implementation.
Use Optional for return types that may be absent; use Null Object where a collaborator is optional.
Avoid when silent no-op hides an actual error.
⚠ Follow-up traps
Should Optional be a field or parameter? Generally no; use it as a return type.
Does Null Object hide bugs? It can; log or assert in critical paths.
#null-object#optional
Q41
Compare patterns with their functional alternatives in modern Java.
intermediate
Many behavioral patterns shrink to functions when the participant has one method.
Strategy: Function/Comparator lambda instead of a class hierarchy.
Command: Runnable/Callable/Supplier.
Template Method: pass step functions or use callbacks (JdbcTemplate row mappers).
Observer: Consumer listeners or Flow.Subscriber.
Decorator: function composition (Function.andThen, Predicate.and).
Visitor: sealed types plus pattern-matching switch.
Builder: records with with-style copy methods.
Keep classes when behavior needs state, names, several methods, or independent testing and discoverability.
⚠ Follow-up traps
Are lambdas always better? No, a named class documents intent and can be injected, configured, and given identity.
Lambda capturing mutable state? Captured locals must be effectively final; mutable shared captures create races.
#functional#lambdas#patterns
Q42
What are common design pattern anti-patterns?
intermediate
God object: one class doing everything.
Anemic domain model: entities with only getters/setters and all logic in services.
Singleton abuse: global mutable state.
Service Locator: hidden dependencies via static lookup.
Golden hammer: applying one pattern everywhere.
Spaghetti code and lava flow: tangled or dead code left in place.
Premature abstraction: interfaces with single implementations and factories for trivial objects.
Distributed monolith: microservices that must deploy together.
Shared database across services.
⚠ Follow-up traps
Is the anemic model always bad? Not for simple CRUD; it hurts when rich invariants exist.
Is an interface per class an anti-pattern? Often needless indirection; add the interface when a second implementation or a seam is needed.
#anti-patterns#design
Q43
Explain SOLID briefly with a backend example for each principle.
basic
S, Single Responsibility: separate order pricing from order persistence.
O, Open/Closed: add a new payment method by adding a class, not editing a switch.
L, Liskov Substitution: subclasses must honor the base contract (a read-only list that throws on add violates it).
I, Interface Segregation: small role interfaces instead of a fat Repository.
D, Dependency Inversion: services depend on PaymentGateway, not on StripeClient.
⚠ Follow-up traps
Is Collections.unmodifiableList an LSP violation? Arguably yes, since List permits mutation operations as optional; hence the immutable List.of still throws.
DIP vs DI? DIP is the design principle; DI is a technique to realize it.
#solid#principles
Q44
How do Spring Data repositories work internally?
advanced
Spring creates a JDK proxy for each repository interface at startup. Calls route to SimpleJpaRepository for CRUD methods and to query lookup strategies for derived names and @Query methods, all wrapped in transactional interceptors.
Method names like findByEmailAndActiveTrue are parsed into a PartTree and converted to criteria queries.
Default repository methods are @Transactional(readOnly = true) for reads and @Transactional for writes.
⚠ Follow-up traps
Is there an implementation class you wrote? No, only the interface (plus optional custom fragments).
Which patterns combined? Proxy, Repository, Strategy for query lookup, and Template Method in the base class.
#spring-data#proxy#repository
Q45
What is the Mediator pattern and how does it differ from Observer?
intermediate
Mediator centralizes communication between components so they interact with the mediator instead of each other, reducing many-to-many coupling.
Examples: DispatcherServlet mediating controllers and views, ApplicationEventPublisher as an event bus, in-app command buses.
Observer defines a subject-to-many direction; Mediator coordinates two-way interactions and may contain routing logic.
⚠ Follow-up traps
Risk? The mediator can become a god object.
Mediator vs message broker? A broker is a distributed, durable mediator.
#mediator#behavioral
Q46
What is the Iterator pattern and how does it appear in Java?
basic
Iterator provides sequential access to a collection's elements without exposing its representation. In Java: Iterator<E>, Iterable for the for-each loop, and Stream for pipelines.
Custom collections implement Iterable<T> and return an Iterator with hasNext/next.
Most JDK iterators are fail-fast and throw ConcurrentModificationException on structural changes outside the iterator.
⚠ Follow-up traps
Can you reuse a Stream like an Iterable? No, a stream is consumed once.
Does Iterator.remove() work safely? Yes, it is the sanctioned way to remove during iteration.
#iterator#behavioral
Q47
What is the Memento and Snapshot idea in backend systems?
advanced
Memento captures an object's internal state externally so it can be restored later without breaking encapsulation. In backends this appears as aggregate snapshots in event sourcing, saga state persistence and undo/audit versions.
A snapshot is stored every N events to bound replay time.
Keep mementos immutable; the originator creates and restores them, other classes treat them as opaque.
⚠ Follow-up traps
Memento vs serialization? Serialization can implement a memento but also exposes internals if misused.
Memory cost? Large or frequent snapshots grow quickly; prune them.
#memento#snapshot#event-sourcing
Q48
What is the Front Controller pattern?
basic
A single handler receives all requests and dispatches them to appropriate handlers, centralizing cross-cutting concerns such as security and routing. Spring MVC's DispatcherServlet is the canonical implementation.
Flow: HandlerMapping finds the handler, HandlerAdapter invokes it, HttpMessageConverter or ViewResolver renders the result, and exception resolvers handle errors.
Interceptors and filters wrap this flow.
⚠ Follow-up traps
Is there one DispatcherServlet per app? Typically one, though multiple can be registered.
Filter vs interceptor? Filters are servlet-level and see raw requests; interceptors are Spring MVC-level and know the handler.
#front-controller#spring-mvc
Q49
What is the Flyweight pattern and where is it in the JDK?
intermediate
Flyweight shares immutable intrinsic state among many objects to save memory, keeping extrinsic state outside.
JDK: the Integer.valueOf cache for -128..127, the String pool, Boolean.TRUE, enum constants.
Backend: caching immutable reference data such as currency or country objects instead of loading them per request.
⚠ Follow-up traps
Why does Integer a=127, b=127; a==b hold but not for 128? The cache covers -128 to 127; beyond that new objects are created.
Can flyweights be mutable? No, sharing requires immutability.
#flyweight#structural#memory
Q50
How does the Gateway/Anti-Corruption Layer pattern protect your domain?
advanced
An Anti-Corruption Layer translates between your domain model and an external or legacy model, so the foreign concepts and naming never leak in. It combines Adapter, Facade and mappers.
Implement as a CustomerGateway interface in the domain with an implementation that calls the legacy API and converts its DTOs.
Isolates changes: a vendor API version bump touches only the ACL.
⚠ Follow-up traps
Is it overkill for a tiny integration? Possibly; the benefit grows with the foreign model's size and volatility.
Where should the interface live? In your domain layer (dependency inversion), with the implementation in infrastructure.
#gateway#acl#ddd#adapter
Q51
What is the Builder vs Factory vs Prototype decision rule?
basic
Factory: choose which type to create based on input, hiding the constructor.
Builder: assemble one complex object step by step with many optional parts.
Prototype: copy an existing configured instance.
Abstract Factory: create consistent families of objects.
Singleton: guarantee exactly one instance.
⚠ Follow-up traps
Can a builder and factory be combined? Yes, a factory can return a pre-configured builder.
Telescoping constructors versus builder? Builder wins beyond about four parameters, especially optional ones.
#creational#comparison
Q52
Why does the Double-Checked Locking pattern need `volatile`, and what is safe publication?
advanced
instance = new Config() involves allocate, construct, assign the reference. Without a happens-before edge the JIT or CPU may publish the reference before construction finishes, so another thread sees a half-built object. volatile forbids that reordering.
Immutable objects with only final fields are safely published even through a data race (final field semantics).
Other safe publication: static initializers, synchronized, volatile, or concurrent collections.
⚠ Follow-up traps
Does final on all fields remove the need for volatile? For visibility of that object's fields, yes, but publication still races on which reference is seen (null vs non-null), which is harmless for the pattern.
Is holder idiom preferable? Usually, since it needs no explicit synchronization.
#singleton#jmm#safe-publication
Scenarios
Q53
Your lazily initialized singleton occasionally returns an object with null fields under load. What is wrong?
intermediate
Almost certainly double-checked locking without volatile: a thread sees a non-null reference before the constructor's writes are visible. Mark the field volatile, or switch to the holder idiom or an eager static final.
It reproduces rarely and only on multi-core hardware or after JIT optimization, which is why it passes tests.
⚠ Follow-up traps
Would adding synchronized to the constructor help? No, the race is on publication of the reference, not inside the constructor.
Does making the fields final fix it? It guarantees field visibility but volatile or the holder is still cleaner.
#singleton#concurrency#debugging
Q54
A Spring singleton service stores the current user in a field and users see each other's data. Why, and what is the fix?
basic
Singleton beans are shared by every request thread, so a mutable field is shared state. Remove the field and pass the user as a method parameter or read it from SecurityContextHolder; for per-request state use a request-scoped bean with a scoped proxy.
ThreadLocal works but must be cleared in finally on pooled threads.
⚠ Follow-up traps
Does marking the method synchronized fix it? It serializes requests and still shares state incorrectly.
What about virtual threads? The same sharing problem; ThreadLocal is supported but heavy use is discouraged, prefer ScopedValue (preview in 21).
#spring#singleton#thread-safety
Q55
You have a payment flow with Stripe, PayPal and a bank transfer, plus a growing if/else on the provider name. Which pattern, and how in Spring?
basic
Strategy with a registry. Define PaymentProvider with String id() and charge(...), implement per provider as beans, and inject List<PaymentProvider> or Map<String, PaymentProvider> into a router that picks by id.
@Serviceclass PaymentRouter { private final Map<String, PaymentProvider> byId; PaymentRouter(List<PaymentProvider> all) { byId = all.stream().collect(Collectors.toMap(PaymentProvider::id, p -> p)); } PaymentProvider forId(String id) { var p = byId.get(id); if (p == null) throw new IllegalArgumentException("Unknown provider " + id); return p; }}
Adding a provider means adding a bean; no existing code changes.
⚠ Follow-up traps
Duplicate ids?toMap throws IllegalStateException at startup, which fails fast.
Can Spring inject by bean name instead? Yes via Map<String, PaymentProvider>, where keys are bean names.
#strategy#spring#open-closed
Q56
A report builder has 14 constructor parameters, 9 optional. Callers pass wrong arguments in wrong order. What do you do?
basic
Introduce a Builder (or a record with a compact constructor plus a builder) with named setters, defaults for optional fields, and validation in build(). Group related params into value objects (DateRange, Paging) first; fewer parameters often removes the need.
⚠ Follow-up traps
Why not setters on a mutable object? The object can be observed half-configured and is not thread-safe.
Lombok @Builder with required fields? It does not enforce them by default; validate in a custom build() or constructor.
#builder#refactoring
Q57
Product wants audit logging, metrics and caching around an existing `PriceService` without touching its code. Which pattern and tradeoffs?
intermediate
Decorators (one wrapper per concern implementing PriceService) or Spring AOP aspects for cross-cutting concerns applied declaratively. Use explicit decorators when behavior is specific to one interface and order must be visible; use aspects when the same concern applies to many beans.
Order matters: caching outside audit logs hits only on misses; metrics outside caching includes cache hits.
Spring: @Order on aspects or @Primary on the outermost decorator.
⚠ Follow-up traps
Aspect not firing on internal calls? Self-invocation bypasses the proxy.
Will @Cacheable apply to a private method? No.
#decorator#proxy#aop
Q58
After adding `@Transactional` to a method called from another method in the same class, no transaction is created. Why?
intermediate
Self-invocation bypasses the Spring proxy, so the interceptor never runs. Extract the method to a separate bean, or inject the proxy via @Lazy self-reference, or use TransactionTemplate programmatically.
⚠ Follow-up traps
Expected behaviour of REQUIRES_NEW in same class call? Ignored for the same reason; it joins whatever outer transaction exists.
Does it work if the caller is in another bean? Yes, because the call goes through the proxy.
#proxy#spring#transactional
Q59
`this` inside a bean differs from the injected reference and `==` comparisons fail. Explain.
advanced
Injected references are proxies; this is the raw target. proxy == this is false and proxy.equals(this) depends on implementation. Avoid identity comparisons on beans, and compare by id or business key.
AopProxyUtils.getSingletonTarget or AopUtils.isAopProxy reveal proxies.
Hibernate entity proxies have the same issue: use Hibernate.unproxy or compare by id, and avoid getClass() in equals.
⚠ Follow-up traps
Does getClass() equal the target class on a CGLIB proxy? No, it returns a generated subclass (Foo$$SpringCGLIB$$0).
Why does instanceof still work? CGLIB proxy extends the target class.
#proxy#spring-aop#identity
Q60
Your `@Async` method runs synchronously. What pattern-level reasons exist?
intermediate
Typical causes: self-invocation, missing @EnableAsync, calling a private/final method, or the bean being created outside Spring (new). @Async is applied via a proxy that submits the call to a TaskExecutor.
Provide a bounded executor bean; the default SimpleAsyncTaskExecutor creates a thread per task (Spring Boot auto-configures a pooled applicationTaskExecutor).
⚠ Follow-up traps
Does the exception propagate to the caller? Not for void methods; use AsyncUncaughtExceptionHandler or return CompletableFuture.
Does the transaction context carry over? No, the new thread has no transaction.
#proxy#async#spring
Q61
A downstream inventory service is slow; your threads pile up and the whole API goes down. What patterns apply?
intermediate
Layer protections: client timeouts (connect and read), a circuit breaker to fail fast, a bulkhead to cap concurrent calls to that dependency, and a fallback (cached or degraded response).
Why not just retry? Retries add load to an already degraded service.
Fallback that calls another service? It may cascade failure; keep fallbacks local and cheap.
#circuit-breaker#bulkhead#timeout
Q62
Your order service saves an order then publishes `OrderCreated` to Kafka; sometimes the event is lost or sent for a rolled-back order. Fix it.
advanced
Use the Transactional Outbox: insert the order and an outbox row in the same DB transaction; a poller or Debezium CDC publishes rows to Kafka and marks them sent. Consumers dedupe by event id.
CREATE TABLE outbox ( id UUID PRIMARY KEY, aggregate_id VARCHAR(64) NOT NULL, type VARCHAR(64) NOT NULL, payload JSONB NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), published_at TIMESTAMP NULL);
⚠ Follow-up traps
Would @TransactionalEventListener(AFTER_COMMIT) suffice? No, a crash after commit but before the send loses the event.
Ordering per aggregate? Partition Kafka by aggregate id and publish outbox rows in id/created order.
#outbox#kafka#consistency
Q63
A checkout spans order, payment, and inventory services each with their own DB. Choose and justify an approach.
advanced
Use an orchestrated saga: Order service starts, reserves inventory, charges payment; on failure it issues compensations (release stock, cancel order). Orchestration is preferable with 3+ steps because the flow and failure paths are explicit and observable.
Each step is a local transaction plus outbox event; every command and compensation is idempotent.
Model intermediate states (PENDING, RESERVED, PAID, CANCELLED) to expose in-flight status.
Avoid 2PC/XA: low availability and poor broker/NoSQL support.
⚠ Follow-up traps
Payment succeeded but the reply was lost? Orchestrator retries with the same idempotency key and payment returns the earlier result.
Can you "roll back" an email sent in step 2? No, design order of steps so irreversible actions come last.
#saga#distributed-transactions
Q64
Clients retry `POST /payments` after timeouts and customers are double charged. How do you fix it?
intermediate
Require an Idempotency-Key header. In one transaction insert the key (unique constraint) with status IN_PROGRESS, execute, then store the response. Replays with a completed key return the stored response; concurrent duplicates get 409 or wait.
Also pass the key to the payment provider, which supports its own idempotency.
⚠ Follow-up traps
Process crashes between charge and storing response? Idempotency key at the provider makes the retry safe.
How long to keep keys? Longer than the maximum client retry horizon, commonly 24 hours or more.
#idempotency#payments
Q65
A Kafka consumer processes the same message twice after a rebalance. How do you keep side effects correct?
intermediate
Assume at-least-once delivery and make the handler idempotent: store processed message ids in the same transaction as the business change (unique constraint), or design updates as upserts or absolute-state writes rather than increments.
⚠ Follow-up traps
Does Kafka exactly-once semantics remove this? It covers Kafka-to-Kafka with transactions; external DB or API side effects still need idempotency.
Commit offset before or after processing? After, to avoid loss; duplicates are then handled by idempotency.
#idempotency#kafka#consumer
Q66
A controller returns JPA entities and sometimes throws `LazyInitializationException` or leaks `passwordHash`. What do you change?
basic
Return DTOs (records) built in the service layer inside the transaction, or use projections to select only needed fields. Never expose entities across the API boundary.
Does spring.jpa.open-in-view=false solve leaking? It prevents lazy loading in the view layer but not over-exposure.
@JsonIgnore on entity? Works but couples persistence and API concerns.
#dto#jpa#api
Q67
Reads dominate (95%) and a complex report query is slowing writes on the same tables. Which pattern?
advanced
CQRS with a denormalized read model: keep writes on the normalized transactional tables, and project events (via outbox/CDC) into a read store such as a materialized view, replica, Elasticsearch or Redis. Start with read replicas or materialized views before full separation.
Accept eventual consistency and communicate it in the UX (for example "updating...").
Rebuildable projections let you reshape read models later.
⚠ Follow-up traps
User saves then immediately reads stale data? Read from the write side for that session or use version/etag waits.
When is CQRS overkill? Simple CRUD with modest load.
#cqrs#read-model#scaling
Q68
You must support 5 notification channels with different retry rules but a common flow (build message, send, record). Pattern?
intermediate
Template Method for the fixed flow with channel-specific hooks, or better, composition: a NotificationService with the flow, and a ChannelSender strategy per channel exposing send and retryPolicy(). Prefer composition because channels vary in more than one dimension.
⚠ Follow-up traps
When is Template Method still right? When the skeleton is stable and variation is only in a few protected steps.
Spring-friendly wiring? Inject List<ChannelSender> and index by channel.
#template-method#strategy
Q69
A rules engine evaluates hundreds of nested business conditions (AND/OR/NOT). How do you structure it?
intermediate
Composite plus Specification: a Rule interface with AndRule, OrRule, NotRule composites and leaf rules. Rules are data-driven (loaded from config), testable individually, and evaluated recursively. With Predicate<T> you get and/or/negate for free.
Deeply nested configs? Add a depth limit to prevent stack overflow.
#composite#specification
Q70
A legacy SOAP vendor returns awkward models; the codebase is filling with their field names. What do you do?
intermediate
Introduce an anti-corruption layer: a domain-owned interface (ShippingGateway) with an adapter in infrastructure that calls the SOAP client and maps vendor DTOs to domain types. Business code never imports vendor classes.
⚠ Follow-up traps
Mapping overhead worth it? Yes when the vendor model is large or unstable; you can swap vendors by replacing one adapter.
How to test? Contract tests against the adapter and fake gateway in service tests.
#adapter#acl#integration
Q71
An approval workflow has states DRAFT, SUBMITTED, APPROVED, REJECTED with a switch scattered across 12 methods. Refactor.
intermediate
Replace conditionals with State: an enum or sealed state types that own allowed transitions and behavior. The aggregate holds the state and delegates submit(), approve(); illegal transitions throw a domain exception.
Persist as @Enumerated(EnumType.STRING), not ordinal.
For complex flows with guards and events, consider Spring Statemachine.
⚠ Follow-up traps
Why EnumType.STRING? Reordering enum constants changes ordinals and corrupts data.
Concurrent transitions? Use optimistic locking (@Version) so two actors cannot both transition.
#state#refactoring#enum
Q72
You need undo/redo and a persisted audit of admin actions. Which pattern?
intermediate
Command: model each action as a command object with execute() and undo() (or a compensating command), persist command records with actor, time and payload, and store inverse data needed to undo.
⚠ Follow-up traps
Undo for external side effects? Only possible via compensating actions; some operations (sent email) cannot be undone.
Event sourcing alternative? Gives audit and replay by design but at higher complexity.
#command#audit
Q73
A servlet application needs request logging, auth, rate limiting, and CORS in a configurable order. Pattern and Spring tool?
basic
Chain of Responsibility via servlet filters or a Spring Security SecurityFilterChain. Each filter does its work and calls chain.doFilter. Control order using FilterRegistrationBean.setOrder or @Order.
⚠ Follow-up traps
Filter forgot chain.doFilter? The request hangs or returns an empty response; the rest of the chain never runs.
CORS after auth? CORS preflight must be handled before authentication or it gets 401.
#chain-of-responsibility#filters
Q74
Many components must react when an order is paid (email, loyalty points, analytics). The order service imports all of them. Fix.
basic
Publish an OrderPaidEvent via ApplicationEventPublisher; each component has an @EventListener (or @TransactionalEventListener(AFTER_COMMIT)). The order service no longer knows the subscribers.
For cross-service delivery use a broker (outbox plus Kafka).
Use @Async for slow listeners and handle their errors.
⚠ Follow-up traps
A listener throws, what happens to the payment transaction? With a plain synchronous @EventListener, the exception propagates and can roll back the publisher's transaction.
AFTER_COMMIT listener writes to DB? Needs REQUIRES_NEW, because the original transaction is already completed.
#observer#events#decoupling
Q75
You must open a connection per tenant database and pick the right one per request. Which patterns and how?
advanced
Strategy for tenant resolution (header, subdomain, JWT claim) plus a routing data source: AbstractRoutingDataSource looks up the tenant key (stored in a ThreadLocal/request scope) and returns the matching DataSource, a Factory/registry creating pools lazily.
Clear the tenant context in finally; otherwise pooled threads leak the previous tenant.
Bound the number of pools (Hikari per tenant is costly); prefer schema or discriminator column at scale.
⚠ Follow-up traps
Async tasks lose the tenant? Yes, propagate context with a TaskDecorator.
Risk of cross-tenant leakage? High if context is not reset; add integration tests for it.
#strategy#factory#multitenancy
Q76
A prototype-scoped bean injected into a singleton always returns the same instance. Why?
intermediate
The singleton is created once, so its injection point is resolved once. Inject ObjectProvider<MyPrototype> and call getObject() per use, or use @Lookup, or a scoped proxy.
⚠ Follow-up traps
Does @Scope("prototype") bean get destroyed by Spring? No, @PreDestroy is not called.
Is Provider<T> (JSR-330) acceptable? Yes, it works equivalently.
#prototype#spring#scopes
Q77
Writing JDBC code, developers forget to close connections. Which pattern fixes the root cause?
basic
Template Method/callback: JdbcTemplate acquires and releases connections, statements and result sets and translates exceptions; you only supply SQL and a RowMapper. Plain JDBC alternative is try-with-resources.
List<String> names = jdbc.query("select name from users where active = ?", (rs, i) -> rs.getString("name"), true);
⚠ Follow-up traps
Does JdbcTemplate hold connections between calls? No, it borrows from the pool per operation or uses the transaction-bound connection.
Checked SQLException? Translated to unchecked DataAccessException hierarchy.
#template-method#jdbc#resources
Q78
A team creates `UserServiceImpl` as the lone implementation of `UserService` for every service. Opinion?
intermediate
Single-implementation interfaces by habit add navigation overhead without benefit. Introduce an interface when you need a seam: multiple implementations, a boundary (ports and adapters), or test doubles that cannot be done with a mock of the class.
Mockito can mock classes, so testing alone no longer justifies interfaces.
Keep interfaces at module boundaries and for strategies.
⚠ Follow-up traps
Do Spring proxies need interfaces? No, CGLIB handles classes (default in Boot).
Is it wrong for public library APIs? There an interface helps evolution and compatibility.
#anti-patterns#abstraction
Q79
Two threads create separate instances of a "singleton" in a Spring application. How?
advanced
Possible reasons: multiple ApplicationContexts (parent/child, test contexts), the same class loaded by different class loaders (app servers, plugins), new Foo() bypassing the container, or a prototype scope mistake. A classic static singleton is per class loader; a Spring singleton is per context.
⚠ Follow-up traps
Does Spring's singleton scope guarantee one per JVM? No.
Different test contexts? Different @MockBean sets create separate cached contexts.
#singleton#classloading#spring
Q80
Your Spring Boot app now fails at startup with a circular reference after upgrading to 2.6+. Options?
intermediate
Refactor: extract the shared logic into a third bean, move one dependency to an event, or invert the dependency. Quick fix: @Lazy on one injection point. Last resort: spring.main.allow-circular-references=true.
⚠ Follow-up traps
Why was it changed? Cycles indicate design smells and caused subtle init-order bugs.
Does constructor injection fail on cycles? Always, irrespective of the property.
#di#circular-dependency#spring-boot
Q81
Choose between choreography and orchestration for a 6-step onboarding saga across teams.
advanced
Use orchestration for 6 steps: a single owner holds the state machine, timeouts and compensations, and operators can see where each instance is stuck. Choreography with 6 services creates implicit cyclic coupling and hidden flow.
Use an engine such as Temporal, Camunda or a hand-rolled persistent state machine.
Choreography remains good for 2 to 3 loosely related reactions.
⚠ Follow-up traps
Orchestrator as a single point of failure? It must be persistent and horizontally scalable; state lives in a DB.
Who owns the orchestrator? A clear team, otherwise it becomes a bottleneck.
#saga#choreography#orchestration
Q82
A visitor over 25 AST node types is painful to extend. What do you use on Java 21?
advanced
Model nodes as a sealed interface with records and write operations as switch pattern-matching functions. The compiler checks exhaustiveness: adding a node type breaks every non-exhaustive switch at compile time, which is exactly the visitor's safety, with less boilerplate.
⚠ Follow-up traps
Is a default branch a good idea? No, it disables exhaustiveness checking for new subtypes.
When keep a classic visitor? Cross-module libraries targeting older Java or needing open-ended operation sets.
#visitor#sealed#pattern-matching
Q83
Your service calls a flaky partner API; retries made things worse during their outage. What design?
intermediate
Retries with exponential backoff, jitter and a small attempt cap, wrapped by a circuit breaker so retries stop when the partner is clearly down, plus timeouts and a retry budget. Retry only on transient errors (5xx, timeouts, 429 with Retry-After), not 4xx.
⚠ Follow-up traps
Why jitter? Without it clients retry in sync and cause thundering herd.
Non-idempotent operation? Retry only with an idempotency key.
#retry#backoff#circuit-breaker
Q84
A product catalog is read from DB on every request but changes daily. Which pattern, and what trap do you avoid?
basic
Caching decorator/proxy: @Cacheable("catalog") on the service method (Spring cache abstraction) with Caffeine or Redis, and @CacheEvict on updates. Set TTL and size bounds.
⚠ Follow-up traps
Cache stampede on expiry? Use sync = true, request coalescing, or jittered TTLs.
You must load a heavy `Report` object only when its content is accessed. Pattern?
basic
Virtual proxy: a lightweight proxy holds the id and loads the real object on first method call. JPA does this with @ManyToOne(fetch = LAZY) and getReference(); in plain code use a Supplier memoized with Lazy-style holder.
⚠ Follow-up traps
What breaks it? Accessing it outside the persistence context throws LazyInitializationException.
EAGER default on @ManyToOne? Yes, so set LAZY explicitly.
#proxy#lazy-loading
Q86
In a microservice you want clients not to know which of three internal services handles `/orders/{id}/summary`. Pattern?
intermediate
API Gateway/Facade (or Backend-for-Frontend) that aggregates the calls and exposes one coarse endpoint. Make downstream calls in parallel (CompletableFuture or virtual threads) with timeouts and partial-response fallbacks.
⚠ Follow-up traps
Business logic in the gateway? Avoid; keep it orchestration and translation only.
One slow dependency? Apply per-call timeouts and return partial data rather than failing all.
#facade#api-gateway#aggregation
Q87
A builder-created object must be immutable, but one field is a `List`. What can go wrong?
intermediate
If the builder passes its mutable list reference to the object, later builder or caller mutations change the "immutable" object. Copy defensively with List.copyOf(list) in the constructor, which also rejects nulls.
⚠ Follow-up traps
Collections.unmodifiableList enough? No, it is a view; the underlying list can still change.
Does List.copyOf always copy? It returns the same instance if the argument is already an immutable List.of collection.
#builder#immutability#defensive-copy
Q88
A configuration object loaded at boot must be shared by all threads and reloaded on change without locking readers. Design?
advanced
Hold an immutable Config in an AtomicReference<Config> (or a volatile field). A reloader builds a new instance and swaps it atomically; readers get a consistent snapshot lock-free. This is copy-on-write with a Singleton accessor.
⚠ Follow-up traps
Mutating fields of the current config instead? Readers could see partial updates.
Multiple reads in one request? Capture the reference once per request to get a consistent view.
#singleton#immutability#concurrency
Q89
Your DAO layer exposes 40 query methods growing weekly with combinations of filters. What pattern?
intermediate
Specification (Spring Data JpaSpecificationExecutor) or Querydsl predicates: build reusable filter fragments and combine at runtime. Return a Page<DTO> using a projection.
Combinatorial indexes? Specifications build the query only; design indexes for the actual patterns.
Null parameters? Make specs return null/conjunction when the filter is absent.
#specification#repository#query
Q90
An `EventListener` runs before the surrounding transaction commits and sends an email for an order that later rolls back. Fix.
intermediate
Use @TransactionalEventListener(phase = AFTER_COMMIT) so the listener runs only after a successful commit. For guaranteed delivery use the outbox, since a crash after commit still loses an in-memory event.
⚠ Follow-up traps
If no transaction is active? The @TransactionalEventListener is not invoked unless fallbackExecution = true.
Can it do DB writes? Only in a new transaction (REQUIRES_NEW).
#observer#transactions#events
Q91
A shared `SimpleDateFormat` singleton throws random parse errors in production. Cause and fix?
basic
SimpleDateFormat is mutable and not thread-safe; sharing it in a singleton corrupts its internal calendar. Use java.time.format.DateTimeFormatter, which is immutable and thread-safe, and can safely be a static constant.
⚠ Follow-up traps
ThreadLocal<SimpleDateFormat> fix? Works but unnecessary since Java 8.
Output of two threads parsing concurrently? Undefined: wrong dates or NumberFormatException.
#singleton#thread-safety#java-time
Q92
How would you implement a pluggable export (CSV, PDF, XLSX) where new formats come from separate jars?
intermediate
Strategy plus discovery: Exporter interface; formats register via ServiceLoader (META-INF/services) in plain Java, or as Spring beans collected by List<Exporter>. The router selects by supports(format).
⚠ Follow-up traps
ServiceLoader and class loaders? It uses the context class loader; container environments may need explicit loader passing.
Duplicate support for the same format? Define priority (@Order) or fail at startup.
#strategy#serviceloader#spring
Q93
Entities in a bidirectional relationship cause infinite recursion when serialized to JSON. Pattern-level fix?
basic
Do not serialize entities. Map to DTOs that contain only the necessary fields and ids of related entities. Annotations like @JsonManagedReference/@JsonBackReference or @JsonIgnore are patches that couple entities to the API.
⚠ Follow-up traps
Output without a fix?StackOverflowError or HttpMessageNotWritableException.
Mapping tool? MapStruct generates mappers at compile time.
#dto#serialization#jpa
Q94
A scheduled job runs on 3 instances and sends every email three times. What pattern fixes this?
intermediate
Ensure single execution via a leader/lock: ShedLock (DB or Redis lock per job name), or run jobs in one dedicated worker or use a queue so each unit is claimed once (SELECT ... FOR UPDATE SKIP LOCKED). Also make sending idempotent so duplicates are harmless.
⚠ Follow-up traps
Lock expires while the job is still running? Another instance starts; set lock duration above runtime or renew.
Is the lock enough for correctness? No, add idempotency since locks can fail under pauses (GC, clock skew).
#distributed-lock#idempotency#scheduling
Q95
You need to add a new payment method; the `switch` on `type` appears in 8 classes. What is the refactor and the risk?
intermediate
Replace conditional with polymorphism: move each branch into an implementation of one interface, with a factory or registry to select it. Do it incrementally with tests, starting with the most duplicated switch.
⚠ Follow-up traps
Leftover switch in the factory? Acceptable: one place instead of eight.
Sealed interface with switch instead? Also fine when the set of types is closed and owned.
#refactoring#polymorphism#strategy
Q96
A reactive subscriber is too slow for the publisher. Which pattern concept is missing?
advanced
Classic Observer pushes without regard for consumer speed. Reactive Streams (java.util.concurrent.Flow, Reactor) adds backpressure: the subscriber requests n items through Subscription.request(n) and the publisher never sends more.
⚠ Follow-up traps
Behavior with an unbounded request(Long.MAX_VALUE)? It disables backpressure.
Alternative for in-process queues? Bounded BlockingQueue blocks or rejects producers.
#observer#backpressure#reactive
Q97
You decorate `InputStream` as `new BufferedInputStream(new GZIPInputStream(new FileInputStream(f)))`. Which pattern, and what is the closing rule?
basic
Decorator. Closing the outermost stream closes the wrapped ones, so use try-with-resources on the outermost (or all) streams. Order matters: buffering outside decompression reads decompressed bytes in larger chunks.
⚠ Follow-up traps
Does closing the outer close the inner? Yes, by delegation in the standard decorators.
If the wrapper constructor throws? The inner stream leaks unless it was declared as its own resource.
#decorator#io
Q98
How would you design a plug-in discount engine where rules stack, have priorities and can be tested independently?
intermediate
A DiscountRule interface with apply(Cart, Price) returning a new price, held in an ordered list (chain/pipeline) sorted by @Order or priority. Each rule is a small, stateless bean; the engine folds the price through the list. Stop rules (exclusive discounts) can short-circuit.
⚠ Follow-up traps
Non-commutative rules? Order must be explicit and documented; test it.
Money type? Use BigDecimal or integer minor units, not double.
#chain-of-responsibility#strategy#design
Q99
Your read path uses a cache-aside pattern and shows stale data after updates. What do you check?
intermediate
Cache-aside means read: check cache then DB and populate; write: update DB then invalidate cache. Staleness arises from invalidating before the DB commit (a reader refills the old value), failed invalidations, or long TTLs. Invalidate after commit, set TTLs as a safety net, and use versioned keys or CDC-based invalidation for strictness.
⚠ Follow-up traps
Update the cache instead of delete? Concurrent writers can leave an older value; deletion is safer.
Cache penetration for missing ids? Cache negative results briefly or use a Bloom filter.
#cache-aside#consistency
Q100
In a hexagonal (ports and adapters) service where do patterns sit?
advanced
Ports are interfaces owned by the domain (inbound: use cases; outbound: OrderRepository, PaymentGateway). Adapters implement them (REST controller, JPA repository, Kafka publisher). Dependencies point inward so domain code imports no framework.
Uses Adapter, Repository, Strategy and Dependency Inversion together.
Pays off in long-lived, complex domains; simple CRUD services rarely need it.
⚠ Follow-up traps
Are JPA entities part of the domain? In strict form no, you map between domain and persistence models; pragmatic teams annotate domain entities.
Where does @Transactional go? On the application-service (use case) layer.
#hexagonal#ports-adapters#architecture
Q101
Two services must update the same customer record; optimistic updates keep failing. Pattern considerations?
advanced
Use optimistic locking (@Version), catch OptimisticLockingFailureException, reload and retry a bounded number of times (the retried unit of work must be re-read, not replayed on stale state). If contention is genuinely high, partition ownership (one writer service) or use pessimistic locks or atomic SQL updates.
⚠ Follow-up traps
Retrying inside the same transaction? Not valid; retry the whole transaction.
Expected exception for a stale update?ObjectOptimisticLockingFailureException in Spring Data JPA.
#optimistic-locking#retry#concurrency
Q102
Your team debates a `Util` class with 60 static methods. Assessment?
basic
A kitchen-sink utility class has low cohesion and static coupling that hampers testing. Split by concept into focused types (MoneyUtils, a Slugifier service), prefer instance behavior on the domain type, and use the JDK or Apache/Guava utilities rather than rewriting them.
⚠ Follow-up traps
Is every static method bad? No, pure stateless functions are fine.
Static helper that calls a database? That is a hidden dependency and should be an injected bean.
#anti-patterns#static#cohesion
Q103
A circuit breaker is stuck OPEN after the downstream recovered. What could be misconfigured?
advanced
Check waitDurationInOpenState (too long), automaticTransitionFromOpenToHalfOpenEnabled (without it the transition occurs only on the next call), permittedNumberOfCallsInHalfOpenState too small to prove recovery, slow-call thresholds too aggressive, or the fallback masking success so trial calls never succeed.
⚠ Follow-up traps
Does half-open allow all traffic? No, only the configured number of probe calls; others still fail fast.
Do ignored exceptions count as failures? No, ignored exceptions are neither success nor failure for the failure rate.
#circuit-breaker#resilience4j#troubleshooting
Q104
Choose a pattern: after an `Order` is created, calculation of tax, shipping and discount runs in a fixed order but each step has multiple implementations per country.
intermediate
A pipeline/Template Method fixes the sequence (tax -> shipping -> discount) in one orchestration class, and each step is a Strategy chosen by country from a registry. The skeleton stays stable while implementations vary per country, avoiding both a giant switch and an inheritance explosion.
⚠ Follow-up traps
Why not subclass per country? Variation on three axes would multiply subclasses.
Missing country implementation? Fail fast at startup with a validation of the registry, or provide a documented default strategy.