How to approach OOD interviews
A step-by-step framework for object-oriented design interviews, from clarifying requirements to implementing clean, extensible code in a bounded session.
You just heard "Design a parking lot system" and your mind went blank. Not because you don't know what a parking lot is, but because you don't know where to start. Do you draw a class diagram, write code, ask questions, or do all three at once?
A common OOD failure mode is starting implementation before the scope and model are clear. The result is often a plausible class diagram with missing relationships, or a lot of code with no time left to discuss trade-offs. The fix is a repeatable sequence that keeps requirements, structure, and implementation connected.
This guide gives you that sequence: five steps, adaptable time splits, and a concrete output for each stage.
TL;DR
The purpose of this article is to help you turn a broad object-oriented design prompt into a small, explainable model and a traceable core implementation.
- 30-second idea: clarify scope, identify entities, map relationships, choose only justified abstractions, then implement and trace one core scenario.
- Mental model:
requirements β entities β relationships β design forces β patterns/abstractions β code β scenario trace. - Repeatable process: write assumptions, keep the entity list focused, label dependencies and multiplicity, connect every pattern to a force, and revisit the model when a requirement changes.
- Adapt to constraints: protect the core use case first. If time or scope is tight, reduce boilerplate and optional extensions before sacrificing a coherent model or a runnable path.
What interviewers actually evaluate
Before learning the framework, understand the dimensions commonly discussed in feedback. OOD interviews often consider requirements gathering, entity modeling, pattern selection, code quality, and trade-off discussionβnot just code volume.
| Dimension | What they're watching for | Common failure mode |
|---|---|---|
| Requirements gathering | Do you ask clarifying questions before designing? | Jumping straight into code without confirming scope |
| Entity modeling | Can you identify the right nouns and their relationships? | Missing key entities or creating unnecessary ones |
| Pattern selection | Do you pick patterns that solve real forces, not just patterns you memorized? | Using Strategy for everything, or naming patterns without justifying them |
| Code quality | Is the code clean, extensible, and testable? | Giant classes, public fields, no interfaces |
| Trade-off discussion | Can you articulate why you chose X over Y? | "I always use this pattern" with no rationale |
A correct design can still be hard to evaluate if its assumptions are never stated. Conversely, a smaller implementation can communicate strong design judgment when the candidate explains its trade-offs. The relative weight of code and discussion varies by interviewer and exercise, so make both the reasoning and the implementation visible.
The one thing interviewers remember
Make decisions easy to follow. A concise explanation such as "I chose the simpler enum because the state has only two behaviors; I would introduce State if those behaviors diverged" communicates judgment without relying on a pattern name alone.
The 5-step framework
Use this process as a default for OOD prompts, then adjust it to the stated scope.
Each step has a specific output. Move on when that output is clear enough to support the next decision, whether it is on a whiteboard or in a shared document.
| Step | Duration | Output |
|---|---|---|
| 1. Clarify requirements | ~5 min | Bulleted list of functional/non-functional requirements |
| 2. Identify entities | ~5 min | List of 4-8 core classes with key attributes |
| 3. Define relationships | ~5 min | Class diagram showing associations, inheritance, composition |
| 4. Choose patterns | ~10 min | Named patterns anchored to specific forces |
| 5. Implement and trace | ~20 min | Working code + walkthrough of one scenario |
Step 1: Clarify requirements
Do not start designing before you know what is in scope. Broad prompts are an opportunity to narrow the problem with explicit assumptions.
Ask three categories of questions:
Scope: "Should I focus on the core booking flow, or also handle payments and cancellations?" This tells the interviewer you understand that 45 minutes has a budget and you're spending it wisely.
Actors: "Who are the users of this system? Just customers, or also admins and operators?" Every actor becomes a potential class or interface.
Constraints: "Should I handle concurrency? Multi-floor support? Different vehicle types?" These constraints directly influence which patterns you'll need.
A reasonable default is to spend 3-5 minutes here, ask a focused set of questions, and write the answers as bullets. If the interviewer leaves a choice open, state an assumption and proceed; revisit it if later requirements change the design.
The silent interviewer trap
Some interviewers deliberately stay quiet when you ask questions, answering with "It's up to you." This is not a signal to stop asking. It means they want you to make and state your own assumptions. Say: "I'll assume X, Y, Z. I can revisit if needed."
Step 2: Identify entities
Now turn the requirements into nouns. A parking lot system might give you: ParkingLot, Floor, Spot, Vehicle, Ticket, Payment. An elevator system: Elevator, Floor, Request, Scheduler.
Three rules for entity identification:
- Start with the core domain objects. What are the physical or logical things in this system? A parking spot, a vehicle, a ticket.
- Look for actors. Who interacts with the system? An attendant, a customer, an admin. Not all actors become classes, but many do.
- Identify the orchestrator. There is often a service or manager that coordinates the workflow.
ParkingLotService,ElevatorScheduler,OrderProcessor.
Don't overmodel at this stage. Start with 4-8 entities and check any larger list against the core use cases. Drop anything that does not directly serve a requirement, carry state, or own meaningful behavior.
For each entity, jot down 2-4 key attributes. Not every field, just the ones that drive behavior. A ParkingSpot needs spotType, isOccupied, and spotNumber. It does not need createdAt or lastModified at this stage.
Step 3: Define relationships
This is where a plausible list becomes a usable design. Relationships are the skeleton of the model; clear ownership and dependency direction give the code a shape that can be traced.
For each pair of entities, ask:
- IS-A or HAS-A? Is a
Cara type ofVehicle(inheritance)? Or does aParkingLothave manyFloorobjects (composition)? - Multiplicity? Does one
Floorhave manySpotobjects? Does oneTicketreference exactly oneVehicle? - Dependency direction? Does
ParkingLotServicedepend onSpotAssignmentStrategy, or the other way around? Get this wrong and your code becomes untestable.
Draw a quick class diagram on the whiteboard. It doesn't need to be UML-perfect. Boxes with names, lines with labels. The interviewer wants to see that you think in terms of structure, not just procedural code.
The biggest mistake here is making everything inherit from everything. Prefer composition when behavior or ownership can vary independently. If you are drawing more than two levels of inheritance, stop and reconsider whether an interface, composition, or an enum is clearer.
Step 4: Choose patterns
This is the step that separates candidates who memorized patterns from candidates who understand them. Don't pick a pattern because you know it. Pick it because a specific force in your design demands it.
The conversation should sound like:
"We have multiple vehicle types that need different spot sizes. This is a classic case where the assignment logic varies by type, so I'll use the Strategy pattern for spot assignment. This way we can add new vehicle types without modifying the core parking logic."
Not:
"I'll use the Strategy pattern here because it's a good pattern."
Several patterns recur in OOD problems, but none is required unless a design force calls for it:
| Pattern | Use when... | Example |
|---|---|---|
| Strategy | Behavior varies by type or configuration | Pricing algorithms, assignment rules, notification channels |
| State | An object has a lifecycle with distinct phases | Order states, elevator states, game turns |
| Observer | Multiple components react to a single event | Notifications, analytics, audit logging |
For your interview: name the pattern, state the force it addresses, and explain how it makes the design extensible. One sentence each. Then move to code.
Pattern justification formula
"We need [capability]. Without a pattern, we'd have [problem: if/else chain, tight coupling, etc.]. [Pattern name] solves this by [mechanism]. This means we can [extension point] without modifying [existing code]."
Step 5: Implement and trace
Now write code. But don't try to implement everything. Pick the 3-5 most important classes and write them fully. For the rest, declare the interface and move on.
Priorities when coding:
- Interfaces and abstractions first. Write
SpotAssignmentStrategybeforeNearestSpotStrategy. - One concrete implementation. Prove the pattern works with one real subclass.
- The orchestrator. The service class that wires everything together.
- Skip boilerplate. Don't write getters, setters, equals, hashCode. Say "I'd generate these" and move on.
After writing the code, trace one complete scenario verbally: "A motorcycle arrives. The attendant calls parkVehicle(). The service checks assignSpot() using the motorcycle strategy, which finds the smallest available spot. A ticket is created, the spot is marked occupied, and the ticket is returned." This trace checks that the design connects end to end.
Step-by-step walkthrough: parking lot
Let me walk through the framework with a compressed parking lot example to show what the output looks like at each step.
Step 1 output (requirements):
- Multi-floor parking lot with different spot sizes (compact, regular, large)
- Support multiple vehicle types (motorcycle, car, truck)
- Assign the nearest available spot that fits the vehicle
- Issue tickets on entry, accept payment on exit
- Track occupancy per floor
Step 2 output (entities):
ParkingLot,Floor,ParkingSpot,Vehicle(with subtypes),Ticket,Payment
Step 3 output (relationships):
ParkingLotHAS manyFloor(composition)FloorHAS manyParkingSpot(composition)VehicleIS-A hierarchy:Motorcycle,Car,TruckTicketreferences oneVehicleand oneParkingSpot
Step 4 output (patterns):
- Strategy for spot assignment (different algorithms: nearest, most-compact, load-balanced)
- Enum for spot types and vehicle types (type-safe, no magic strings)
- Observer for occupancy tracking (floor dashboard updates when spots change)
Step 5: Here's what the core code looks like. Not every class, just the ones that matter.
public class ParkingSpot {
private final String spotId;
private final SpotType type;
private final int floor;
private Vehicle currentVehicle;
public ParkingSpot(String spotId, SpotType type, int floor) {
this.spotId = spotId;
this.type = type;
this.floor = floor;
}
public boolean isAvailable() { return currentVehicle == null; }
public boolean canFit(Vehicle vehicle) {
return isAvailable() && type.fits(vehicle.getType());
}
public void occupy(Vehicle vehicle) {
if (!canFit(vehicle)) {
throw new IllegalStateException("Spot cannot fit this vehicle");
}
this.currentVehicle = vehicle;
}
public void release() { this.currentVehicle = null; }
// Getters omitted for brevity
public String getSpotId() { return spotId; }
public SpotType getType() { return type; }
public int getFloor() { return floor; }
}Notice what's not here: no ParkingLot class wrapping everything, no Floor class (the spot already has a floor number), and no payment calculation logic. For a bounded design discussion, implement the core and mention the rest verbally: "Payment calculation would use another Strategy, and I'd add an Observer for the floor occupancy dashboard." If the requirements make those concerns central, promote them into the model instead of treating them as optional.
Trade-offs and adapting to constraints
The framework is a way to spend attention, not a fixed list of classes or patterns.
- Narrow scope: keep the model around the core use cases and state assumptions about omitted concerns such as persistence, payments, or notifications.
- More variation: introduce an interface or Strategy when a behavior genuinely has multiple implementations or an explicitly requested extension. Use direct code when the behavior is stable.
- Shared data, different roles: prefer composition or role interfaces when one person can hold multiple roles; use inheritance when substitutability and shared behavior are clear.
- Limited coding time: write the interfaces, one concrete path, and the orchestrator first. Describe routine boilerplate and optional implementations rather than leaving the core flow disconnected.
- New scale or concurrency constraints: revisit ownership, synchronization, and storage boundaries. A clean in-memory class model is a useful starting point, not proof that the same implementation handles distributed load.
- Changing assumptions: say what would change in the model if a requirement moves. This keeps the design honest and shows where the extension points are.
30-second and 5-minute explanations
30-second explanation
"I start by clarifying the actors, core use cases, and constraints. Then I identify the domain entities and their relationships, including ownership and multiplicity. I introduce an abstraction only where a real behavior varies, implement the core path, and trace one scenario end to end. For each choice I can state the simpler alternative and what requirement would justify changing it."
5-minute explanation
Begin with the scope and assumptions, then list the entities that participate in the main use cases. Map relationships, multiplicity, and dependency direction before choosing patterns. In the parking-lot example, ParkingLotService orchestrates the flow, SpotAssignmentStrategy isolates the assignment rule, and the domain objects enforce their own invariants. Trace a vehicle from arrival to ticket creation and then discuss what changes for a new vehicle type, a different pricing rule, persistence, or concurrent entry gates. Keep the explanation anchored to requirements rather than to a catalog of patterns.
Time management: how to spend 45 minutes
Time pressure can expose scope problems even when the design knowledge is solid. Here is one useful split for a 45-minute session; adjust it to the actual format:
| Phase | Minutes | What to produce | Danger sign |
|---|---|---|---|
| Clarify requirements | 0-5 | 5-7 bullet points confirmed with interviewer | Still asking questions at minute 8 |
| Entities and relationships | 5-15 | Class diagram with 4-8 entities and labeled edges | Drawing 15+ classes |
| Choose patterns | 15-25 | 2-3 named patterns with justifications | Naming 5+ patterns without justification |
| Implement code | 25-40 | 3-5 core classes, runnable logic | Writing boilerplate getters for 10 minutes |
| Trace and discuss | 40-45 | End-to-end scenario walkthrough, extensions | Silence, or still writing code |
Three rules for staying on track:
Set verbal checkpoints. At the 15-minute mark, say out loud: "I've identified the core entities and relationships. Now I'll pick patterns before coding." This signals structure to the interviewer and keeps you honest about pacing.
Skip boilerplate aggressively. Don't write constructors, getters, equals, toString. Say "I'll assume standard boilerplate" and spend that time on real logic. The interviewer is evaluating your design decisions, not your ability to type public String getName() { return name; }.
Code the happy path first. Get one scenario working end-to-end. Then, if time permits, discuss error handling, concurrency, and edge cases verbally. A working happy path with clearly stated edge cases is often more useful than half-implemented error handling.
What separates junior from senior
The same design problem, three different experience levels. Here's what changes:
| Dimension | Junior (L3-L4) | Mid (L4-L5) | Senior (L5-L6) |
|---|---|---|---|
| Requirements | Starts coding immediately | Asks 2-3 questions | Runs a structured requirements mini-interview, states own assumptions |
| Entities | One or two god classes | Reasonable split, but some anemic models | Clean domain model with behavior in the right places |
| Relationships | Everything is public, no clear dependencies | Uses composition, some inheritance | Dependency inversion, interface-first, testable structure |
| Patterns | None named, or uses "design pattern" as a buzzword | Names Strategy or Observer, may not justify it | Names patterns, states the force, explains the alternative they rejected |
| Code quality | Long methods, public fields, no validation | Clean-ish code, some defensive checks | Small focused methods, immutable where possible, clear error messages |
| Trade-offs | "This is how I always do it" | "We could also use X" (if prompted) | "I chose X over Y because Z. If the requirements changed to W, I'd switch to Y." |
| Time management | Runs out of time mid-code | Finishes code, no time for discussion | Finishes with 5 minutes for walkthrough and extensions |
A valuable distinction is proactive trade-off discussion. For example: "I'm choosing composition over inheritance here because the vehicle types might overlap in the future, and inheritance would force a rigid hierarchy." The important part is connecting the choice to a requirement, not assigning a label to the candidate.
Common pitfalls
1. Skipping requirements entirely
Jumping straight to code hides your assumptions and makes backtracking more likely. Spend a short, focused pass asking questions or stating assumptions before you build; it is a small investment that protects the rest of the design.
2. Over-modeling
You don't need a class for every noun in the problem statement. A parking lot doesn't need a ParkingLotConfiguration, SpotFactory, VehicleRegistry, FloorManager, and SpotValidator. Start with the minimum viable set of entities. If the interviewer wants more depth, they'll ask.
3. Pattern name-dropping without justification
Saying "I'll use the Factory pattern here" without explaining why is worse than not naming the pattern at all. It signals that you memorized a list but don't understand the forces. Always connect the pattern to a specific design force: "Multiple vehicle types need different spot sizes, so I'll use Strategy for assignment."
4. Writing too much code
With roughly 15-20 minutes for code in a 45-minute interview, aim for a focused core rather than a line-count target. Write the key interfaces, one concrete implementation, and the service that orchestrates them. Declare routine boilerplate or optional implementations as stubs or describe them verbally.
5. Forgetting to trace a scenario
Your design exists in your head. The interviewer needs to see it work. Walk through one complete scenario: "A truck arrives, the service calls assignSpot(), the strategy returns the first large spot on floor 2, a ticket is issued, the spot is marked occupied." This is your proof that the design actually hangs together.
6. Ignoring extensibility
The interviewer may ask: "What if we need to add electric vehicle spots?" or "What if the pricing changes?" If one feature requires modifying many unrelated classes, revisit the boundaries. Use the Open-Closed Principle where it reduces a real change surface: add new behavior through a suitable extension point instead of forcing edits throughout the core.
How this shows up in interviews
Typical prompts:
- "Design a parking lot system"
- "Design an elevator system"
- "Design a library management system"
- "Design a vending machine"
What interviewers probe for:
| Interviewer question | What they're really asking | Strong response |
|---|---|---|
| "Walk me through your approach" | Do you have a process? | "I'll start by clarifying requirements, then model entities, define relationships, pick patterns, and implement core classes." |
| "Why did you use inheritance here?" | Can you justify structural decisions? | "Vehicles share a common interface but have type-specific behavior. I used an interface, not abstract class, because there's no shared state." |
| "What if we add a new vehicle type?" | Is the design extensible? | "I'd add a new VehicleType enum value and a new SpotAssignmentStrategy. No existing classes change." |
| "How would you test this?" | Do you think about testability? | "The Strategy interface makes unit testing easy. I can inject a mock strategy into ParkingLotService and verify behavior without real spots." |
| "What patterns did you use?" | Do you recognize and name patterns? | "Strategy for assignment, Observer for occupancy tracking. I considered State for spot lifecycle but it's overkill for two states (occupied/available)." |
Explaining rejected alternatives
When the interviewer asks "What patterns did you consider but rejected?", have one ready. "I considered the State pattern for parking spot lifecycle, but a spot only has two states (available/occupied), so a boolean is simpler. If spots had maintenance, reserved, and handicapped states, I'd introduce State." This shows depth without over-engineering.
Test Your Understanding
Quick recap
- OOD interviews commonly evaluate requirements gathering, entity modeling, pattern selection, code quality, and trade-off discussions. Make both the reasoning and the code visible.
- Follow the 5-step framework: Clarify, Identify entities, Define relationships, Choose patterns, Implement and trace.
- For a 45-minute session, a useful starting split is roughly 5/10/10/15/5 minutes across the five steps; adjust it when the scope changes and set verbal checkpoints.
- Name patterns only when you can state the specific force they address. "I'll use Strategy because..." beats "I'll use Strategy" with no context.
- Prefer composition when it keeps variation and ownership independent. Use inheritance or an interface when substitutability and shared behavior make it clearer.
- Write 3-5 core classes, skip boilerplate, and trace one scenario end-to-end to check that the design connects.
- Proactive trade-off discussion makes it clear why the design is shaped the way it is and how it would change under new constraints.
- Over-modeling, pattern name-dropping, and ignoring extensibility make the design harder to evaluate and adapt.
Related concepts
- Identifying entities expands the noun, verb, lifecycle, and responsibility checks used in Step 2.
- Choosing design patterns focuses on matching abstractions to real design forces.
- Writing clean code covers the naming, method boundaries, and error contracts used during Step 5.