Design Patterns Interview Questions

Top Design Patterns Interview Questions

Apoorva
October 24th, 2026
11
15:00 Minutes

Design patterns come up in almost every software developer interview. Interviewers use them to check how you think about code, not just how you write it. Strong answers explain the problem, the solution and the trade-offs.

This blog covers the most common design patterns interview questions. It starts with the basics for freshers. It then moves to intermediate topics, experienced-level design questions and real-world scenarios. Each answer is short, clear and ready to use in an interview.

Let’s start!

Design Patterns Interview Questions for Freshers

Freshers usually face basic questions on definitions, principles, and pattern categories. Interviewers want to see that you understand the “why” behind patterns. Use the answers below to build a strong foundation.

1. What are design patterns?

What are design patterns?

Design patterns are reusable, high-level blueprints that help developers solve recurring problems in software design. Rather than providing a direct copy-and-paste code snippet, they offer a standard template or architecture that you can adapt to fit your specific program.

It makes your code more organized, maintainable, flexible, and easier to understand. They are especially useful in object-oriented programming, where developers need to manage relationships between classes and objects.

2. How are design patterns different from algorithms?

Design patterns and algorithms solve different types of problems in software development.

A design pattern provides a reusable approach to structuring classes, objects and their interactions. An algorithm provides a sequence of steps to solve a specific computational problem.

For example, the Strategy pattern allows us to switch between different payment methods without changing the main payment processing logic. In contrast, a sorting algorithm, such as Quick Sort, arranges elements in a particular order.

The main difference is that design patterns focus on software structure and object interaction, while algorithms focus on the steps required to solve a problem. Both are important for writing efficient and maintainable code.

3. How are design principles different from design patterns?

Design principles are guidelines that help developers write clean, flexible and maintainable code. Design patterns are reusable approaches that help developers solve common software design problems.

For example, the Open-Closed Principle states that software entities should be open for extension but closed for modification. This means developers can add new functionality without repeatedly changing existing, stable code.

The Strategy pattern helps achieve this principle by letting developers introduce new algorithms or behaviors through separate classes.

In simple terms, design principles guide how we design software, while design patterns show us how to apply suitable solutions to recurring design problems. Following good design principles also helps us choose and implement design patterns effectively.

4. What are some of the design patterns used in Java’s JDK library?

The Java Development Kit (JDK) uses several design patterns to make its APIs flexible, reusable and easier to work with. Some common examples are:

Design patternJava examplePurpose
SingletonRuntime.getRuntime()Provides access to a shared runtime instance.
Factory MethodCalendar.getInstance()Creates and returns an appropriate calendar instance.
ObserverPropertyChangeListener with PropertyChangeSupportNotifies registered listeners when a property changes.
IteratorIterator interfaceAllows sequential access to elements in a collection.
DecoratorBufferedInputStreamAdds buffering functionality to an existing input stream.
AdapterArrays.asList()Adapts an array to a list-style interface, with specific limitations.

These examples show how design patterns appear in everyday Java programming. However, not every API maps perfectly to one pattern in every implementation. Understanding the purpose of each example is more important than simply memorizing class names.

5. What do you understand by the Open-Closed Principle (OCP)?

The Open-Closed Principle, or OCP, is one of the five SOLID principles of object-oriented design. It states that software entities, such as classes and modules, should be open for extension but closed for modification.

This means developers should be able to add new functionality without making unnecessary changes to existing, stable code.

For example, imagine an application that supports credit card payments. Later, the business needs to support UPI payments as well.

Instead of adding more conditional statements to a single payment-processing class every time a new payment method is introduced, we can define a PaymentMethod interface and create separate implementations for credit cards and UPI.

When we introduce another payment method, we can add a new implementation without changing the core payment-processing logic.

This approach reduces the risk of breaking existing functionality and makes the application easier to maintain and extend. Design patterns such as Strategy can help implement the Open-Closed Principle.

6. What are the SOLID Principles?

SOLID is an acronym for five object-oriented design principles. These principles help developers create software that is easier to maintain, test, understand and extend.

What are the SOLID Principles?

The five SOLID principles are:

1. Single Responsibility Principle (SRP): A class should have one responsibility and one main reason to change. For example, an invoice class should not also handle email delivery.

2. Open-Closed Principle (OCP): Software entities should allow new functionality without requiring unnecessary changes to existing code. For example, we can add a new payment method by implementing an existing interface.

