ATM — Financial & Payments
Difficulty: Intermediate
State pattern for ATM lifecycle management, Chain of Responsibility for greedy bill dispensing across denominations, and clean separation between authentication, transaction, and cash handling concerns.
Design Patterns for ATM
- State Pattern
- Chain of Responsibility Pattern
Key Abstractions in ATM
- ATM: Context class that delegates all operations to its current state
- ATMState: Interface defining valid operations: insertCard, enterPin, withdraw, ejectCard
- IdleState: Waiting for card insertion. Rejects pin entry and withdrawal.
- CardInsertedState: Card present, awaiting PIN. Rejects withdrawal until authenticated.
- AuthenticatedState: PIN verified. Allows withdrawal and card ejection.
- CashDispenser: Chain of Responsibility handler for denomination-based bill dispensing
- Account: Holds balance, validates PIN, processes debits
- Transaction: Immutable record of a completed withdrawal
Key Points for ATM
- State pattern makes invalid operations per state a compile-time concern, not a runtime if/else maze
- Chain of Responsibility for dispensing: $100 -> $50 -> $20 -> $10. Greedy largest-first minimizes bill count.
- Each state only implements the operations valid for that state. Everything else throws an explicit error.
- Adding a new denomination means adding one handler to the chain. No existing code changes.
Common Mistakes with ATM
- Using if/else chains on an enum instead of the State pattern. You end up with every method checking state, and forgetting one check means dispensing cash without a PIN.
- Not validating withdrawal amount against available denominations. $35 can't be dispensed with $20 and $10 bills alone.
- Forgetting to reset ATM state after card ejection or transaction failure. The machine gets stuck in an invalid state.
- Dispensing without checking if the ATM's physical cash supply covers the withdrawal. Balance check alone isn't enough.
Related Designs
Vending Machine, Digital Wallet, Payment System
Car Rental — Booking & Reservations
Difficulty: Intermediate
Strategy pattern for flexible pricing tiers and state pattern for vehicle lifecycle. Date-range availability search across a fleet of sedans, SUVs, and trucks.
Design Patterns for Car Rental
- Strategy Pattern
- State Pattern
- Observer Pattern
Key Abstractions in Car Rental
- RentalSystem: Orchestrator. Handles reservations, returns, and fleet queries through a single entry point.
- Vehicle: Entity with a type hierarchy (Sedan, SUV, Truck). Tracks plate, model, and current state.
- Reservation: Booking that binds a customer to a vehicle over a date range with a computed price.
- Customer: Renter profile with name, license, and contact details.
- PricingStrategy: Computes rental cost. Daily rate, weekly discount, insurance add-ons are all swappable strategies.
- VehicleState: State machine: Available, Reserved, Rented, Maintenance. Controls which operations are legal.
Key Points for Car Rental
- Strategy pattern for pricing: daily, weekly, and promotional rates are interchangeable without modifying reservation logic
- State pattern for vehicle lifecycle ensures illegal transitions (e.g., renting a vehicle under maintenance) are caught immediately
- Observer notifies the fleet dashboard when vehicle state changes, keeping availability search results accurate
- Date-range overlap check is the core of availability search. Two reservations conflict when start_a < end_b and start_b < end_a.
Common Mistakes with Car Rental
- Storing vehicle availability as a boolean. A vehicle can be available today but reserved for next week. You need date-range checks.
- Putting pricing logic inside Reservation. That makes it impossible to swap pricing strategies or add promotional rates without touching bookings.
- Skipping the state pattern and using string flags. You end up with scattered if-else blocks that miss edge cases like double-renting.
- Not handling reservation cancellations. A cancelled reservation must transition the vehicle back to Available for that date range.
Related Designs
Hotel Management, Parking Lot
Chess — Games & Simulations
Difficulty: Advanced
Each piece owns its move generation via Strategy. Command pattern wraps every move so you can undo it during check validation. Board stays clean.
Design Patterns for Chess
- Strategy Pattern
- Command Pattern
- Template Method
Key Abstractions in Chess
- Game: Turn management, game state transitions, coordinates between board and move validation
- Board: 8x8 grid, executes and undoes moves, tracks piece positions by color
- Piece: Abstract base with color, position, and delegated move generation
- King / Queen / Rook / Bishop / Knight / Pawn: Concrete pieces, each implementing its own legal move generation
- MoveValidator: Filters pseudo-legal moves by checking if they leave the king in check
- Move: Value object with from/to squares, captured piece, and special flags (castling, promotion)
- GameState: Enum: IN_PROGRESS, CHECK, CHECKMATE, STALEMATE
Key Points for Chess
- Each piece generates its own pseudo-legal moves. MoveValidator filters out ones that leave the king exposed.
- Command pattern on Move allows execute/undo. You need undo to test whether a candidate move leaves you in check.
- Board tracks all pieces by color for fast iteration when checking if any enemy piece attacks the king's square.
- GameState is computed after every move: if the opponent has no legal moves, it's either checkmate or stalemate.
Common Mistakes with Chess
- Putting move generation logic in the Board class instead of each piece. You end up with one massive switch statement.
- Not implementing undo on moves. Without it, check detection requires cloning the entire board for every candidate move.
- Using inheritance for color (WhiteKing, BlackKing). Color is data, not behavior. Use composition.
- Forgetting that a move is only legal if it doesn't leave your own king in check, including discovered checks.
Related Designs
Tic Tac Toe, Snake & Ladder
Concert Ticket Booking — Booking & Reservations
Difficulty: Advanced
Seat locking with TTL prevents double-booking during the payment window. Optimistic concurrency for seat selection, pessimistic locks for confirmation. Tiered pricing by venue section.
Design Patterns for Concert Ticket Booking
- Strategy Pattern
- Observer Pattern
- State Pattern
Key Abstractions in Concert Ticket Booking
- BookingSystem: Orchestrator. Manages events, seat locking, booking confirmation, and cancellation flows.
- Event: Concert with a name, venue, date, and auto-generated seating layout from venue sections.
- Seat: Individual seat with section, row, number, and a state machine (Available/Locked/Booked).
- Booking: Confirmed reservation linking a user to specific seats. Tracks payment reference and total.
- PaymentGateway: Strategy interface for payment processing. Supports credit card, UPI, wallet backends.
- SeatLock: Temporary hold on seats during payment. Has a TTL. Expired locks auto-release seats.
Key Points for Concert Ticket Booking
- Temporary seat locking is the centerpiece. When a user selects seats and goes to payment, those seats are held for a configurable TTL. No one else can grab them during that window.
- Lock expiry prevents ghost holds. If payment is not completed within the timeout, seats automatically become available again. No manual cleanup needed.
- Strategy pattern for payment processing. Credit card, UPI, and digital wallet are different payment backends with the same interface: charge(amount) returns a receipt.
- Observer pattern for waitlist. When a sold-out event gets cancellations, waitlisted users are notified that seats opened up.
Common Mistakes with Concert Ticket Booking
- Skipping the lock step and going straight from available to booked. Payment takes seconds or minutes. During that gap, another user can book the same seat.
- Locks without expiry. A user locks seats, gets distracted, never pays. Those seats are stuck in limbo forever. Every lock needs a TTL.
- Checking availability and booking in separate non-atomic operations. Between the check and the write, another thread can grab the same seat.
- Flat seat list without sections. Concert venues have VIP, floor, and balcony tiers with different pricing. A flat list loses that structure entirely.
Related Designs
Movie Ticket Booking, Online Auction
Course Registration — Platform Scale
Difficulty: Intermediate
Prerequisite validation and capacity-based enrollment with pluggable waitlist strategies (FIFO vs priority). Schedule conflict detection prevents overlapping time slots before enrollment commits.
Design Patterns for Course Registration
- Observer Pattern
- Strategy Pattern
Key Abstractions in Course Registration
- RegistrationSystem: Orchestrator that coordinates enrollment, prerequisite checks, capacity limits, and waitlist management
- Course: Academic offering with a capacity cap, prerequisites list, schedule, and enrolled student roster
- Student: Enrollee with a set of completed courses, current enrollments, and a schedule
- Enrollment: Record linking a student to a course with status tracking: ENROLLED, WAITLISTED, DROPPED
- Schedule: Time slot conflict detector that prevents overlapping course times for a student
- WaitlistStrategy: Strategy for ordering waitlisted students: FIFO (first-come) or priority-based (GPA, seniority)
Key Points for Course Registration
- Prerequisite validation runs before capacity checks. No point reserving a seat if the student is not qualified.
- Schedule conflict detection happens before enrollment commits. Two courses at the same time slot are rejected upfront.
- Waitlist is strategy-driven. FIFO is default, but swapping to priority-based (by GPA or seniority) is a single strategy swap.
- Observer notifies waitlisted students when a seat opens up from a drop. The system auto-promotes the next eligible student.
Common Mistakes with Course Registration
- Checking capacity but not prerequisites. A student who has not passed Calc I should never get a seat in Calc II regardless of availability.
- Letting students enroll in time-conflicting courses. If two courses overlap on Tuesday 10-11am, the second enrollment must be rejected.
- Hardcoding FIFO waitlist logic. When the registrar wants priority-based promotion (seniors first), you have to rewrite the waitlist.
- Forgetting to notify waitlisted students on drop. Without observer, seats freed by drops sit empty until someone manually checks.
Related Designs
Library Management, Hotel Management
CricInfo — Platform Scale
Difficulty: Advanced
Observer for live score updates pushed to every connected client. State pattern manages match lifecycle transitions from NOT_STARTED through IN_PROGRESS and INNINGS_BREAK to COMPLETED.
Design Patterns for CricInfo
- Observer Pattern
- State Pattern
- Strategy Pattern
Key Abstractions in CricInfo
- CricketMatch: Top-level orchestrator holding teams, innings, toss result, lifecycle state, and observer management
- Team: Named squad of players with a batting order
- Player: Individual with accumulated batting stats (runs, balls faced) and bowling stats (wickets, runs conceded)
- Innings: One team's batting session containing overs, running total, and wicket count
- Over: Collection of up to 6 legal deliveries bowled by one bowler
- Scorecard: Live score tracker that computes batting card, bowling card, and summary from raw ball data
- MatchObserver: Observer interface for pushing live updates to subscribers: website, mobile app, TV overlay
Key Points for CricInfo
- Observer pattern pushes live score updates to website, mobile app, TV overlay, and push notifications simultaneously
- State pattern manages match lifecycle: NOT_STARTED to IN_PROGRESS to INNINGS_BREAK to COMPLETED with explicit transitions
- Scorecard recomputes from raw ball data on every call. No cached totals that can drift out of sync.
- Ball-by-ball event tracking is the atomic unit. Every aggregate (innings total, strike rate, economy) derives from individual deliveries.
Common Mistakes with CricInfo
- Caching total runs as a mutable field on Innings. One missed update and the scoreboard silently shows wrong numbers.
- Using string comparisons for match state transitions. An enum with a state machine prevents invalid jumps like COMPLETED to IN_PROGRESS.
- Putting all scoring logic inside the Match class. Match should delegate to Innings, which delegates to Over. Keep each level focused.
- Treating extras as regular runs. Wides and no-balls have different rules: a wide does not count as a legal delivery, so the over does not advance.
Related Designs
Notification System, Pub/Sub System
Digital Wallet — Financial & Payments
Difficulty: Intermediate
Double-entry bookkeeping for every transaction, idempotency keys to prevent duplicate transfers, and a strategy pattern for payment methods so adding UPI or crypto never touches the transfer logic.
Design Patterns for Digital Wallet
- Strategy Pattern
- Observer Pattern
- Command Pattern
Key Abstractions in Digital Wallet
- Wallet: Account with a balance, supports credit and debit with concurrency safety
- Transaction: Immutable record of a credit or debit with amount, type, and timestamp
- TransferService: Orchestrates P2P transfers using double-entry bookkeeping
- PaymentMethod: Strategy interface for bank transfer, card, UPI, and other funding sources
- TransactionLog: Append-only audit trail of every wallet operation
Key Points for Digital Wallet
- Double-entry bookkeeping: every transfer creates two entries, one debit and one credit. Balances always sum to zero.
- Idempotency keys on every transfer request. Retries are safe because duplicate keys are rejected.
- Strategy pattern for payment methods. Adding a new funding source means one new class, zero changes to TransferService.
- Command pattern stores each operation. Undo, audit trail, and replay all come from the same log.
Common Mistakes with Digital Wallet
- Single-entry bookkeeping. If you only debit the sender without crediting the receiver atomically, a crash leaves money in limbo.
- Missing idempotency keys. Network retries will double-charge users. Every external-facing endpoint needs one.
- Using floating point for money. Use Decimal or BigDecimal. Floating point rounding errors compound over thousands of transactions.
- Not locking wallets in a consistent order during transfers. Two concurrent transfers between Alice and Bob can deadlock.
Related Designs
Payment System, Splitwise
Elevator System — Real-World Systems
Difficulty: Intermediate
Multiple elevators with pluggable scheduling. Each elevator runs its own state machine while a central dispatcher assigns requests using the LOOK algorithm to prevent starvation.
Design Patterns for Elevator System
- Strategy Pattern
- Observer Pattern
- State Pattern
Key Abstractions in Elevator System
- ElevatorSystem: Facade that accepts floor requests and dispatches them to the best elevator
- Elevator: Manages its own state, current floor, direction, and pending stop queue
- Request: Captures a floor number and desired direction from a hall call
- Scheduler: Strategy for assigning incoming requests to elevators. Swappable.
- ElevatorState: Enum: IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPEN. Governs valid transitions.
Key Points for Elevator System
- LOOK algorithm over naive nearest-elevator: it services requests in one sweep direction before reversing, preventing starvation of distant floors
- Each elevator is an independent unit with its own lock and stop queue. Parallelism comes naturally.
- Scheduler is decoupled from Elevator. Single responsibility: one picks, the other moves.
- State enum makes transitions explicit. An elevator can't go from DOOR_OPEN to MOVING_DOWN without closing first.
Common Mistakes with Elevator System
- Putting scheduling logic inside Elevator. Now every elevator duplicates the same decision code, and swapping algorithms means editing every elevator.
- Using a flat list of pending floors without direction awareness. That leads to unnecessary direction changes and wasted trips.
- Not handling concurrent requests. Two people pressing buttons at the same time can corrupt the stop queue without proper locking.
- Forgetting the IDLE state. An elevator with no pending requests should stop moving, not keep sweeping endlessly.
Related Designs
Parking Lot, Traffic Signal
Food Delivery (Swiggy) — Platform Scale
Difficulty: Advanced
Order lifecycle via state machine, delivery agent assignment via strategy pattern, and real-time tracking via observer. A three-sided marketplace where coordination timing is everything.
Design Patterns for Food Delivery (Swiggy)
- Strategy Pattern
- Observer Pattern
- State Pattern
Key Abstractions in Food Delivery (Swiggy)
- DeliveryPlatform: Orchestrator that coordinates restaurants, customers, agents, and order lifecycle
- Restaurant: Menu provider that confirms or rejects incoming orders
- Customer: Orderer who places orders and receives delivery status updates
- DeliveryAgent: Driver with location and current load who picks up and delivers orders
- Order: Core entity with state machine: PLACED -> CONFIRMED -> PREPARING -> OUT_FOR_DELIVERY -> DELIVERED
- MenuItem: Individual food item with name, price, and availability flag
- AssignmentStrategy: Algorithm for picking a delivery agent: nearest, load-balanced, or rating-based
Key Points for Food Delivery (Swiggy)
- State machine on Order enforces valid transitions. An order cannot jump from PLACED to DELIVERED.
- Strategy pattern for agent assignment: swap between nearest-agent, load-balanced, or rating-based without changing order logic
- Observer notifies customer and restaurant on every status change for real-time tracking
- Restaurant confirmation is an explicit state. The order waits for the restaurant to accept before preparation begins.
Common Mistakes with Food Delivery (Swiggy)
- Skipping the CONFIRMED state. Without restaurant confirmation, you charge the customer before knowing if the food can be made.
- Assigning a delivery agent at order placement. The agent should be assigned when food is almost ready, not 30 minutes early.
- Hardcoding agent assignment logic. Load balancing during peak hours and nearest-agent during off-peak need different strategies.
- Not tracking agent load. Assigning five orders to one agent while others sit idle kills delivery times.
Related Designs
Ride Sharing, Restaurant Management
HashMap — Foundational
Difficulty: Starter
An array of buckets, a hash function, and linked lists for collisions. Simple pieces that combine into the most used data structure in software engineering.
Design Patterns for HashMap
- Strategy Pattern
- Iterator Pattern
Key Abstractions in HashMap
- HashMap: Public API for put, get, remove. Manages capacity and triggers resizing.
- Bucket: Linked list chain holding entries that hash to the same index
- Entry: Key-value-hash triple. Stores the precomputed hash to avoid rehashing during resize.
- HashFunction: Strategy interface for computing hash codes. Swappable for different key types.
Key Points for HashMap
- Separate chaining handles collisions by appending to a linked list at each bucket index.
- Power-of-2 capacity lets you replace modulo with bitwise AND. index = hash & (capacity - 1) is faster.
- Rehashing at 0.75 load factor keeps average chain length short, preserving O(1) amortized access.
- Storing the hash in each Entry avoids recomputing it during resize. Small cost, big win at scale.
Common Mistakes with HashMap
- Using key equality without checking hash first. Compare hashes before calling equals() to short-circuit expensive comparisons.
- Forgetting to handle null keys or duplicate keys on put(). Duplicates should update the value, not create a second entry.
- Not resizing. Without resize, chains grow linearly and your O(1) map degrades to O(n).
- Rehashing with the old capacity instead of the new one. Every entry must be re-indexed against the new size.
Related Designs
LRU Cache, Rate Limiter
Hotel Management — Booking & Reservations
Difficulty: Intermediate
Room inventory with date-range bookings and pluggable pricing. Strategy handles seasonal rates and weekend premiums. Factory creates room types. Observer fires check-in/out events.
Design Patterns for Hotel Management
- Strategy Pattern
- Factory Method
- Observer Pattern
Key Abstractions in Hotel Management
- Hotel: Facade. Coordinates room inventory, bookings, and guest management.
- Room: Physical room with number, type, floor, and amenity list.
- RoomType: Enum: SINGLE, DOUBLE, SUITE. Drives pricing and capacity.
- Reservation: Binds a guest to a room for a check-in/check-out date range.
- Guest: Contact info and reservation history.
- PricingStrategy: Interface for rate calculation: standard, seasonal, dynamic.
- BookingService: Checks date-range availability and creates reservations with concurrency control.
Key Points for Hotel Management
- Date-range availability checking is the core complexity. A room is available only if no existing reservation overlaps the requested date range.
- Strategy pattern for pricing. Standard pricing uses a flat rate per room type. Seasonal pricing applies multipliers for holidays. Weekend pricing bumps Friday/Saturday rates.
- Factory method for room creation. Adding a PENTHOUSE type means one new case in the factory, not scattered construction logic.
- Observer fires events on check-in and check-out. Housekeeping subscribes to check-out. Billing subscribes to check-in for deposit capture.
Common Mistakes with Hotel Management
- Checking availability by a single date instead of a date range. A room booked Jan 5-8 is unavailable on Jan 6, even though no reservation starts on that date.
- Putting pricing logic inside the Room class. Room describes physical attributes. Pricing is a business rule that changes by season, by demand, by customer loyalty tier.
- No overlap detection on reservations. Two guests end up booked into the same room for overlapping dates. Classic concurrency bug.
- Using inheritance for room types instead of composition. SINGLE, DOUBLE, SUITE differ in attributes, not behavior. An enum plus a config record works better.
Related Designs
Restaurant Management, Concert Ticket Booking
In-Memory File System — Platform Scale
Difficulty: Intermediate
Composite pattern for hierarchical file/directory structure with path resolution. Files and directories share a common interface, so recursive operations like size calculation and deletion work uniformly across the tree.
Design Patterns for In-Memory File System
- Composite Pattern
- Iterator Pattern
Key Abstractions in In-Memory File System
- FileSystem: Facade with root directory and path resolution. All operations go through here.
- FileSystemEntry: Abstract base for File and Directory. Enables uniform treatment across the tree.
- File: Leaf node holding a name and string content
- Directory: Composite node with a children map for O(1) child lookup by name
- Path: Utility for parsing and resolving absolute path strings into tree traversals
Key Points for In-Memory File System
- Composite pattern lets files and directories share a common interface. Recursive operations like size() work without type-checking.
- Children stored as HashMap, not a list. Name-based lookups are O(1) instead of scanning every entry.
- Path resolution walks the tree from root by splitting on '/'. Missing segments fail fast with clear errors.
- FileSystemEntry is abstract with polymorphic behavior. No instanceof calls in client code.
Common Mistakes with In-Memory File System
- Using isinstance/instanceof checks everywhere instead of polymorphism. Adding a new entry type means touching every operation.
- Storing children in a list. Linear scan for every lookup is fine for 10 files, painful for 10,000.
- Forgetting that rm on a directory should be recursive. Leaving orphaned children is a silent resource leak.
- Not validating paths. Operations on '/foo/bar' should fail cleanly when '/foo' does not exist.
Related Designs
Stack Overflow, Logging Framework
Library Management — Real-World Systems
Difficulty: Intermediate
Separate the catalog from physical copies. A Book is metadata. A BookCopy is the thing on the shelf. This distinction drives every other design decision in the system.
Design Patterns for Library Management
- Observer Pattern
- Strategy Pattern
Key Abstractions in Library Management
- Library: Facade coordinating books, members, loans, and search
- Book: Catalog entity with title, author, ISBN. Has multiple physical copies.
- BookCopy: Individual physical copy with its own availability status
- Member: Borrower with checkout limits and active loan tracking
- Loan: Checkout record linking a member to a book copy with due date and fine calculation
- SearchStrategy: Strategy interface for searching by title, author, or ISBN
Key Points for Library Management
- Book vs BookCopy separation mirrors real libraries. Five people can request the same title because five copies exist independently.
- Strategy pattern on search lets you add new search criteria without modifying the Library class.
- Fine calculation is its own strategy. Flat rate per day, tiered rates, or grace periods are all just different implementations.
- Observer pattern notifies members when a reserved book becomes available. No polling required.
Common Mistakes with Library Management
- Treating Book and BookCopy as one entity. Then you cannot track which specific copy is checked out or damaged.
- Hardcoding checkout limits. Different membership tiers should have different limits.
- Calculating fines at checkout time instead of return time. Fines depend on how late the book actually comes back.
- Not handling the case where a member returns a book that someone else has reserved. The reservation queue needs to trigger automatically.
Related Designs
Hotel Management, Car Rental
Logging Framework — Foundational
Difficulty: Starter
Chain of Responsibility for handlers, Strategy for formatters, Singleton for the root logger. Three patterns solving three orthogonal concerns in one system.
Design Patterns for Logging Framework
- Chain of Responsibility
- Singleton Pattern
- Strategy Pattern
Key Abstractions in Logging Framework
- Logger: Singleton root logger. Accepts log calls and dispatches to the handler chain.
- LogLevel: Enum (DEBUG, INFO, WARN, ERROR) with ordinal comparison for filtering
- LogHandler: Abstract handler in the chain. Each handler decides whether to process and/or pass along.
- ConsoleHandler: Writes formatted log records to stdout
- FileHandler: Appends formatted log records to a file
- LogFormatter: Strategy interface for formatting. JSON, plain text, or custom formats.
- LogRecord: Immutable value object carrying timestamp, level, message, and logger name
Key Points for Logging Framework
- Chain of Responsibility lets you stack handlers. A log record flows through console, file, and remote handlers without the logger knowing how many exist.
- Each handler has its own minimum level. Console might show DEBUG, while file only captures WARN and above.
- Singleton ensures one logger instance across the entire application. All modules log through the same configured pipeline.
- LogRecord is immutable. Once created, handlers can read it freely without synchronization concerns.
Common Mistakes with Logging Framework
- Making the logger non-thread-safe. Multiple threads writing to the same handler without synchronization produces garbled output.
- Formatting inside the logger instead of delegating to the handler's formatter. That couples the logger to a specific output format.
- Forgetting to flush or close file handlers. Buffered writes get lost on crash if you never flush.
- Using string concatenation for log messages even when the level is filtered out. Build the message lazily or check the level first.
Related Designs
Notification System, Pub/Sub System
LRU Cache — Foundational
Difficulty: Starter
Doubly linked list plus hashmap. One gives you O(1) lookups, the other gives you O(1) eviction ordering. Together they solve the cache problem.
Design Patterns for LRU Cache
- Strategy Pattern
- Template Method
Key Abstractions in LRU Cache
- Cache: Public API for get and put, enforces capacity limits
- EvictionPolicy: Strategy interface so you can swap LRU for LFU or FIFO
- DoublyLinkedList: Maintains access ordering with O(1) move-to-front
- Node: Wraps key-value pairs with prev/next pointers
Key Points for LRU Cache
- HashMap for O(1) lookup, DLL for O(1) eviction ordering. Together they cover both needs.
- Strategy pattern makes the eviction policy swappable without touching cache internals
- The DLL tail is always the eviction candidate. No scanning required.
- Move-to-front on both get() and put() keeps the ordering correct
Common Mistakes with LRU Cache
- Forgetting to update node position on get(), not just put(). Stale ordering breaks LRU correctness.
- Not removing the evicted key from the hashmap after DLL removal. That's a silent memory leak.
- Skipping thread safety. Production caches need locks or concurrent structures around both the map and list.
- Ignoring capacity=0 edge case. Should reject all puts gracefully.
Related Designs
Rate Limiter, HashMap, Notification System
Movie Ticket Booking — Booking & Reservations
Difficulty: Advanced
Concurrent seat booking with TTL-based locks and dynamic pricing. Facade pattern wraps cinemas, shows, and seat selection into a clean BookMyShow-style API.
Design Patterns for Movie Ticket Booking
- Strategy Pattern
- Observer Pattern
- State Pattern
Key Abstractions in Movie Ticket Booking
- BookMyShow: Facade. Single entry point for searching shows, locking seats, and confirming bookings.
- Cinema: Theater with a name, location, and multiple screens. Each screen runs independent shows.
- Show: A movie playing on a specific screen at a specific time. Owns the seat map for that screening.
- Seat: Individual seat with type (Regular, Premium, VIP), row, number, and state machine.
- Booking: Confirmed reservation with status transitions: Pending, Confirmed, Cancelled.
- PricingStrategy: Dynamic pricing by show time, seat type, and day of week. Matinee vs evening, weekday vs weekend.
Key Points for Movie Ticket Booking
- Seat locking with TTL is non-negotiable for concurrent bookings. When a user selects seats and goes to checkout, those seats must be held. Without locking, two users pay for the same seat.
- Dynamic pricing via strategy pattern. A Friday night show charges more than a Tuesday matinee. VIP seats cost more than regular. These rules compose without touching the booking flow.
- Show scheduling per screen prevents overlaps. A screen cannot run two movies at the same time. The system validates time slots before creating a show.
- Observer notifies users when a sold-out show gets cancellations. Waitlisted users get a push notification with available seat count.
Common Mistakes with Movie Ticket Booking
- Making Cinema a God class that manages seats directly. Cinema owns screens, screens own shows, shows own seats. Three levels of delegation, not one.
- Flat pricing for all shows. A 10 AM Tuesday matinee and a 9 PM Friday premiere have very different demand curves. Pricing must reflect this.
- No TTL on seat locks. A user locks 4 seats for a blockbuster premiere, then puts their phone down. Those seats are gone for every other user until you manually intervene.
- Skipping show-time validation. Two shows on the same screen at overlapping times means double-selling the same physical seats.
Related Designs
Concert Ticket Booking, Restaurant Management
Music Streaming (Spotify) — Platform Scale
Difficulty: Advanced
Iterator for playlist traversal with pluggable playback strategies (shuffle, repeat, sequential). Observer pushes now-playing updates to all connected UI components without the player knowing who is listening.
Design Patterns for Music Streaming (Spotify)
- Observer Pattern
- Strategy Pattern
- Iterator Pattern
Key Abstractions in Music Streaming (Spotify)
- MusicService: Facade coordinating user actions, playlist management, and playback
- Song: Immutable metadata (title, artist, album, duration) plus a reference to the audio source
- Playlist: Ordered collection of songs with an owner, add/remove, and iterator creation
- User: Listener with a subscription tier, owned playlists, and listening history
- Player: Playback controller managing current song, play/pause/skip state, and observer notifications
- PlaybackStrategy: Defines how the next song is picked: sequential, shuffle, or repeat
- RecommendationEngine: Suggests songs based on listening history and what the user has not heard yet
Key Points for Music Streaming (Spotify)
- Iterator pattern decouples playlist traversal from the playlist data structure. The player calls next() and does not care about order logic.
- Strategy pattern makes playback modes (shuffle, repeat, sequential) swappable at runtime without touching the Player class
- Observer pattern pushes now-playing updates to UI, social feeds, and scrobbling services simultaneously
- Playlist is a first-class entity with its own identity and encapsulation, not just a bare list of song IDs
- Song is immutable. It holds a reference to the audio, not the bytes. Safe to share across playlists without copying.
Common Mistakes with Music Streaming (Spotify)
- Baking shuffle logic directly into the Player. Adding repeat-one later means rewriting the play loop.
- Storing full audio data inside the Song object. Songs should reference audio, not contain it.
- Skipping an iterator and using raw index math for playlist traversal. Shuffle and repeat edge cases break immediately.
- Making Playlist a plain list with no encapsulation. You lose the ability to enforce ordering rules or notify on changes.
Related Designs
Notification System, Pub/Sub System
Notification System — Platform Scale
Difficulty: Intermediate
Multi-channel notification delivery with user preferences and templated messages. Strategy pattern selects the channel, Template Method standardizes formatting, and Observer triggers notifications from domain events.
Design Patterns for Notification System
- Observer Pattern
- Strategy Pattern
- Template Method
Key Abstractions in Notification System
- NotificationService: Orchestrator that resolves user preferences, selects channels, renders templates, and dispatches delivery
- NotificationChannel: Strategy interface for delivery. Email, SMS, and Push each implement send() differently.
- Template: Message template with named placeholders. Renders final content by substituting context values.
- UserPreference: Per-user channel preferences. Controls which channels a user receives notifications on.
- NotificationQueue: Async delivery buffer with retry logic. Decouples notification creation from actual sending.
Key Points for Notification System
- Strategy pattern makes channels pluggable. Adding a new channel means one new class, zero changes to the orchestrator.
- Template Method standardizes message formatting. Every channel follows the same render-then-send flow, but each customizes the rendering step.
- User preferences are first-class. Never blast every channel. Respect what the user opted into.
- Async queue with retry handles transient failures. SMTP timeouts and push gateway errors are inevitable.
Common Mistakes with Notification System
- Hardcoding channel logic in the service. Every new channel turns into another if-else branch that nobody wants to touch.
- Skipping user preferences. Sending push notifications to users who disabled them is a fast path to uninstalls.
- Synchronous delivery in the request path. An SMTP timeout should not cause a 500 on your checkout endpoint.
- No retry mechanism. Transient failures in SMS gateways or push services are normal. Dropping notifications silently is not.
Related Designs
Pub/Sub System, Task Management
Online Auction — Platform Scale
Difficulty: Intermediate
Auction platform with time-based bidding, automatic expiry, and winner determination. State machine controls the auction lifecycle while Observer keeps bidders notified in real time.
Design Patterns for Online Auction
- State Pattern
- Observer Pattern
- Strategy Pattern
Key Abstractions in Online Auction
- AuctionService: Facade, single entry point for creating auctions, placing bids, and closing
- Auction: Core entity holding item, bids, time window, and current lifecycle state
- AuctionStatus: Enum for lifecycle states: SCHEDULED, ACTIVE, CLOSED, CANCELLED
- Bid: Immutable record of bidder, amount, and timestamp
- Item: What is being auctioned: name, description, starting price
- User: Participant who can be a seller or bidder
- AuctionCloser: Determines winner from bids, triggers settlement notifications
Key Points for Online Auction
- State pattern for auction lifecycle prevents bids on closed or cancelled auctions at the type level
- Observer pattern notifies outbid users and announces auction results without coupling to notification delivery
- Strategy pattern supports multiple auction types: English ascending, Dutch descending, sealed-bid
- Bid validation is a separate concern from bid storage because business rules change independently
Common Mistakes with Online Auction
- Letting anyone bid on a CLOSED or CANCELLED auction. State pattern makes invalid transitions impossible.
- Not validating that a new bid exceeds the current highest. You end up with garbage data and angry sellers.
- Coupling notification logic into the Auction class. When you add SMS or push, you are rewriting auction code.
- Using strings for auction status instead of enums. One typo and your state transitions silently break.
Related Designs
Stock Brokerage, Concert Ticket Booking
Online Shopping — Platform Scale
Difficulty: Advanced
Product catalog, shopping cart, and order pipeline with inventory management. Cart is mutable and references products by ID. Orders are immutable snapshots with reserved inventory.
Design Patterns for Online Shopping
- Strategy Pattern
- State Pattern
- Observer Pattern
Key Abstractions in Online Shopping
- ShoppingService: Facade, single entry point for browsing, cart operations, and order placement
- Product: Catalog item with name, price, description, and inventory count
- Cart: User's mutable collection of items and quantities before checkout
- CartItem: Links a product reference to a desired quantity in the cart
- Order: Immutable snapshot created from cart at checkout, with shipping and payment details
- OrderStatus: Enum for order lifecycle: PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
- InventoryManager: Tracks stock levels and handles reservation on order placement
- DiscountStrategy: Pricing modifier: percentage off, flat discount, coupon-based
Key Points for Online Shopping
- Cart items reference products by ID, not by object, so price changes reflect at checkout time
- Inventory reservation happens at order placement to prevent overselling across concurrent checkouts
- Order is an immutable snapshot of the cart. Cart keeps changing after checkout, but the order does not.
- Observer pattern sends low-stock alerts and order status notifications without coupling to inventory logic
Common Mistakes with Online Shopping
- Storing product objects directly in the cart. If the price updates, the cart shows stale data.
- Not reserving inventory at checkout. Two users checking out the last item both succeed and one gets nothing.
- Making Order mutable after creation. Orders should be a frozen record of what the customer agreed to.
- Hardcoding discount logic. Promotions change weekly. Strategy pattern keeps pricing rules swappable.
Related Designs
Movie Ticket Booking, Payment System
Parking Lot — Real-World Systems
Difficulty: Intermediate
Multi-floor, multi-vehicle-type parking with real-time availability. Layered domain model where each class owns exactly one responsibility.
Design Patterns for Parking Lot
- Strategy Pattern
- Factory Method
- Observer Pattern
Key Abstractions in Parking Lot
- ParkingLot: Facade, single entry point for park/unpark operations
- ParkingFloor: Manages spots on one level, knows availability by type
- ParkingSpot: Individual slot that knows its type, size, and occupant
- Vehicle: Abstract base. Car, Truck, Motorcycle with different size requirements.
- ParkingStrategy: Spot selection algorithm: nearest, spread-out, or type-optimized
- Ticket: Proof of parking, links vehicle to spot with entry time
Key Points for Parking Lot
- Strategy pattern for spot selection: nearest-to-entrance vs spread-load vs type-optimized
- Factory method for vehicle creation avoids switch statements scattered across the codebase
- Observer notifies display boards when availability changes, decoupled from parking logic
- Enum for SpotType, not strings. Compile-time safety on slot size matching.
Common Mistakes with Parking Lot
- Treating ParkingLot as a God class. It should delegate to floors, not manage individual spots.
- Using a flat list of spots instead of floor-based grouping. That kills O(1) availability lookups per floor.
- Hardcoding spot assignment logic makes it impossible to change allocation strategy without rewriting core
- Forgetting concurrency. Two cars arriving at the same time can be assigned the same spot without locks.
Related Designs
Elevator System, Traffic Signal, Car Rental
Payment System — Financial & Payments
Difficulty: Advanced
Strategy pattern for payment methods so adding UPI or crypto is one new class, chain of responsibility for layered fraud checks, a state machine enforcing payment lifecycle transitions, and idempotency keys to prevent double charges.
Design Patterns for Payment System
- Strategy Pattern
- Chain of Responsibility
- Observer Pattern
Key Abstractions in Payment System
- PaymentProcessor: Orchestrator that validates, fraud-checks, and routes payments through the correct method
- PaymentMethod: Strategy interface with subtypes CreditCard, DebitCard, UPI, and NetBanking
- PaymentGateway: Adapter wrapping external payment providers behind a uniform charge/refund interface
- Transaction: Core entity with a state machine governing its lifecycle from INITIATED through COMPLETED or FAILED
- FraudChecker: Chain of responsibility for layered validation: velocity checks, amount limits, blacklist matching
Key Points for Payment System
- Strategy for payment methods: each method validates itself and talks to its own gateway. Adding a new method never touches PaymentProcessor.
- Chain of responsibility for fraud checks. Each checker either approves the payment and passes it along, or rejects it. New rules are new links in the chain.
- State machine on Transaction enforces legal transitions. PROCESSING cannot jump to REFUNDED. FAILED cannot become COMPLETED.
- Idempotency keys prevent double charges. If a key was already processed, return the existing result without re-executing.
Common Mistakes with Payment System
- Hardcoding payment methods with if/else chains. Every new method means modifying the processor, which means risk and merge conflicts.
- Running fraud checks after charging the customer. Validate before you touch their money, not after.
- Using a free-form status string instead of an enum with enforced transitions. You will eventually see FAILED payments magically become COMPLETED.
- Missing idempotency on the charge endpoint. Network retries are a fact of life, and without key-based deduplication, users get double-charged.
Related Designs
Digital Wallet, ATM
Pub/Sub System — Platform Scale
Difficulty: Advanced
Topic-based message broker with subscriber management. Observer pattern at its core, Strategy pattern for delivery semantics. Decouples producers from consumers completely.
Design Patterns for Pub/Sub System
- Observer Pattern
- Strategy Pattern
Key Abstractions in Pub/Sub System
- MessageBroker: Facade coordinating topic creation, subscriptions, and message routing
- Topic: Named channel that holds a subscriber list and dispatches messages
- Subscriber: Interface with an on_message callback. Any class can subscribe by implementing it.
- Publisher: Sends messages to specific topics through the broker
- Message: Immutable payload with topic, content, timestamp, and unique ID for deduplication
- SubscriptionManager: Tracks topic-to-subscriber mappings, handles subscribe/unsubscribe lifecycle
Key Points for Pub/Sub System
- Observer pattern is the backbone. Topics maintain subscriber lists and notify on publish.
- Strategy pattern controls delivery semantics: synchronous vs asynchronous, at-most-once vs at-least-once.
- Message IDs enable deduplication. Without them, network retries cause duplicate processing.
- Topic-based routing chosen over content-based for predictability. Subscribers know exactly what they signed up for.
Common Mistakes with Pub/Sub System
- Making subscribers concrete classes instead of interfaces. Every new integration requires modifying the broker.
- No message IDs. Retries and redeliveries silently duplicate work without deduplication support.
- Synchronous-only delivery. One slow subscriber blocks all others on the same topic.
- Forgetting thread safety on the subscriber list. Concurrent subscribe and publish causes ConcurrentModificationException.
Related Designs
Notification System, Logging Framework
Rate Limiter — Foundational
Difficulty: Intermediate
Token bucket for smooth throughput, sliding window for strict counting. Strategy pattern lets you swap algorithms without rewiring the system.
Design Patterns for Rate Limiter
- Strategy Pattern
- Factory Pattern
Key Abstractions in Rate Limiter
- RateLimiter: Facade that checks if a request is allowed and delegates to the active strategy
- RateLimitStrategy: Interface for swappable algorithms. Each strategy decides allow or reject independently.
- TokenBucketStrategy: Refills tokens at a fixed rate, each request costs one token. Allows controlled bursts.
- SlidingWindowStrategy: Tracks timestamps in a rolling window. Strict count-based limiting with no burst allowance.
- RateLimitConfig: Immutable config holding capacity, refill rate, and window size parameters
Key Points for Rate Limiter
- Token bucket allows short bursts up to bucket capacity, then throttles to the refill rate. Good for APIs that tolerate spikes.
- Sliding window counts requests in a rolling time window. Stricter than token bucket but uses more memory per client.
- Strategy pattern means you pick the algorithm at config time. Changing from token bucket to sliding window requires zero code changes in the limiter itself.
- Lazy token refill avoids background threads. Calculate tokens earned since last request on each allow() call.
Common Mistakes with Rate Limiter
- Using a fixed window instead of sliding. Requests at the boundary of two windows can double your actual rate. A client can send N requests at 11:59 and N more at 12:00.
- Running a timer thread to refill tokens. Wasteful. Compute the refill lazily when allow() is called by checking elapsed time.
- Forgetting thread safety on the token count. Two concurrent requests can both read tokens=1 and both pass, exceeding the limit.
- Not cleaning up old timestamps in sliding window. Without pruning, memory grows without bound for high-traffic clients.
Related Designs
LRU Cache, URL Shortener, Notification System
Restaurant Management — Booking & Reservations
Difficulty: Intermediate
Table allocation, order lifecycle, and kitchen queue coordination. Strategy picks tables, State drives orders through their lifecycle, and Observer wires the kitchen to the wait staff.
Design Patterns for Restaurant Management
- Strategy Pattern
- State Pattern
- Observer Pattern
Key Abstractions in Restaurant Management
- Restaurant: Facade. Entry point for reservations, ordering, and billing.
- Table: Physical seating unit with a capacity and occupancy status.
- Reservation: Binds a party to a future time slot and table assignment.
- Order: Collection of menu items moving through PLACED/PREPARING/READY/SERVED states.
- MenuItem: Name, price, category. Immutable value object.
- Kitchen: Processes order queue. Notifies observers when an order is ready.
- Bill: Aggregates order totals, applies tax and optional tip.
Key Points for Restaurant Management
- Strategy pattern for table allocation: first-fit grabs the first table that fits the party, best-fit picks the smallest adequate table to minimize wasted seats
- State pattern on Order so each status transition is explicit. PLACED can move to PREPARING, PREPARING to READY, READY to SERVED. No random jumps.
- Observer decouples Kitchen from wait staff. Kitchen fires an event when food is ready. The waiter listener handles delivery. Adding a notification to the customer's phone is just another observer.
- Enums for OrderStatus and TableStatus. No magic strings drifting through the codebase.
Common Mistakes with Restaurant Management
- Letting Restaurant manage individual order state transitions. That's the Order's job. Restaurant should delegate.
- Skipping the Kitchen abstraction and having Order prepare itself. That conflates what you ordered with how it gets made.
- Using strings for order status instead of an enum. One typo ('PREPRING') and your state machine silently breaks.
- Forgetting thread safety on table assignment. Two hosts seating parties simultaneously can double-book the same table.
Related Designs
Hotel Management, Food Delivery
Ride Sharing (Uber) — Platform Scale
Difficulty: Advanced
Driver matching via strategy pattern, ride lifecycle via state machine, and surge pricing that adapts to demand. The core of any ride-hailing platform in clean OOP.
Design Patterns for Ride Sharing (Uber)
- Strategy Pattern
- Observer Pattern
- State Pattern
Key Abstractions in Ride Sharing (Uber)
- RideService: Orchestrator that coordinates rider requests, driver matching, and ride lifecycle
- Rider: Requester who initiates a ride with pickup and dropoff locations
- Driver: Provider with real-time location, rating, and availability status
- Ride: Core entity with state machine: REQUESTED -> MATCHED -> EN_ROUTE -> IN_PROGRESS -> COMPLETED
- MatchingStrategy: Algorithm for selecting a driver: nearest-first, rating-based, or hybrid
- PricingStrategy: Fare calculation: distance-based flat rate or surge pricing during peak demand
- Location: Lat/lng coordinate pair with Haversine distance calculation
Key Points for Ride Sharing (Uber)
- Strategy pattern for driver matching: swap between nearest-driver, highest-rated, or load-balanced without touching core logic
- State machine on Ride enforces valid transitions. You cannot go from REQUESTED to IN_PROGRESS directly.
- Observer pattern notifies riders of driver location updates and ride status changes
- Pricing strategy is decoupled from ride logic. Surge pricing multiplier is a strategy swap, not an if-else chain.
Common Mistakes with Ride Sharing (Uber)
- Putting matching logic inside RideService directly. That becomes unmaintainable when you need A/B testing between matching algorithms.
- Using string-based ride status instead of an enum with enforced transitions. You will end up with rides in impossible states.
- Calculating distance with Euclidean formula instead of Haversine. Lat/lng coordinates are not Cartesian.
- Forgetting to mark a driver as unavailable after matching. Two riders can get matched to the same driver.
Related Designs
Food Delivery, Parking Lot
Snake & Ladder — Games & Simulations
Difficulty: Starter
Board with position jumps, configurable snakes and ladders, and a dice strategy so you can swap fair for loaded. Builder pattern keeps board construction clean.
Design Patterns for Snake & Ladder
- Strategy Pattern
- Builder Pattern
- Observer Pattern
Key Abstractions in Snake & Ladder
- Game: Orchestrator managing player turns, dice rolls, and win detection
- Board: Grid of cells with registered snakes and ladders, resolves final position after a move
- Player: Tracks name, current position, and move history
- Dice: Strategy interface for rolling. FairDice gives uniform distribution, LoadedDice can be weighted.
- Snake: Position jump where start > end. Landing on head sends you to tail.
- Ladder: Position jump where start < end. Landing on bottom sends you to top.
- GameState: Enum tracking IN_PROGRESS, WON, or DRAW
- BoardBuilder: Fluent builder for constructing boards with validated snake and ladder placement
Key Points for Snake & Ladder
- Snakes and ladders are both position jumps. A Snake has start > end, a Ladder has start < end. Same interface, different direction.
- Strategy pattern on Dice lets you inject fair, loaded, or deterministic dice for testing.
- Builder pattern for Board handles validation: no snake head at a ladder bottom, no cycles, no overlapping positions.
- Board is separate from Game so you can test position resolution without dealing with turns or players.
Common Mistakes with Snake & Ladder
- Putting snake/ladder logic directly in Game instead of Board. Makes it impossible to test board rules in isolation.
- Using a raw integer for dice without an abstraction. You lose the ability to inject test doubles.
- Forgetting to handle the case where a player rolls past position 100 and should stay put.
- Allowing a snake head and ladder bottom on the same cell. That's an ambiguous board state.
Related Designs
Tic Tac Toe, Chess
Splitwise — Financial & Payments
Difficulty: Advanced
Expense splitting with multiple strategies, debt simplification via a greedy graph algorithm, and a balance ledger that tracks net amounts instead of individual transactions.
Design Patterns for Splitwise
- Strategy Pattern
- Observer Pattern
- Command Pattern
Key Abstractions in Splitwise
- ExpenseService: Facade for creating expenses and querying balances
- Expense: Immutable record of who paid, how much, and the split breakdown
- SplitStrategy: Strategy interface for equal, exact, and percentage split calculations
- BalanceLedger: Net balance tracker with O(1) lookup per user pair
- DebtSimplifier: Greedy graph algorithm to minimize number of settlements
- User: Participant with name and unique ID
Key Points for Splitwise
- Strategy pattern for split logic: equal, exact, and percentage splits without if/else chains
- Balance ledger stores net amounts, not individual transactions. O(1) per-pair lookup.
- Debt simplification is a greedy graph problem. Sort creditors and debtors, match greedily.
- Command pattern records expense history, so undo support and audit trail come for free
Common Mistakes with Splitwise
- Storing gross amounts instead of net balances. That leads to O(n) scan on every 'who owes whom' query.
- Not validating that split amounts sum to the expense total. You get silent balance drift over time.
- Treating percentage splits as exact after rounding. The last person should absorb the rounding error.
- Forgetting that the payer can also be a participant in the split. Common off-by-one on share count.
Related Designs
Digital Wallet, Payment System, ATM
Stack Overflow — Platform Scale
Difficulty: Advanced
Q&A platform with reputation system, voting mechanics, and search ranking. Observer pattern for reputation events, Strategy pattern for search, and State pattern for question lifecycle. Reputation thresholds gate privileged actions like voting and closing.
Design Patterns for Stack Overflow
- Observer Pattern
- Strategy Pattern
- State Pattern
Key Abstractions in Stack Overflow
- Platform: Facade coordinating users, questions, answers, votes, tags, and search
- Question: Holds title, body, tags, answers list, vote count, and open/closed state
- Answer: Response to a question with its own vote count and accepted/rejected status
- User: Author with reputation score that gates privileged actions like voting and closing
- Vote: Upvote or downvote with reputation effects on both voter and target author
- Tag: Categorization label attached to questions for filtering and trending
- SearchStrategy: Strategy interface for ranking search results by relevance, votes, or recency
Key Points for Stack Overflow
- Reputation is the central mechanic. It gates who can vote, comment, edit, and close questions. Without thresholds, the system has no quality control.
- Observer pattern propagates reputation changes. When a vote happens, both the voter and the content author get reputation adjustments.
- Strategy pattern for search ranking. Users switch between relevance, votes, and date sorting without changing the search infrastructure.
- State pattern governs question lifecycle. Open questions accept answers. Closed questions reject them. The state object enforces this cleanly.
Common Mistakes with Stack Overflow
- Letting anyone vote without reputation thresholds. Sock puppet accounts and vote manipulation become trivial.
- Computing reputation on the fly from vote history every time. This is expensive. Store it as a running total and update incrementally.
- Hardcoding search ranking logic. Relevance, votes, and date sorting should be swappable strategies, not if-else chains.
- No separation between vote action and reputation effect. Changing the reputation formula means hunting through every place votes are cast.
Related Designs
Notification System, Task Management
Stock Brokerage — Financial & Payments
Difficulty: Advanced
Price-time priority order matching with per-stock order books, sorted buy/sell queues for O(log n) insertion, partial fill tracking, and Observer notifications on every executed trade.
Design Patterns for Stock Brokerage
- Observer Pattern
- Strategy Pattern
- Command Pattern
Key Abstractions in Stock Brokerage
- Exchange: Top-level matching engine that routes orders to the correct order book and updates portfolios
- Order: Buy or sell request with price, quantity, and fill tracking. MarketOrder and LimitOrder are subtypes.
- OrderBook: Per-stock bid/ask queues sorted by price-time priority, with its own lock for parallel matching
- Portfolio: User's cash balance and stock holdings, updated atomically on each trade
- MatchingEngine: Core logic inside OrderBook that pairs incoming orders against resting counter-orders
- Trade: Immutable record of an executed match: buyer, seller, price, quantity, timestamp
Key Points for Stock Brokerage
- One OrderBook per stock. AAPL and GOOGL match independently, so they can run in parallel without contention.
- Price-time priority: best price wins. Ties broken by earliest submission. This is how NYSE and NASDAQ work.
- Market orders match immediately at the best available price. Limit orders wait in the book until a compatible counter-order arrives.
- Partial fills are first-class. A buy for 100 shares might match against three separate sells of 30, 30, and 40.
Common Mistakes with Stock Brokerage
- Using a single order book for all stocks. Matching AAPL orders would block GOOGL for no reason.
- Not handling partial fills. A large order routinely matches against several smaller counter-orders at different price levels.
- Sorting the sell side in descending order instead of ascending. The lowest ask should match first, not the highest.
- Forgetting to update portfolio holdings after a trade executes. The order is filled but the user's position stays stale.
Related Designs
Online Auction, Payment System
Task Management — Real-World Systems
Difficulty: Intermediate
State machine for task lifecycle, observers for notifications, commands for undo. Three patterns that handle the three hardest parts of any project tracker.
Design Patterns for Task Management
- Observer Pattern
- State Pattern
- Command Pattern
Key Abstractions in Task Management
- TaskBoard: Orchestrator that manages tasks, users, sprints, and coordinates notifications
- Task: Entity with state machine transitions: TODO to IN_PROGRESS to DONE
- User: Can be assignee or reporter, receives notifications on subscribed tasks
- Sprint: Time-boxed container grouping tasks for a development iteration
- TaskObserver: Interface for receiving notifications when task state changes
Key Points for Task Management
- Task state transitions are explicit: TODO -> IN_PROGRESS -> DONE, with DONE -> TODO for reopening.
- Observer pattern decouples notification logic from task state changes. Add email, Slack, or webhooks without touching Task.
- Command pattern wraps state changes so you can undo them. Undo is just reversing the last command.
- Sprints are containers, not owners. A task can move between sprints without losing its history.
Common Mistakes with Task Management
- Allowing arbitrary state transitions. A task jumping from TODO directly to DONE skips important workflow steps.
- Notifying inside the Task class. That couples Task to every notification channel you will ever support.
- Not storing the previous state in commands. Without it, undo has no idea what to revert to.
- Making Sprint responsible for task state. Sprint groups tasks by time. State transitions are the task's own business.
Related Designs
Notification System, Stack Overflow
Tic Tac Toe — Games & Simulations
Difficulty: Starter
O(1) win detection using row/column/diagonal counters instead of scanning the board. Works for any NxN size.
Design Patterns for Tic Tac Toe
- State Pattern
- Strategy Pattern
- Observer Pattern
Key Abstractions in Tic Tac Toe
- Game: Orchestrator that manages turns and delegates to board and win checker
- Board: Grid state with placement validation and cell ownership
- Player: Participant with a symbol (X/O) and an optional move strategy
- WinChecker: O(1) win detection using row/col/diagonal accumulators
- MoveStrategy: Strategy for AI players: random, minimax, or human input
- GameState: Enum tracking IN_PROGRESS, X_WINS, O_WINS, DRAW
Key Points for Tic Tac Toe
- O(1) win detection by incrementing counters per row, column, and diagonal instead of scanning the board
- State pattern manages game lifecycle with clean transitions between IN_PROGRESS, WIN, and DRAW
- Strategy pattern for player moves. Same interface for human input and AI algorithms.
- Board is decoupled from win logic so you can swap win conditions without touching placement code
Common Mistakes with Tic Tac Toe
- Scanning the entire board after each move to check for a win. O(n^2) when O(1) is right there.
- Using strings for player symbols instead of enums. Typo bugs like 'x' vs 'X' will bite you.
- Hardcoding 3x3. A general NxN solution is barely more code and shows design maturity.
- Putting game logic inside the Board class. Board should only manage cell state, not turns or wins.
Related Designs
Chess, Snake & Ladder
Traffic Signal — Real-World Systems
Difficulty: Intermediate
State pattern drives clean phase transitions across an intersection. Each signal knows only its own color. A controller coordinates them so conflicting directions never get green simultaneously.
Design Patterns for Traffic Signal
- State Pattern
- Observer Pattern
- Strategy Pattern
Key Abstractions in Traffic Signal
- TrafficController: Manages an intersection, coordinates signal phases so conflicting directions never overlap
- Signal: One direction's light. Holds current state and transitions based on timing config.
- SignalState: Enum: RED, YELLOW, GREEN. Each state knows its duration and valid next state.
- TimingConfig: Duration per state. Different configs for rush hour, nighttime, or adaptive mode.
- Intersection: Groups multiple signals into a coordinated unit with named directions
- EmergencyOverride: Observer that preempts normal cycling when an emergency vehicle approaches
Key Points for Traffic Signal
- State pattern eliminates if/else chains for phase transitions. Each state knows its successor, so invalid transitions are structurally impossible.
- Signals are passive. They don't decide when to change. The controller tells them. This prevents two conflicting directions from going green.
- Timing config as a separate object means rush hour, nighttime, and adaptive schedules are just data swaps, not code changes.
- Observer for emergency vehicles keeps priority override decoupled from normal signal logic.
Common Mistakes with Traffic Signal
- Letting signals self-transition without a controller. Two perpendicular signals can both go green, causing a simulated collision.
- Using string comparisons for signal states instead of enums. 'GRREN' won't cause a compile error, but it will cause a runtime bug.
- Hardcoding timing durations inside the signal. Now changing rush hour timing means editing signal internals.
- Forgetting the yellow phase. Jumping straight from green to red is unrealistic and breaks any simulation that models driver behavior.
Related Designs
Elevator System, Parking Lot
URL Shortener — Foundational
Difficulty: Starter
Base62 encode a counter and store the mapping. Reads dominate writes 100:1, so optimize your lookup path. The strategy pattern lets you swap encoding schemes without rewiring anything.
Design Patterns for URL Shortener
- Strategy Pattern
- Factory Pattern
Key Abstractions in URL Shortener
- URLShortener: Orchestrator that coordinates code generation, storage, and analytics
- CodeGenerator: Strategy interface for generating short codes: base62, hash-based, or random
- URLStore: Persistence interface for storing and retrieving URL mappings
- Analytics: Tracks click counts, timestamps, and referrer data per short URL
Key Points for URL Shortener
- Base62 encoding gives you [a-zA-Z0-9] characters. A 7-character code supports 62^7 = 3.5 trillion unique URLs.
- Counter-based generation guarantees uniqueness without collision checks. Hash-based needs collision handling.
- Read-to-write ratio is roughly 100:1. Cache the short-to-long mapping aggressively.
- Strategy pattern on CodeGenerator means you can swap base62 for MD5-based hashing without touching the shortener.
Common Mistakes with URL Shortener
- Using a full MD5/SHA hash and truncating it. Truncation dramatically increases collision probability.
- Not handling the same long URL submitted twice. Decide upfront: same short code or different ones.
- Skipping input validation. Malformed URLs, empty strings, and excessively long inputs all need guardrails.
- Ignoring the read path. If you only optimize writes, your redirect latency will suffer under real traffic.
Related Designs
HashMap, Rate Limiter
Vending Machine — Games & Simulations
Difficulty: Intermediate
State pattern turns a mess of if-else chains into clean, self-contained state objects. Each state knows what it can do and when to hand off to the next one. The machine just delegates.
Design Patterns for Vending Machine
- State Pattern
- Strategy Pattern
Key Abstractions in Vending Machine
- VendingMachine: Context object that delegates all behavior to the current state
- State: Interface with methods for each user action: insertMoney, selectProduct, dispense
- Inventory: Manages product stock, pricing, and availability checks
- PaymentProcessor: Handles coin/note acceptance and change calculation using greedy algorithm
Key Points for Vending Machine
- State pattern eliminates nested conditionals. Each state class handles only the actions valid for that state.
- Transitions are explicit. IdleState moves to HasMoneyState on coin insert. No ambiguity.
- Greedy algorithm for coin change works because standard denominations are canonical (each coin divides the next).
- Inventory is separate from the state machine. Stock management and state transitions are independent concerns.
Common Mistakes with Vending Machine
- Putting all state logic in one giant switch statement. Adding a new state means touching every case block.
- Forgetting to return change when the user cancels. Money in the machine must always be accounted for.
- Not checking stock before accepting money. The user inserts coins for an out-of-stock item and gets frustrated.
- Allowing negative inventory. Every dispense must check stock first, then decrement.
Related Designs
ATM, Payment System