Skip to main content
Design Patterns Map - All 23 Gang of Four Patterns
Time to Master: 2-3 hours | Prerequisites: OOP basics | Interview Frequency: Very High
Pattern Selection Tip: Don’t force patterns! If simple code solves the problem, use simple code. Patterns add complexity that must be justified with real benefits.

πŸ“Š Pattern Overview

Design patterns are proven solutions to common software design problems. They are not inventions β€” they are discoveries. The Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) catalogued these patterns in 1994 by studying what experienced developers did repeatedly. Think of patterns as a shared vocabulary: when you say β€œlet’s use a Strategy here,” every engineer on the team immediately understands the structure, the trade-offs, and the intent. There are 23 classic patterns divided into 3 categories:

Creational (5)

How objects are createdSingleton, Factory, Abstract Factory, Builder, Prototype

Structural (7)

How objects are composedAdapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Behavioral (11)

How objects communicateStrategy, Observer, Command, State, Template, Iterator, Mediator, Chain of Responsibility, Visitor, Memento, Interpreter
Interview Focus: You don’t need to memorize all 23! Focus on: Singleton, Factory, Strategy, Observer, State, Decorator, Command - these cover 90% of LLD interviews.

🎯 Pattern Decision Tree

Use this to quickly identify which pattern fits your problem:

πŸ”΅ Creational Patterns

How objects are created

Singleton

One instance only

Factory

Create without specifying class

Builder

Step-by-step construction

1. Singleton Pattern

When to Use: Database connections, Configuration, Logging, Thread pools, Caches - anything where only ONE instance should exist.
Ensures a class has only one instance with global access. This is the simplest pattern to understand but the easiest to misuse. Use it when having multiple instances would cause real problems β€” such as multiple database connection pools competing for resources, or multiple configuration objects with conflicting settings. The key insight is that Singleton controls identity (there is exactly one), not just access (it is globally reachable). If you only need global access, a module-level variable or dependency injection is often a better choice.
Use When: Database connections, Configuration, Logging, Thread pools, Caches
Singleton Anti-patterns:
  • Makes unit testing harder (global state)
  • Can hide dependencies
  • Consider dependency injection instead for better testability

2. Factory Pattern

Creates objects without specifying exact classes. Factory encapsulates the β€œwhich class to instantiate” decision, so the calling code depends only on the interface, not the concrete type. This directly supports the Open/Closed Principle: when you add a new notification channel, you update the factory’s registry β€” the rest of your codebase never changes.
Use When: Object creation logic is complex, Multiple types share interface

3. Builder Pattern

Constructs complex objects step by step. Builder shines when an object has many optional parameters and you want a readable, fluent API instead of a constructor with 15 arguments. The key insight is separating construction from representation β€” the same building process can produce different results. In real-world codebases, you will see Builder used for query builders (SQLAlchemy), HTTP request builders, and configuration objects.
Use When: Object has many optional parameters, Step-by-step construction

Structural Patterns

How objects are composed

4. Adapter Pattern

Makes incompatible interfaces work together.
Use When: Integrating legacy code, Third-party library integration

5. Decorator Pattern

Adds behavior to objects dynamically.
Use When: Add features without subclassing, Compose behaviors

6. Facade Pattern

Provides simple interface to complex subsystem.
Use When: Simplify complex systems, Provide unified API

Behavioral Patterns

How objects communicate

7. Strategy Pattern

Defines a family of interchangeable algorithms. Strategy is arguably the most important pattern for LLD interviews because it directly implements the Open/Closed Principle. The core idea: extract varying behavior into its own class hierarchy, then inject the specific variant at runtime. Whenever you see a decision that might change (pricing rules, sorting algorithms, payment processing), Strategy is your go-to pattern. It turns compile-time decisions into runtime decisions, giving you flexibility without modifying existing code.
Use When: Multiple algorithms for same task, Switch behavior at runtime

8. Observer Pattern

Notifies multiple objects about state changes. Observer implements a one-to-many dependency so that when one object changes state, all dependents are notified automatically. This is the foundation of event-driven architectures, UI frameworks (React’s state management), and pub/sub systems. The critical design insight is decoupling: the subject (publisher) does not know or care about the specific observers (subscribers). This means you can add new notification channels, analytics hooks, or audit logging without touching the core business logic.
Use When: One-to-many dependencies, Event systems

9. State Pattern