3. Liskov Substitution Principle (LSP): Objects of a subclass should be usable wherever objects of the parent type are expected without breaking the intended behavior. This helps ensure that inheritance works correctly.

4. Interface Segregation Principle (ISP): Clients should not be forced to depend on methods they do not use. For example, several smaller, focused interfaces may be better than one large interface that contains unrelated methods.

5. Dependency Inversion Principle (DIP): High-level modules should depend on abstractions rather than concrete implementations. For example, a notification service can depend on a messaging interface instead of being tightly coupled to one email provider.

Together, these principles encourage loosely coupled, modular and maintainable software. They also help developers decide when a design pattern can improve an application's structure.

7. What is the Gang of Four (GoF) in Design Patterns?

The Gang of Four, commonly known as GoF, refers to four software engineering authors: Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.

They wrote the influential book Design Patterns: Elements of Reusable Object-Oriented Software, published in 1994. The book documented 23 classic design patterns that address recurring problems in object-oriented software design.

These patterns are divided into three main categories:

  • Creational patterns: Focus on object creation. Examples include Factory Method, Abstract Factory, Builder, Singleton and Prototype.
  • Structural patterns: Focus on how classes and objects are organized. Examples include Adapter, Decorator, Facade and Composite.
  • Behavioral patterns: Focus on communication and responsibility between objects. Examples include Observer, Strategy, Command and Iterator.

The GoF patterns remain important because they provide a common vocabulary for discussing software architecture and reusable design solutions. Developers can use them to communicate design decisions more clearly and build maintainable applications.

8. What is Inversion of Control?

Inversion of Control, or IoC, is a software design principle in which control over certain tasks, such as creating objects or managing dependencies, is transferred from application code to an external framework or container.

Without IoC, a class may create its own dependencies directly. With IoC, an external component can provide those dependencies instead.

For example, imagine a notification service that needs an email sender. Without IoC, the service might create an EmailSender object inside its constructor. This makes the service tightly coupled to that specific implementation.

With IoC, the application can provide an implementation through a constructor parameter or an interface. The notification service no longer needs to create the dependency itself.

Frameworks such as Spring use IoC containers to manage objects and their dependencies. Dependency Injection is a common way to implement IoC.

The main benefit of Inversion of Control is that it reduces tight coupling and makes software easier to test, maintain and extend.

9. What are the types of design patterns in Java?

Design patterns in Java are commonly divided into three categories: creational, structural and behavioral. Each category addresses a different aspect of software design.

1. Creational design patterns

Creational patterns focus on how objects are created. They help developers manage object creation without unnecessarily coupling the application to specific classes.

Examples include:

  • Singleton: Ensures a class has a single instance within the intended scope.
  • Factory Method: Lets subclasses or implementations determine which object to create.
  • Abstract Factory: Provides an interface for creating families of related objects.
  • Builder: Constructs complex objects step by step.
  • Prototype: Creates new objects by copying existing instances.

2. Structural design patterns

Structural patterns focus on how classes and objects are combined to form larger structures.

Examples include:

  • Adapter: Makes incompatible interfaces work together.
  • Decorator: Adds behavior to an object without changing its original class.
  • Facade: Provides a simpler interface to a complex subsystem.
  • Composite: Organizes individual objects and groups of objects into a tree structure.
  • Proxy: Controls access to another object.

3. Behavioral design patterns

Behavioral patterns focus on communication, responsibilities and interactions between objects.

Examples include:

  • Observer: Notifies registered observers when an object changes.
  • Strategy: Allows interchangeable algorithms or behaviors.
  • Command: Encapsulates a request as an object.
  • Iterator: Provides a way to traverse a collection.
  • State: Changes an object's behavior based on its internal state.
  • Template Method: Defines the structure of an algorithm while allowing subclasses to customize certain steps.

Understanding these three categories helps Java developers select an appropriate design pattern based on the problem they need to solve, rather than applying patterns unnecessarily.

10. How can you describe a design pattern?

I would describe a design pattern by explaining the problem it solves, the context in which it is useful, its structure and the benefits and trade-offs of using it.

A well-documented design pattern generally includes the following elements:

  1. Pattern name: A short name that helps developers communicate the solution.
  2. Problem: The recurring design challenge that the pattern addresses.
  3. Context: The situation in which the problem occurs.
  4. Solution: The general structure of classes, objects and their interactions.
  5. Consequences: The benefits, limitations and trade-offs of applying the pattern.

For example, the Strategy pattern addresses situations where an application needs to support multiple interchangeable algorithms or behaviors.