Object behavior changes based on internal state. State pattern eliminates sprawling if/elif chains that check the current state before deciding what to do. Instead, each state becomes its own class with its own behavior. The context object delegates to the current state, and state transitions happen by swapping the state object. This is the go-to pattern for modeling lifecycles: order processing (Pending, Confirmed, Shipped, Delivered), elevator systems (Moving, Stopped, Maintenance), and vending machines (Idle, HasMoney, Dispensing). When interviewers hear you say β€œI’ll model this as a state machine,” they know you are thinking like a senior engineer.
Use When: Object behavior depends on state, State-specific logic is complex

Pattern Selection Guide


πŸ”₯ Top Patterns for Interviews

These patterns appear most frequently in LLD interviews:

Must-Know (80% of interviews)

Good to Know (20% of interviews)


πŸ“š Pattern Diagrams


πŸ“ Pattern Comparison Table


πŸ’‘ Interview Tips

  • Do: β€œI’ll use Factory here because we need to create vehicles without knowing the exact type at compile time”
  • Don’t: β€œLet me apply all 23 GoF patterns to show I know them”
Pro tip: Wait for the right moment. If you’re designing payment methods, naturally mention Strategy pattern.
Every pattern has costs - always mention them:Always explain why the benefit outweighs the cost for YOUR use case.
Patterns often work together:
  • Factory + Strategy: Create strategies via factory
  • Observer + Singleton: Global event bus
  • State + Factory: Create states via factory
  • Decorator + Factory: Create decorators dynamically
  • Command + Memento: Undo with snapshots
  • β€œWhy did you use X pattern here?”
  • β€œWhat’s the difference between Strategy and State?”
  • β€œHow would you add a new payment method?” (Strategy)
  • β€œHow would you implement undo/redo?” (Command + Memento)
  • β€œHow would you prevent double instantiation?” (Singleton with locking)
Don’t over-engineer! Use patterns when they solve real problems, not to show off. Simple, readable code is often better than pattern-heavy code. Ask yourself: β€œWould this be simpler without the pattern?”

Interview Deep-Dive Questions

Strong Answer:
  • The if/elif chain works fine for 4 channels today, but the real question is: how often will this list change? If the answer is β€œevery quarter when marketing adds a new channel,” then every change forces you to modify the dispatching function, re-test it, and risk breaking existing channels. That violates the Open/Closed Principle β€” the code is not closed for modification.
  • I would use the Strategy pattern here. Define a NotificationChannel interface with a send(message, recipient) method. Each channel (Email, SMS, Push, Slack) is a concrete strategy. The calling code receives the strategy via dependency injection or a factory, and calls strategy.send() without knowing or caring which channel it is.
  • The factory that maps "email" to EmailChannel is the only place that needs updating when a new channel is added. The dispatching code, the business logic, and all existing channel implementations remain untouched.
  • However, I would not use this pattern if the system will only ever have 2-3 channels and the selection logic has domain-specific rules (e.g., β€œsend SMS only if user is in the US and has opted in, otherwise fall back to email”). In that case, the if/elif chain captures the business logic more clearly than a pattern. Patterns should reduce complexity, not add indirection for its own sake.
  • The key insight for the interviewer: I am not choosing the pattern because β€œGoF says so.” I am choosing it because the axis of change (new channels being added frequently) aligns with what Strategy is designed to handle β€” isolating the varying behavior behind an interface.
Red flag answer: β€œI would use Strategy because it is best practice” without explaining why it fits this specific problem, or conversely, β€œif/elif is fine, patterns are over-engineering” without considering the rate of change and maintenance cost.Follow-ups:
  1. What if each notification channel requires completely different configuration (email needs SMTP settings, SMS needs a Twilio API key, push needs device tokens)? How do you handle the construction of these strategy objects β€” and does that suggest a second pattern?
  2. Now the product manager says β€œsome notifications should go to multiple channels simultaneously.” How does your design change? Does Strategy still work, or do you need Observer/Chain of Responsibility?
Strong Answer:
  • You are right that the UML is nearly identical: both have a Context that delegates to an interface, with multiple concrete implementations. The difference is intent and who controls transitions.
  • Strategy: The client chooses which algorithm to inject. The context does not change its strategy on its own. Example: a payment system where the user picks credit card vs PayPal at checkout. The ShoppingCart does not spontaneously switch from CreditCard to PayPal β€” the user (external actor) makes that choice.
  • State: The object itself transitions between states based on internal logic. The context’s behavior changes over time as its state changes. Example: an order that moves from Pending to Processing to Shipped to Delivered. The order transitions itself β€” no external actor says β€œnow be in Shipped state.” Each state class knows which state comes next and triggers the transition.
  • The practical test: if you see context.set_strategy(new_strategy) called by client code, it is Strategy. If you see self.context.state = NextState() called from within a state class, it is State.
  • Another heuristic: Strategy objects tend to be stateless (they are pure algorithms), while State objects often carry state-specific data and have transition logic. In a State pattern, the state classes form a finite state machine with defined transitions; in Strategy, the strategies are independent and do not know about each other.
  • A real-world example that blurs the line: a rate limiter that switches between AllowAll, Throttle, and BlockAll strategies based on current load. Is this Strategy (swapping algorithms) or State (the system transitions between states)? If the system itself monitors load and transitions automatically, it is State. If an ops engineer manually flips a switch, it is Strategy. Same structure, different intent.
Red flag answer: β€œThey are basically the same thing, just different names” or only explaining one of them. The candidate should be able to articulate the intent difference clearly, because this question directly tests whether they understand patterns at a conceptual level or just memorized the UML.Follow-ups:
  1. Can you give me an example of a system where you might start with Strategy and later realize it should be State (or vice versa)? What would trigger that refactoring?
  2. In the State pattern implementation shown earlier, each state class creates the next state object directly (e.g., order.state = ProcessingState()). What is the problem with this coupling, and how would you fix it?
Strong Answer:
  • Singleton for everything β€œshared”: Teams often reach for Singleton for any class they want globally accessible β€” configuration, logging, feature flags, API clients. The problem: Singleton hides dependencies. A function that internally accesses DatabaseConnection.get_instance() has an invisible dependency on the database β€” its signature does not reveal this. Unit testing becomes painful because you cannot substitute a mock without monkey-patching global state. In one project I worked on, 14 Singletons created a hidden dependency graph that made the startup order brittle β€” service A’s singleton initialized before service B’s was ready, causing intermittent failures. The fix: dependency injection. Pass the database connection as a constructor argument. It is more explicit, more testable, and the β€œsingle instance” guarantee can be enforced at the composition root (the main function or DI container) rather than inside the class.
  • Observer pattern with too many subscribers and no backpressure: Observer is elegant for loose coupling, but in a high-throughput system, a subject notifying 200 observers synchronously on every state change turns a single write into 200 function calls on the same thread. If any observer is slow (e.g., one that writes to disk), it blocks the entire notification chain. I have seen this in event-driven systems where adding a new β€œjust log this event” observer increased p99 latency by 300ms because the logger was doing synchronous I/O. The fix: either use async notification (message queue, event bus), or switch to a pub/sub system that decouples the producer’s latency from the consumers. The pattern itself does not tell you to think about backpressure β€” that is your job.
  • Factory pattern for a single concrete class: If NotificationFactory.create("email") always returns EmailNotification and there are no other notification types (and no realistic plan to add them), the factory is pure ceremony. It adds a layer of indirection, an extra file, and a string-based lookup that could fail at runtime β€” all for no benefit. This is the most common pattern anti-pattern: applying a pattern β€œjust in case” against the YAGNI principle. Build the factory when the second or third concrete type actually materializes, not speculatively.
  • Bonus: Decorator stacks that are impossible to debug. When you have LoggingDecorator(RetryDecorator(TimeoutDecorator(AuthDecorator(HttpClient())))), a stack trace for an error is 8 layers deep, and understanding which decorator caught or transformed the exception requires reading every wrapper. At some point, a middleware pipeline (like Django/Express middleware) is clearer because it has explicit ordering and standardized hooks.
Red flag answer: β€œThere is no wrong time to use patterns, they are best practices.” This is the biggest red flag. Every pattern is a trade-off, and a senior engineer should be able to articulate the cost of each one. Another weak answer: vague statements like β€œwhen it is too complex” without concrete examples.Follow-ups:
  1. You are reviewing a pull request from a junior engineer who added a Factory, Strategy, and Observer to a feature that currently has one payment method and one notification channel. How do you give constructive feedback?
  2. How do you decide at what point to introduce a pattern? What heuristic or β€œrule of thumb” do you use to avoid both premature abstraction and too-late refactoring?