The solution is to define a common interface and implement each behavior separately. The application can then select the appropriate implementation at runtime.

This design makes it easier to introduce new behaviors without placing all the logic inside one large class. However, it may introduce additional classes and complexity, so I would use it when the flexibility provides a real benefit.

In my view, a good understanding of design patterns means knowing not only their definitions but also when to use them, how to implement them and when a simpler solution would be more appropriate.

Design Patterns Interview Questions for Intermediate Developers

Intermediate questions go deeper into specific patterns. Interviewers expect you to give examples, name trade-offs and show when you would use each pattern. This section covers the patterns that appear most often.

1. What is the main advantage of using a Prototype Design Pattern over object creation using the new keyword?

The main advantage of the Prototype Design Pattern is that it allows us to create new objects by copying existing objects instead of creating them from scratch using the new keyword. This is useful when object creation is expensive or when we need multiple objects with similar configurations.

For example, suppose an application creates complex report objects with predefined settings, templates and formatting rules. Instead of initializing every report from scratch, we can create a prototype report and clone it whenever we need a similar report.

The Prototype Pattern reduces repeated initialization and makes object creation more flexible. However, we need to handle shallow and deep copying carefully, especially when objects contain references to mutable data.

2. What is Decorator Design Pattern?

The Decorator Design Pattern is a structural design pattern that allows us to add new functionality to an existing object without modifying its original class. It works by wrapping an object inside another object that follows the same interface.

For example, consider a coffee-ordering application. We can start with a basic coffee and add decorators for milk, sugar, or extra espresso. Each decorator adds functionality or cost without requiring separate classes for every possible combination.

The main advantage of the Decorator Pattern is that it follows the Open-Closed Principle. We can extend an object's behavior without modifying existing code. It is particularly useful when we need flexible combinations of features at runtime.

3. What is the Command Pattern?

What is the Command Pattern?

The Command Pattern is a behavioral design pattern that encapsulates a request as an object. It separates the object that requests an action from the object that performs it.

For example, in a text editor, actions such as copy, paste, undo and redo can be represented as command objects. Each command contains the logic required to execute a specific action.

This pattern is useful when we need to queue requests, maintain action history, support undo and redo operations, or schedule tasks for later execution. It also makes the application easier to extend because we can introduce new commands without changing the code that invokes them.

4. What is the Observer Design Pattern?

What is the Observer Design Pattern?

The Observer Design Pattern is a behavioral design pattern that establishes a one-to-many relationship between objects. When one object changes its state, all registered observers receive a notification about that change.

For example, consider a stock market application. When the price of a stock changes, the application can notify all users who have subscribed to updates for that stock. The stock object does not need to know how each user displays the notification.

The Observer Pattern reduces tight coupling between the object that produces an update and the objects that receive it. It is commonly used in event-driven applications, notification systems and user interface frameworks. However, we should manage subscriptions carefully to avoid memory leaks and unnecessary notifications.

5. What problem does the Builder Pattern try to solve?

The Builder Design Pattern solves the problem of creating complex objects that require multiple configuration options or construction steps. It avoids constructors with too many parameters and makes object creation easier to understand.

For example, suppose we are developing an application that creates user accounts with optional settings such as an email address, phone number, profile picture and notification preferences. A constructor with many parameters can become difficult to read and maintain.

With the Builder Pattern, we can set only the required properties and then call a final method to create the object. This makes object construction more readable and reduces errors caused by confusing parameter order.

The Builder Pattern is particularly useful for complex configuration objects, API request objects and immutable objects. It is not always necessary for simple objects with only a few fields.

6. What do you understand by the Null Object Pattern?

The Null Object Pattern is a behavioral design pattern that provides an object with default behavior instead of returning null. This allows the application to call methods on that object without repeatedly checking whether it exists.

For example, consider a notification system in which some users have not configured a notification service. Instead of returning null, the application can return a NoNotification object that implements the same interface but performs no action.

This reduces repetitive null checks and makes the code easier to maintain. It can also help prevent NullPointerException errors in Java.

However, the Null Object Pattern is appropriate only when doing nothing is a valid behavior. We should not use it to hide unexpected errors or situations in which a missing object requires special handling.

7. What is the MVC Design Pattern?

The MVC Design Pattern stands for Model-View-Controller. It is an architectural pattern that separates an application's data, user interface and request-handling logic into three components.

The three components are:

  • Model: Manages the application's data and business rules.
  • View: Displays information to the user.
  • Controller: Handles user input and coordinates the interaction between the Model and View.