Strong Answer:
  • With inheritance, you create a new class for each combination of features. If you have a Coffee base class and want Milk, Sugar, and Whip as options, inheritance requires: MilkCoffee, SugarCoffee, WhipCoffee, MilkSugarCoffee, MilkWhipCoffee, SugarWhipCoffee, MilkSugarWhipCoffee β€” that is 2^N classes for N options. This is the β€œclass explosion” problem. With Decorator, you have 3 decorator classes and compose them at runtime: Whip(Sugar(Milk(Coffee()))). Adding a 4th option (Caramel) adds 1 decorator class instead of doubling the class count.
  • Decorator is strictly better when: (a) you need to combine behaviors in arbitrary ways at runtime, (b) the set of behaviors grows independently, (c) you want to add/remove capabilities dynamically (e.g., toggling features via configuration).
  • Inheritance is the right call when: (a) the relationship is genuinely β€œis-a” and behavior is not composable (a Dog is an Animal, not a decorated Animal), (b) the subclass needs access to protected internals of the parent (decorators only access the public interface), (c) the hierarchy is shallow and stable β€” adding a subclass is cheaper than the decorator infrastructure.
  • A real-world example where Decorator shines: middleware in web frameworks. Each middleware (authentication, logging, rate limiting, compression) wraps the handler. You compose them in a chain, and you can reorder or disable them via configuration. Doing this with inheritance would be absurd.
  • A real-world example where inheritance is better: UI widget hierarchies (Button extends Widget). A Button is not a β€œdecorated Widget” β€” it has fundamentally different rendering logic, not just added behavior on top of Widget’s rendering.
  • The gotcha: Decorator requires that all decorators and the base component share the same interface. If the base interface is large (20 methods), every decorator must delegate all 20 methods, which is tedious and error-prone. In Python, __getattr__ delegation can help, but in strongly typed languages like Java, it is boilerplate-heavy. This is where abstract decorator base classes (like CoffeeDecorator in the example) help β€” they provide default delegation so concrete decorators only override what they change.
Red flag answer: β€œDecorator is always better than inheritance because inheritance is bad.” This is cargo-cult reasoning. Inheritance is a tool; the question tests whether the candidate understands when each tool is appropriate, not whether they can recite β€œfavor composition over inheritance.”Follow-ups:
  1. In Python, functools.wraps and function decorators (@decorator) look similar to the Decorator pattern but are actually different. What is the relationship, and can you use Python’s @decorator syntax to implement the GoF Decorator pattern?
  2. You have a Stream class with read() and write() methods. You want to add encryption, compression, and buffering as optional layers. Walk me through how you design this with the Decorator pattern and what the call flow looks like when a client calls read().
Strong Answer:
  • This is a textbook candidate for the State pattern. The code uses string-based status checking and a growing if/elif chain to determine behavior. Every new state (e.g., β€œrefunded”, β€œcancelled”, β€œreturned”) adds another branch, and the logic for each state is tangled together in one method.
  • Problems with this code: (a) Open/Closed violation β€” adding a new state requires modifying process(), risking bugs in existing states. (b) No compile-time safety β€” order.status is a string, so a typo like "valdiated" would silently fail. (c) State-specific logic is scattered β€” what if β€œvalidated” orders need additional behavior (e.g., sending a confirmation email)? You would add it to the elif branch, making the method longer and harder to test. (d) Transition rules are implicit β€” nothing prevents order.status from being set to β€œdelivered” directly from β€œpending,” skipping validation and payment.
  • Refactoring to State pattern: Each state becomes a class (PendingState, ValidatedState, PaidState, ShippedState, DeliveredState). Each state class has a process(order) method that performs the state-specific action and transitions to the next state. The Order class holds a reference to its current state and delegates process() to it. Invalid transitions can raise exceptions in the state class (e.g., DeliveredState.process() raises β€œalready delivered”).
  • However, I would push back on when to refactor: if this is a startup with 4 states that rarely change, this if/elif is readable and works. I would add a TODO and refactor when the 5th or 6th state appears. The pattern is the right long-term architecture, but premature extraction costs development time and adds indirection that a new team member must learn.
  • The string-based status is the more urgent fix regardless of the pattern question. At minimum, replace strings with an Enum (class OrderStatus(Enum): PENDING = "pending" ...) to get IDE autocomplete and catch typos.
Red flag answer: Either β€œthis code is fine, it works” (does not see the maintenance problem) or β€œimmediately refactor to State pattern” without considering whether the complexity is warranted yet. The best answer demonstrates judgment β€” knowing the right pattern AND knowing when to apply it.Follow-ups:
  1. After refactoring to the State pattern, how would you add a β€œcancelled” state that can be reached from Pending, Validated, or Paid (but not from Shipped or Delivered)? How does the State pattern make this easier or harder than the if/elif approach?
  2. What if this order processor needs to persist state to a database? How do you serialize and deserialize State pattern objects? What are the challenges?
Strong Answer:
  • In the textbook Observer, notify() iterates through observers and calls update() on each one synchronously, on the calling thread. This has three production problems: (a) Latency coupling β€” the slowest observer determines the total notification time. If 1 of 50 observers takes 500ms (e.g., writing to a slow external API), every state change takes at least 500ms. (b) Failure propagation β€” if an observer throws an exception, it can abort the notification loop, leaving the remaining observers un-notified (unless you wrap each call in try/catch). (c) Thread starvation β€” in a single-threaded event loop (like Node.js or a game loop), synchronous notification blocks the entire loop.
  • Kafka implements Observer at the infrastructure level: producers publish events to topics, consumers subscribe to topics. The crucial difference is asynchronous, persistent, buffered delivery. Producers do not wait for consumers. If a consumer is slow, events accumulate in the topic partition. Consumers can replay events, process at their own pace, and even go offline temporarily. This is the β€œObserver pattern at scale” β€” decoupled by a durable message broker.
  • React (state management via useState/useReducer): When state changes, React does not immediately re-render all subscribed components. It batches state updates, reconciles the virtual DOM, and only re-renders components whose props/state actually changed. This is asynchronous, batched notification with diffing β€” a highly optimized Observer. React 18’s concurrent mode takes this further by allowing React to interrupt and prioritize renders.
  • Django signals: post_save, pre_delete, etc. are synchronous Observer hooks. When you call model.save(), all connected signal handlers execute synchronously in the same database transaction. This is a common footgun: a signal handler that sends an HTTP request to a third-party API slows down every save. The Django community’s recommendation: use signals only for decoupled app-to-app communication within the same process; for anything involving I/O, dispatch to Celery (async task queue) from the signal handler.
  • The evolution: Textbook Observer (in-process, synchronous) to Event Bus (in-process, async) to Message Queue (cross-process, persistent) to Event Streaming (cross-service, replayable, ordered). Each step adds infrastructure complexity but gains in decoupling, resilience, and scalability.
Red flag answer: β€œObserver is just pub/sub” without explaining the synchronous vs asynchronous distinction, or not knowing that production systems almost never use the textbook synchronous Observer at any meaningful scale.Follow-ups:
  1. How would you handle the case where an observer needs the notification to be exactly-once (e.g., charging a credit card)? How does this change the Observer implementation?
  2. In the textbook Observer, if observer A detaches observer B during the notification loop (because A’s update logic removes B), what happens? How would you make the notification loop safe against concurrent modification?
Strong Answer:
  • Singleton has earned its β€œanti-pattern” reputation not because the pattern itself is flawed, but because it is dramatically overused for the wrong reasons. People use it for global access when what they actually need is single instance.
  • Testing nightmare: A class that calls Database.get_instance() internally cannot be tested with a mock database. You must monkey-patch the global singleton, which is fragile, order-dependent, and can bleed state between tests. In a test suite of 2000 tests, shared singleton state causes intermittent failures that are almost impossible to reproduce.
  • Hidden dependencies: When a function’s signature is process_order(order) but it internally accesses Config.instance(), Logger.instance(), and PaymentGateway.instance(), the function’s true dependencies are invisible. A new developer cannot understand what this function needs just by reading its signature. This is the antithesis of clean architecture.
  • Initialization ordering: If Singleton A depends on Singleton B during initialization, and B depends on A, you have a circular dependency that manifests as a crash or undefined behavior at startup. With dependency injection, the DI container detects this cycle at configuration time and gives you a clear error.
  • What to use instead: Dependency Injection (DI). Define the interface, create the instance once at the application’s composition root (the main() function or a DI container like Python’s dependency-injector), and pass it to everything that needs it. The result: same single instance in production, but tests can inject mocks, and every dependency is explicit in constructor signatures.
  • When Singleton is actually fine: Truly stateless utility classes (like a Math singleton β€” though that is better as a module), or when the language/framework provides singleton semantics naturally (Python modules are singletons, Spring beans are singletons by default). The pattern is fine; the abuse of it for everything β€œshared” is the problem.
  • A concrete example: A team had a FeatureFlags.instance() singleton that read flags from a file at startup. Unit tests could not test behavior with different flag combinations because the singleton loaded once and never reset. Switching to constructor injection (Service(feature_flags=flags)) let tests pass any flag configuration they wanted, and the production code still used a single FeatureFlags instance created at startup.