For example, in an online shopping application, the Model manages product and order data, the View displays product details and the Controller handles actions such as adding a product to the cart.

The main advantage of MVC is separation of concerns. Each component has a specific responsibility, which makes the application easier to test, maintain and modify. MVC is commonly used in web applications and frameworks such as Spring MVC and ASP.NET MVC.

8. What are the components of the Composite Entity Pattern?

The Composite Entity Pattern is a J2EE design pattern that manages a group of related objects as a single entity. It helps manage complex data relationships and reduces the need for frequent communication between client code and individual objects.

Its main components are:

  • Composite Entity: Represents the main object and manages its dependent objects.
  • Coarse-Grained Object: Represents a larger object that contains multiple related pieces of data.
  • Dependent Objects: Represent smaller objects that belong to or depend on the coarse-grained object.
  • Client: Interacts with the composite entity instead of managing each dependent object separately.

For example, in an employee management system, an employee record may contain personal details, contact information and employment details. The application can manage these related objects through a single composite entity.

The main benefit is that it simplifies access to related data and can reduce remote calls in distributed applications. However, this pattern is primarily associated with traditional J2EE enterprise applications, so we should distinguish it from the Composite Pattern, which organizes objects into tree-like structures.

9. What is the Chain of Responsibility Pattern? In what scenarios should we apply this pattern?

The Chain of Responsibility Pattern is a behavioral design pattern that passes a request through a sequence of handlers. Each handler decides whether to process the request or pass it to the next handler in the chain.

For example, consider an online payment system. A payment request may pass through several handlers that validate the payment details, check fraud risks, verify transaction limits and authorize the payment. Each handler performs its own validation before forwarding the request.

We should use this pattern when multiple objects can handle a request, when the processing order matters, or when we want to change the processing sequence without modifying the client code.

It is also useful in logging systems, authentication pipelines, request validation and middleware.

The main advantage is loose coupling between the sender and the handlers. However, we must design the chain carefully because a request might remain unprocessed if no handler accepts it.

10. How can you achieve thread-safe Singleton Patterns in Java?

A thread-safe Singleton Pattern ensures that an application creates only one instance of a class, even when multiple threads try to access it simultaneously.

In Java, we can achieve thread safety in several ways. The appropriate approach depends on whether we need lazy initialization, simplicity, or protection against serialization and reflection-related issues.

1. Using an enum

public class Main {
enum DatabaseConnection {
INSTANCE
}
public static void main(String[] args) {
DatabaseConnection db1 = DatabaseConnection.INSTANCE;
DatabaseConnection db2 = DatabaseConnection.INSTANCE;
System.out.println(db1 == db2);
}
}

Using an enum

Java initializes enum constants safely and provides built-in protection against creating additional enum instances through normal serialization and reflection mechanisms. This is one of the simplest approaches for implementing a Singleton.

2. Using a static inner class

public class Main {
private Main() {}
private static class Holder {
private static final Main INSTANCE =
new Main();
}
public static Main getInstance() {
return Holder.INSTANCE;
}
public static void main(String[] args) {
Main db1 = Main.getInstance();
Main db2 = Main.getInstance();
System.out.println(db1 == db2);
}
}

Using a static inner class

This approach uses Java's class initialization guarantees to create the instance safely when it is first requested. It supports lazy initialization without requiring explicit synchronization.

3. Using double-checked locking

public class Main {
private static volatile Main instance;
private Main() {}
public static Main getInstance() {
if (instance == null) {
synchronized (Main.class) {
if (instance == null) {
instance = new Main();
}
}
}
return instance;
}
public static void main(String[] args) {
Main db1 = Main.getInstance();
Main db2 = Main.getInstance();
System.out.println(db1 == db2);
}
}

Using double-checked locking

The volatile keyword ensures that threads see the properly initialized instance. The synchronized block prevents multiple threads from creating separate instances during initialization, while the outer check avoids synchronization on subsequent calls.

For most applications, I would prefer the enum approach for a simple Singleton or the static inner class approach when I need lazy initialization. I would use double-checked locking only when there is a specific reason to choose it.

The important point is that thread safety requires more than a private constructor. We must also ensure that concurrent access cannot create multiple instances.

Design Patterns Interview Questions for Experienced Developers

Experienced candidates face design and architecture questions. Interviewers want to hear how you combine patterns, handle failures and avoid overengineering. Answer with a clear approach, a real example and a trade-off.

1. How would you refactor a monolithic application using design patterns to improve maintainability and scalability without disrupting existing functionality?

I would start by understanding the existing monolith before making any changes. I would identify tightly coupled components, duplicated logic, performance bottlenecks and modules that frequently cause bugs. I would also review the existing tests and establish a baseline for application performance.

Next, I would refactor the application incrementally rather than rewriting everything at once. I would use the Facade pattern to simplify interactions with complex subsystems, the Strategy pattern to separate interchangeable business rules and the Dependency Injection pattern to reduce tight coupling between components.

For example, if an e-commerce application has payment logic mixed with order processing, I would extract the payment logic behind a common interface. The order service could then work with different payment providers without depending on their internal implementations.

I would introduce automated tests before changing critical modules. I would also use feature flags, backward-compatible APIs and gradual rollouts to reduce deployment risks.

My goal would be to improve the application's internal structure without changing its existing behavior. I would consider microservices only when independent deployment, scaling requirements, or team ownership justify the additional operational complexity.

Key takeaway: I would refactor incrementally, validate every change and introduce design patterns only where they solve a real architectural problem

2. How would you design a pluggable notification system using Strategy, Factory and Dependency Injection patterns to support email, SMS and push notifications?

I would design the notification system around a common NotificationSender interface. Email, SMS and push notification providers would implement this interface and handle their own delivery logic.

I would use the Strategy pattern to encapsulate each notification method. This would allow the application to switch between notification channels without changing the business logic.

The Factory pattern would select the appropriate notification strategy based on the requested channel. Dependency Injection would provide the required implementations and their dependencies, such as provider clients and configuration settings.

For example, when a user requests a password reset, the notification service could select email as the delivery channel. If the business requirements allow SMS as an alternative, the service could select the SMS implementation instead.

I would also consider provider failures, rate limits, retries and delivery tracking. For production workloads, I would use a message queue to process notifications asynchronously. I would add idempotency controls to prevent duplicate deliveries when jobs are retried.

I would keep the notification channel separate from the delivery provider when necessary. For example, an email notification could use different providers without changing the business rules.

Key takeaway: Strategy handles interchangeable behavior, Factory handles implementation selection and Dependency Injection manages dependencies.

3. How would you implement an event-driven architecture using Observer or Pub/Sub patterns while handling duplicate events, failures and retries?

I would choose between the Observer and Pub/Sub patterns based on the application's communication requirements.

The Observer pattern works well when multiple components need to react to changes in a subject. Pub/Sub is more suitable for distributed systems where publishers and subscribers communicate through a message broker without depending directly on each other.

For example, when an order is placed in an e-commerce application, the order service could publish an OrderPlaced event. The inventory service could update stock, the notification service could send a confirmation and the analytics service could record the transaction independently.

However, I would not assume that every event is delivered exactly once. Distributed systems can produce duplicate messages, temporary failures and out-of-order events.

To handle these issues, I would implement the following safeguards:

  • Idempotency: Each event would have a unique identifier. Consumers would track processed events to prevent duplicate business operations.
  • Retries: Transient failures would trigger retries with exponential backoff and jitter.
  • Dead-letter queues: Messages that repeatedly fail would move to a separate queue for investigation and controlled reprocessing.
  • Transactional Outbox: The application would save its business change and outgoing event in the same database transaction. A separate process would publish the event to the message broker.
  • Monitoring: I would track message failures, processing latency, queue depth and retry counts.

I would also make consumers tolerant of duplicate delivery and define clear rules for event ordering where business operations require it.

Key takeaway: An event-driven architecture needs reliable publishing, idempotent consumers, controlled retries and monitoring. Choosing Observer or Pub/Sub alone does not guarantee reliability.

4. How would you choose between the Decorator, Proxy and Adapter patterns when extending, controlling, or integrating existing functionality?

I would choose between the Decorator, Proxy and Adapter patterns based on the problem I need to solve. Although all three patterns wrap an existing object, they serve different purposes.

  • Decorator Pattern

I use it when I need to add new behavior to an object without changing its original implementation. For example, I can add caching, logging, or performance monitoring to a service.

  • Proxy Pattern

I use it when I need to control access to an object. For example, a proxy can implement authorization checks, lazy loading, rate limiting, or remote service communication.

  • Adapter Pattern

I use it when two components have incompatible interfaces. For example, if my application uses a standard payment interface but an external payment provider exposes a different API, I can create an adapter to make both interfaces compatible.

Let me explain this with a practical example. Suppose I have a payment service that processes transactions.