Red flag answer: β€œSingleton is an anti-pattern, never use it” (dogmatic) or β€œSingleton is fine, just use it for everything that should be shared” (no awareness of the problems). The nuanced answer is: the pattern is a tool, the overuse is the anti-pattern.Follow-ups:
  1. In Python specifically, how does module-level instantiation (config = Config() in config.py) differ from the Singleton pattern? What are the advantages and disadvantages?
  2. You inherit a codebase with 12 Singletons. You cannot rewrite everything at once. What is your incremental strategy to reduce Singleton dependency while keeping the system working?
Strong Answer:
  • I do not start by thinking about patterns. I start by identifying the axes of change β€” the parts of the system that are most likely to vary or grow over time. Patterns are solutions to specific change scenarios.
  • Axis 1 β€” Vehicle types (car, motorcycle, truck, EV): These share an interface (park(), getSize()) but have different behaviors. This is classic Factory territory. A VehicleFactory maps the vehicle type string from the entry sensor to a concrete class. When the lot adds EV charging spots, I add an ElectricVehicle class and update the factory β€” nothing else changes.
  • Axis 2 β€” Pricing strategies (hourly, daily, weekend, membership): The pricing logic varies by customer type and time. This is Strategy. A PricingStrategy interface with calculateFee(entry_time, exit_time). The ParkingTicket holds a reference to the pricing strategy. When marketing adds a β€œholiday pricing” tier, it is a new strategy class β€” no modification to existing pricing logic.
  • Axis 3 β€” Spot assignment logic: How do you pick which spot to assign? Closest to entrance? Closest to exit? Distribute evenly across floors? This varies by lot and could even change at runtime (normal mode vs event mode). Again Strategy, but for a different axis. The ParkingLot delegates spot assignment to a SpotAssignmentStrategy.
  • Axis 4 β€” Notifications (spot available, lot full, payment receipt): Multiple systems need to react to events (display boards, mobile app, admin dashboard). This is Observer β€” the ParkingLot is the subject, displays and apps are observers.
  • Axis 5 β€” Payment processing: Multiple payment methods. Strategy again β€” PaymentStrategy interface with CreditCard, Cash, MobilePayment implementations.
  • What I would not use: Singleton for the ParkingLot class (a common mistake). There is nothing preventing a company from managing multiple lots. Even if there is β€œonly one lot,” making it a Singleton adds global state. I would create one instance and pass it through constructor injection.
  • My reasoning framework: for each entity or behavior, I ask: (a) Will there be multiple variants? If yes, Factory or Strategy. (b) Does behavior change based on state? State pattern. (c) Do multiple components need to react to changes? Observer. (d) Is the construction complex? Builder. If the answer is β€œno” to all of these, plain classes and simple methods are fine β€” no pattern needed.
Red flag answer: β€œI would use Singleton for ParkingLot, Factory for Vehicles, Strategy for Payment, Observer for notifications” β€” listing patterns without explaining why each one fits. The interviewer is testing the reasoning process, not pattern name-dropping. An even worse answer: starting with β€œlet me apply the 23 GoF patterns” β€” this shows the candidate is pattern-driven rather than problem-driven.Follow-ups:
  1. The interviewer pushes back: β€œYou used Strategy for both pricing and spot assignment. Is that not confusing? How do you keep the codebase clear when the same pattern appears multiple times?” How do you respond?
  2. Six months after launch, the parking lot needs to support β€œreserved spots” that are pre-booked by time slot. Which existing pattern in your design accommodates this, or do you need a new one?

πŸ”— Continue Learning

Practice: Case Studies

See patterns in action with real problems

UML Diagrams

Learn to draw pattern diagrams

Interview Cheat Sheet

Quick reference for interviews