If I want to add logging without modifying the payment service, I would use the Decorator pattern. If I want to restrict access to the payment service or delay its initialization, I would use the Proxy pattern. If I want to integrate a third-party payment gateway with a different interface, I would use the Adapter pattern.

I would also consider the Single Responsibility Principle. Each pattern should solve one clear problem instead of introducing unnecessary abstraction.

My rule of thumb is simple: Decorator adds behavior, Proxy controls access and Adapter makes incompatible interfaces work together.

5. How would you design a thread-safe, lazily initialized Singleton and what concurrency or serialization issues could arise with your implementation?

I would design a thread-safe, lazily initialized Singleton by ensuring that the application creates only one instance when it is first needed. I would also make sure that multiple threads cannot create separate instances during simultaneous access.

In Java, my preferred approach would be a static inner helper class, also known as the Initialization-on-Demand Holder Idiom.

public class Main {
public static void main(String[] args) {
AppConfig config1 = AppConfig.getInstance();
AppConfig config2 = AppConfig.getInstance();
System.out.println(config1 == config2);
}
public static final class AppConfig {
private AppConfig() {
// Prevent external instantiation
}
private static class Holder {
private static final AppConfig INSTANCE =
new AppConfig();
}
public static AppConfig getInstance() {
return Holder.INSTANCE;
}
}
}

Example of Design Patterns

This approach is thread-safe because Java initializes the inner class when it is first accessed. The JVM guarantees safe class initialization, so I do not need explicit synchronization or double-checked locking.

I would consider a few important risks.

  • Race conditions: If I use an unsafe lazy initialization approach, two threads might create separate instances at the same time. The holder idiom avoids this problem.
  • Synchronization overhead: I would avoid unnecessary synchronization on every access. The holder idiom provides lazy initialization without requiring a synchronized getter.
  • Serialization: If the Singleton implements Serializable, deserialization can create another instance unless I protect it. In Java, I can use readResolve() to return the existing instance. For stronger serialization guarantees, an enum Singleton is often a better choice.
  • Reflection and cloning: Reflection can sometimes bypass a private constructor and cloning can create another instance. I would restrict these operations where necessary. An enum Singleton also provides strong protection against several of these issues.

At last I will question whether a Singleton is necessary at all. For application services, dependency injection is often easier to test and maintain. A Singleton is most appropriate when a single shared instance is a genuine requirement, not just a convenient shortcut.

6. How would you apply Chain of Responsibility to build a request-processing pipeline with authentication, authorization, validation, logging and error handling?

I would create a Handler interface with one method, such as handle(request, next). Each concern would become its own handler. The pipeline would run in this order:

  1. Logging: Record the request and start a trace ID.
  2. Authentication: Confirm who the caller is.
  3. Authorization: Confirm the caller can perform the action.
  4. Validation: Check the request data.
  5. Business logic: Process the request.

Error handling wraps the whole chain. I would place it at the outermost layer so it can catch failures from any handler and return a consistent response.

Order matters. For example, I would authenticate before I validate. This stops unknown callers from reaching deeper logic. Each handler would stop the chain and return an error if its check fails.

I would also keep handlers small and independent. Teams can then add, remove, or reorder handlers through configuration without changing the client code. Web frameworks such as servlet filters and middleware stacks use this idea.

Key takeaway: Give each concern one handler, set the order with care and wrap the chain with central error handling.

7. How would you design a payment processing system using State and Strategy to handle payment lifecycles, multiple providers, retries and failures?

I would use State for the payment lifecycle and Strategy for the payment providers.

  • State pattern: A payment moves through states such as Created, Pending, Authorized, Captured, Failed and Refunded. Each state class decides which actions are allowed. For example, a Failed payment cannot be captured. This removes long if-else blocks and blocks invalid transitions.
  • Strategy pattern: A PaymentProvider interface hides each provider’s API. Stripe, PayPal, or a UPI gateway would each implement it. The system can switch providers without changing the lifecycle logic.

For retries, I would retry only temporary errors, such as timeouts. I would use exponential backoff and a unique idempotency key for every payment. This prevents double charges. For permanent errors, such as a declined card, I would move the payment to Failed and stop.

I would store every state change for auditing. I would also add a fallback provider for outages, plus monitoring and alerts.

Key takeaway: State controls what a payment can do next. Strategy controls who processes it. Idempotency keys keep retries safe.

8. How would you prevent the Factory pattern from becoming a maintenance bottleneck when an application supports dozens of implementations?

A large factory with a long switch statement is hard to maintain. It also breaks the Open-Closed Principle, because every new plugin needs an edit.

I would use these approaches:

  • Registry-based factory: Each implementation registers itself with a key. The factory looks up the key in a map. New plugins need no change to the factory.
  • Service discovery: In Java, ServiceLoader finds implementations at runtime. Spring can inject all beans that implement an interface.
  • Convention and configuration: I would load implementations by name from configuration files.
  • Smaller factories: If one factory still grows too large, I would split it by domain.

I would also add tests that check every registered plugin follows the contract. Clear documentation and version rules help teams add plugins safely.

Key takeaway: Replace the switch statement with a registry, so adding a plugin never requires editing the factory.

9. How would you use the Command pattern to implement undo/redo, transaction rollback, or reliable background jobs?

The Command pattern turns each action into an object. That object can run, store, undo and replay.

  • Undo and redo: Each command has execute() and undo() methods. The app keeps two stacks, one for undo and one for redo. When the user undoes an action, the app pops a command from the undo stack, calls undo() and pushes it to the redo stack. A new action clears the redo stack.
  • Transaction rollback: I would run a list of commands in order. If one fails, I would call undo() on the completed commands in reverse order. This works well for multi-step workflows where each step has a compensating action.
  • Reliable background jobs: I would save each command as a serializable job in a queue or database. Workers pick up jobs and run them. If a worker crashes, another worker can retry the saved command. I would add idempotency keys so retries do not repeat side effects.

Key takeaway: Because commands are objects, you can store them, replay them and reverse them.

10. How would you identify and refactor overused design patterns that add unnecessary abstraction, complexity, or performance overhead?

I would look for warning signs first:

  • Interfaces with only one implementation and no plan for more.
  • Factories that create only one type.
  • Deep wrapper chains that make debugging hard.
  • New developers who need days to follow a simple flow.
  • Slow code paths caused by extra layers or object creation.

Next, I would measure. I would profile the code and review how often the code changes. If a pattern adds cost but does not support real change, I would remove it.

I would refactor in small steps. I would add tests, inline the unneeded abstraction and keep the behavior the same. For example, I would replace a single-implementation Strategy with a direct method call.

I follow a simple rule: use a pattern when it solves a problem I have today or a change I can reasonably expect. Simple code is often the better design.

Key takeaway: Patterns are tools. Remove them when they cost more than they give.

Design Pattern Scenario-Based Interview Questions

Scenario-based questions test how you solve real problems. Interviewers want to see how you combine patterns, plan for failure and explain your trade-offs. Structure each answer as problem, patterns, flow and risks.

1. You are building an AI-powered assistant that plans tasks, calls external APIs and generates reports. Tool calls can fail and some tasks need human approval. Which design patterns would you use?

I would combine several patterns.

  • Command: Each task step becomes a command object. This makes steps easy to queue, log, retry and replay.
  • State: Each task moves through states such as Planned, Running, Waiting for Approval, Failed and Completed. This makes the workflow clear and blocks invalid transitions.
  • Chain of Responsibility or pipeline: Steps run in order, such as plan, call tool, validate output and generate report.
  • Strategy: Each external tool uses the same interface, so the agent can swap tools easily.
  • Retry with backoff and Circuit Breaker: These protect the system from failing or slow APIs.
  • Saga: If a later step fails, compensating actions undo earlier steps.

For human approval, I would pause the workflow in the Waiting for Approval state and save its data. The system would resume after a person approves or rejects the step. I would also use idempotency keys, so a retry does not repeat an action such as sending an email.

Key takeaway: Use Command and State for control, Retry and Circuit Breaker for failures and Saga for recovery.

2. You are designing an e-commerce platform where payment, inventory and notification services consume order events. Events can arrive more than once and services can go down. How would you use Pub/Sub, Saga and Idempotency?

  • Pub/Sub: The order service publishes an OrderPlaced event to a broker. Payment, inventory and notification services subscribe and work independently. A failure in one service does not block the others.
  • Idempotency: Every event has a unique ID. Each consumer stores the IDs it has processed. If the same event arrives again, the consumer skips it. This prevents double charges and double stock deductions.
  • Saga: The order flow has several steps across services. If one step fails, the saga runs compensating actions. For example, if payment succeeds but inventory fails, the system refunds the payment and cancels the order. I would use either choreography, where services react to each other’s events, or orchestration, where a central coordinator controls the flow. Orchestration is easier to track in complex flows.

I would also add a transactional outbox, retries with backoff and dead-letter queues. Monitoring would alert the team when messages fail repeatedly.

Key takeaway: Pub/Sub decouples services, Idempotency makes duplicates safe and Saga handles partial failures.

3. Your application depends on several third-party APIs. One becomes slow or starts returning errors and latency rises across the system. How would you combine Circuit Breaker, Retry, Timeout and Bulkhead?

Each pattern solves one part of the problem.

  • Timeout: I would set a strict time limit for every external call. This stops slow responses from holding threads for too long.
  • Retry: I would retry only temporary failures, with exponential backoff and jitter. I would limit the number of attempts. Retrying a failing service too often makes things worse.
  • Circuit Breaker: If the error rate crosses a threshold, the breaker opens and the app stops calling the API for a short time. After a cooldown, it allows a few test calls. If they succeed, it closes again.
  • Bulkhead: I would give each API its own thread pool or connection limit. If one API slows down, it cannot use all the resources and hurt other features.

I would also add a fallback, such as cached data or a default response. Metrics and alerts would show which dependency is failing.

Key takeaway: Timeouts limit waiting, Retries handle brief errors, Circuit Breakers stop repeated failures and Bulkheads contain the damage.

4. Your organization needs to migrate a large legacy monolith to microservices without disrupting users. How would you apply Strangler Fig, Anti-Corruption Layer and Dependency Injection while managing data consistency?

  • Strangler Fig: I would place a routing layer, such as an API gateway, in front of the monolith. I would then move one feature at a time into a new service and route its traffic to the new service. The monolith shrinks over time until I can retire it. I would start with low-risk features.
  • Anti-Corruption Layer: The legacy system has its own models and rules. An anti-corruption layer translates between legacy models and the new service models. This keeps the legacy design from leaking into the new code.
  • Dependency Injection: I would inject interfaces instead of concrete classes. This lets me swap a legacy implementation for a new service without large code changes. It also makes testing easier.

For data consistency, I would avoid a big-bang data move. I would sync data between old and new systems with events or change data capture. For a short period, one system would own each piece of data. For multi-service workflows, I would use Sagas instead of distributed transactions.

I would use feature flags and gradual rollouts, so I can roll back quickly if something breaks.

Key takeaway: Migrate one feature at a time, protect new code with an anti-corruption layer and give each piece of data a single owner.

5. Your company uses multiple AI models with different costs, latency, capabilities and availability. You must select models dynamically, enforce security policies and log important actions. How would you combine Strategy, Factory, Proxy and Chain of Responsibility to design an AI gateway?

I would design the gateway in layers.

  • Chain of Responsibility: Every request passes through a chain of handlers. Typical handlers are authentication, rate limiting, input filtering, policy checks and logging. Each handler can reject the request or pass it on. Teams can add or reorder handlers without touching the others.
  • Strategy: Each model selection rule becomes a strategy. One strategy picks the cheapest model. Another picks the fastest. Another picks the most capable model for a task. The gateway swaps strategies based on the request or the customer plan.
  • Factory: A factory creates the right model client for the chosen model. Each client follows one common ModelClient interface, so adding a new model needs little change.
  • Proxy: A proxy wraps each model client. It adds access control, caching, timeouts, fallbacks and audit logging. The calling code does not know the proxy is there.

For example, a request arrives and passes through authentication and policy checks. The strategy picks a model. The factory builds its client. The proxy calls the model, logs the action and falls back to another model if the first one fails.

Key takeaway: Chain of Responsibility enforces rules, Strategy selects the model, Factory builds the client and Proxy secures and monitors each call.

Final Words

Design patterns interviews reward clear thinking more than memorized definitions. For every pattern, learn the problem it solves, a simple real-world example and one trade-off. Practice explaining each answer out loud in under two minutes.

Start with the basics and the GoF categories. Then practice the Singleton, Factory, Strategy, Observer and Decorator patterns in code. Finally, work on the scenario questions, because they show how you apply patterns in real systems. With steady practice, you can answer any design patterns interview question with confidence.

About the Author
Apoorva | igmGuru
About the Author

Apoorva has spent close to fifteen years in enterprise IT, working across infrastructure planning, service management, system architecture, and emerging technology trends during digital transformation. Late-night outages and change-management reviews shaped his hands-on grounding. He tests new tools in lab environments before writing, giving IT professionals a realistic view of how technology decisions play out.

Drop Us a Query
Fields marked * are mandatory
×

Your Shopping Cart


Your shopping cart is empty.