InterviewPrepKit

Home / Learn / Object-Oriented Design

What Is an OOD Interview?

In this lesson, we’ll walk through what the OOD round actually is. A common prompt is “design a parking lot.” You get a shared editor, about forty-five minutes, and a requirement that changes near the end. Most of that time is barely scored. Two or three decisions determine the result, and we want you knowing which ones before you start.

We’ll cover what you receive, what you must deliver, the one thing being measured, and how OOD differs from the other design rounds it gets confused with. Every term of art is defined the first time it appears. By the end you’ll be able to name the artifact this round wants, spot the two or three decisions that carry it, and tell in one question which design round you are actually in.

Start with the words in the title

An object is a bundle of data together with the operations allowed on that data. A parking Spot holds whether it is occupied, and offers the operations that occupy it and free it. The data and the operations travel together.

A class is the template describing one kind of object. An object is one filled-in copy of that template. Spot is the class; the spot on level 2 in row C is an object.

Object-oriented design (OOD) is deciding which classes exist and which one is responsible for each decision the system has to make. So “design a parking lot” is really two questions: when a car arrives, which object decides where it goes, and which object decides what it costs?

OOD and LLD (low-level design) name the same round. The only difference is how much code you are expected to type.

The one thing being measured

The round measures whether the boundaries you draw between objects survive the next requirement.

Not whether you can recite the four pillars, and not whether you know twenty-three patterns. A change is prepared in advance (say, “now add electric-vehicle bays”) and what is scored is whether you answer it by adding a class or by editing six of them.

flowchart TD
    R["New requirement:<br/>electric-vehicle bays"] --> G["Good boundary:<br/>add one FeeModel subclass,<br/>edit nothing"]
    R --> B["Bad boundary:<br/>edit ParkingLot, Spot, Ticket,<br/>and three more classes"]

The unit of scale here is the number of files touched per requirement change, not queries per second, not gigabytes of stored data. Those belong to a different round. Correctness means an invariant holds across every method call and the asserts you wrote pass, where an invariant is a statement that must be true before and after every operation (“a spot holds at most one vehicle”).

The design move that makes changes cheap is to put an interface (a small named contract with no code behind it, which several classes can each fulfil in their own way) at the point you expect to change most often. In the parking lot the fee rule changes every quarter and the “does this vehicle fit this spot” rule has not changed in years, so FeeModel is an interface and the fit check is a plain method. Tomorrow’s fee change then arrives as a new class instead of an edit.

If that is the one thing scored, the next question is mechanical: what do you actually hand over to earn the score? That is what fixing the inputs and outputs settles.

What goes in, and what comes out

Candidates lose this round by producing the wrong artifact more often than by producing a bad one, so fix the inputs and outputs first.

In, in this order: one deliberately underspecified sentence of prompt (“design a parking lot”); your own clarifying questions, which fill in what the prompt left out; and one requirement change delivered near the end.

Out, all three expected:

  1. A class diagram: a box per class listing its data and operations, with lines showing which class knows about which.
  2. Roughly 60–150 lines of code that actually runs.
  3. A short account of the change in terms of files: what is new, what is untouched, what it costs.

That last account, for the EV change, sounds like: “EV bays are a new SpotType value and a new FeeModel subclass. ParkingLot and Ticket are untouched. The cost is that the pricing rule now lives in a second file, so reading the whole fee flow means opening two.”

The vocabulary

These terms recur throughout the track. Skim them once and return when one comes up.

TermIn plain words
Class / objectThe template, and one filled-in copy of it
Interface vs implementationThe interface is the promise (operation names and what they return); the implementation is one way of keeping it. Callers depend on the promise, so the implementation can be swapped
EncapsulationThe object owns its data and no one reaches in. You call spot.occupy(car); you do not write spot.vehicle = car from outside
CouplingHow much one class must know about another’s internals. Low coupling lets you change one without opening the other
CohesionHow well the things inside one class belong together. High cohesion means every field and method serves the same job
Composition vs inheritanceComposition means one object holds another and delegates to it, swappable at runtime. Inheritance means one class is a special case of another and reuses its code, fixed when written
InvariantThe statement that must never be false, such as one vehicle per spot
CardinalityThe count on each end of a relationship: one lot has one-or-more levels, a spot has zero-or-one vehicle
UMLUnified Modeling Language, the standard notation for class diagrams — the boxes, arrows, and diamonds
Value objectA small object whose whole identity is its contents, compared by what it holds. Two readings of 19.0 °C are the same reading
Actor / use caseAn actor is anything that starts an operation, including non-humans (a clock, a sensor). A use case is one operation end to end, with the condition that must hold first

The four pillars are the textbook summary of object-oriented programming: encapsulation (an object hides how it stores data and exposes only operations), abstraction (a caller depends on what an operation promises, not how it is done), inheritance (one class reuses and specialises another), and polymorphism (several kinds of object answer the same call, each in their own way).

SOLID is a five-item checklist for class design. Reciting it scores nothing; the value is in the consequences, which OOP Fundamentals derives from code that fails without them.

LetterPrincipleIn plain words
SSingle responsibilityA class should have one reason to change
OOpen/closedAdding behaviour should mean adding code, not editing code that already works
LLiskov substitutionAnywhere the base class works, a subclass must work too, without the caller noticing
IInterface segregationSeveral small interfaces beat one large one, so nobody depends on operations they never call
DDependency inversionThe class holding the policy depends on an interface; the concrete implementation is handed in from outside

The twenty-three patterns are the named, reusable solutions catalogued in the 1994 book Design Patterns. Five appear in this track:

PatternWhat it does
StrategyPuts a varying rule behind an interface, so a new rule is a new class (a fee model, a thermostat policy)
StateGives an object different behaviour per lifecycle phase, each phase its own class (vending machine, elevator)
FactoryOne place that decides which concrete class to build from some input (the right Spot subtype from a size code)
ObserverLets objects register to be told when something changed, so the changer need not know who cares (logger, notifier)
CompositeTreats a single item and a group of items through the same interface (a file and a folder both answering size())

Naming a pattern is worth little on its own; what scores is naming the change it makes cheap.

Which round is this

Four different interviews get called “the design round,” and answering one with another’s material is the most expensive mistake available. One row settles which one you are in: the decisive question.

DimensionOOD / LLD (this track)Classic system designML system designAgent design
The artifactA class diagram and running codeA topology and a data modelA target definition and an operating pointA control loop and its guards
The decisive questionWhat changes next, and who owns itRead:write ratioWhere the label comes fromCalls per task
Unit of costFiles edited per changeStorage, egress, fan-outFeature fetchInference tokens
Scale of the answerOne process, one machineA fleetA pipelineA loop

The one-line boundary: classic system design asks which machine the data lives on; OOD asks which object owns the decision. They are not a hard round and an easy round, just different questions. Answering an OOD prompt with QPS (queries per second) numbers reads as badly as answering a system design prompt with class diagrams.

So two things you will not be asked for here are capacity estimates and network topology. If the interviewer wants those, they will say “and now it is a distributed service across 500 garages,” which is the moment to switch to the system design material.

This is a coding interview wearing a design hat

If the invite says LLD, machine coding, low-level design, or object-oriented design, and the format is a shared editor, you will be judged on code that runs. The design discussion is the first stretch of a coding round. Three formats exist:

FormatHow to tellWhere the time goesWhat “done” means
Shared editor / LLDCoderPad, HackerRank, “bring your laptop”~15 min design, ~30 min codeIt executes, with a main that exercises it
Whiteboard OODNo editor mentioned, on-site loop~25 min design, ~20 min code sketchInterfaces and one real method, on the board
Machine codingTwo-hour slot, common at some fintechs20 min design, 100 min buildA CLI or test suite the interviewer runs

CoderPad and HackerRank are shared-editor products with a language runtime attached. A main is the entry point that exercises your classes when the file runs. A CLI (command-line interface) is a text menu driven by typing. All three formats score the same design decisions; they differ only in how much code you produce, and the shared-editor version is now the common case. If an interpreter is open, run the code, because an interviewer reads untested code as broken.

What “runs” looks like

Here is the whole bar in one file: a thermostat with a temperature target, a rule for deciding heat/cool/off, and three asserts proving the rule. The rule lives in its own class, and the file checks itself.

from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum


class Mode(Enum):
    HEAT = "heat"
    COOL = "cool"
    OFF = "off"


@dataclass(frozen=True)
class Reading:
    celsius: float


class ControlPolicy(ABC):
    @abstractmethod
    def decide(self, reading: Reading, target: float) -> Mode: ...


class Deadband(ControlPolicy):
    """Do nothing inside +/- width of target, so the compressor does not thrash."""

    def __init__(self, width: float) -> None:
        self.width = width

    def decide(self, reading: Reading, target: float) -> Mode:
        if reading.celsius < target - self.width:
            return Mode.HEAT
        if reading.celsius > target + self.width:
            return Mode.COOL
        return Mode.OFF


@dataclass
class Thermostat:
    target: float
    policy: ControlPolicy

    def tick(self, reading: Reading) -> Mode:
        return self.policy.decide(reading, self.target)


t = Thermostat(target=20.0, policy=Deadband(width=0.5))
assert t.tick(Reading(19.0)) is Mode.HEAT
assert t.tick(Reading(20.2)) is Mode.OFF
assert t.tick(Reading(21.0)) is Mode.COOL
print("thermostat ok")

With a target of 20.0 and a width of 0.5, the deadband runs from 19.5 to 20.5: 19.0 is below it (HEAT), 20.2 is inside it (OFF), and 21.0 is above it (COOL). The three asserts trace the three branches.

Five Python devices in that file recur across the track:

  • Enum: a closed, named set of constants. Mode can only be heat, cool, or off. No stray strings.
  • @dataclass: a class whose fields are declared once, with the constructor and equality check generated for you.
  • frozen=True: makes the dataclass immutable, turning Reading into a value object that nothing can mutate after construction.
  • ABC and @abstractmethod: an abstract base class cannot be instantiated and exists to declare the interface; @abstractmethod marks an operation every subclass must supply. Together they let ControlPolicy state a promise with no code behind it.
  • Dependency injection: Deadband is passed into the Thermostat instead of being created inside it, so a different policy needs no edit to Thermostat.

The same thing, drawn

Four boxes, three arrows. The one that matters is the solid arrow from Thermostat to ControlPolicy: that is the seam where tomorrow’s requirement arrives.

classDiagram
    class Thermostat {
        +float target
        +tick(Reading) Mode
    }
    class ControlPolicy {
        <<interface>>
        +decide(Reading, float) Mode
    }
    class Deadband {
        +float width
    }
    class Reading {
        +float celsius
    }

    Thermostat "1" --> "1" ControlPolicy : injected
    ControlPolicy <|.. Deadband : implements
    Thermostat ..> Reading : uses

The marks are UML, and they recur in every chapter: each box is a class with data above operations, + marks what is public, <<interface>> labels a promise with no implementation, the solid arrow with "1" at each end means one thermostat holds exactly one policy, the dashed arrow with a hollow triangle (<|..) reads implements, and the plain dashed arrow (..>) reads uses, mentioned in a signature but not held.

What is present is the shape of a passing answer: an enum, an abstract policy, a value object, one class with one real method, and asserts that state the behaviour. What is absent is deliberate: no getters and setters (one-line methods that only read or write a field, which is a public field with extra ceremony), no do-everything ThermostatManager, no parody AbstractPolicyFactoryBuilder. The deadband is a separate object precisely because the next requirement (“support a schedule that overrides the target overnight”) becomes a new ControlPolicy with zero edits to Thermostat. Fold decide into an if/elif chain inside Thermostat.tick and the same feature edits the class that also owns the target, the sensor wiring, and the display: three times the blast radius.

The shape of the time

The round divides into six blocks. The point of knowing them is to notice when you are behind: roughly twenty of the forty-five minutes are code and extension, so if you have not typed a class by the halfway mark, stop drawing and start typing. When time runs short, what gets cut is drawing, never code.

BlockMinutesWhat it lands
Clarify and scope0–5Whether your questions change the design
Actors and use cases5–10Whether you found the operations before the nouns
Objects and relationships10–20The diagram, with cardinalities
The 2–3 decisions20–25The pattern, and the change it makes cheap
Code the core25–40Whether it runs
Extend40–45Whether the design absorbs the change
flowchart LR
    A["Clarify and scope<br/>0-5"] --> B["Actors and use cases<br/>5-10"]
    B --> C["Objects and relationships<br/>10-20"]
    C --> D["The 2-3 decisions<br/>20-25"]
    D --> E["Code the core<br/>25-40"]
    E --> F["Extend<br/>40-45"]

The first block carries the whole design, because every class diagram is a set of assumptions with boxes attached: that fee rules will change and matching rules will not, that vehicle sizes are a closed list, that one spot holds one vehicle. Get one wrong and you get the wrong classes, not a tweak. The last block is the point of all the others: the extension question is what a design that can absorb change is built to answer.

What the interviewer has prepared

Three things, in priority order:

  1. A requirement change, chosen to hit the seam in the obvious design: the joint where a naive model has spread one decision across several classes. In a parking lot it is EV bays or monthly passes; in an elevator, a service mode; in a vending machine, refunds.
  2. A concurrency question. Concurrency means two things happening at once inside your program, so their steps can interleave: “two cars arrive at the last spot at the same time.” A thread is one independently running sequence of steps; an interleaving is one specific ordering in which two threads’ steps land; a lock is the flag that forces one thread to wait. The answer is never just “add a lock.” It is which object owns the lock, its scope, and what the failing interleaving looks like without it (worked end to end in How to design a parking lot).
  3. A “why not the simpler thing” question: “why an interface and not a function?” Sometimes the honest answer is that the simpler thing is correct, and saying so is the right answer.

What this round is not

Five habits carried in from other interviews cost time the rubric does not pay for.

Not thisBecause
A UML examNobody checks whether you drew a hollow diamond. They check whether the lifetime claim it makes is true
A pattern quizNaming a pattern is one point; naming the change it makes cheap is three
A capacity exerciseOne process. If a number matters here it is a cardinality, like “3 entrances,” not a throughput
A database schemaTables come up only if persistence was in scope. Model the objects; mention the mapping in one sentence
A LeetCode problemThe data structures are trivial on purpose. A hash map from spot id to spot is the whole algorithm

Throughput is traffic per second (a system design concern). Persistence means storing data so it outlives the running program, and a schema is the table-and-column layout that holds it. A hash map is a lookup table from key to value with constant-time access. The algorithmic content of an OOD problem is near zero by design, so every minute spent optimizing a lookup is a minute not spent on what is scored.

The prompt space is small

Almost every prompt is one of six shapes, and recognizing the shape early tells you where the hard part is before you have modelled anything.

ShapePromptsThe hard part
Resource allocationParking lot, hotel booking, seat reservation, lockerMatching rule, pricing rule, and the race for the last unit
State machineVending machine, elevator, order lifecycle, ATMIllegal transitions, and where the transition table lives
Rules engineChess, tic-tac-toe, blackjack, card gamesSeparating move legality from execution from win detection
Hierarchy / traversalFile system, org chart, comment threadsComposite, and recursion that does not blow the stack
LedgerSplitwise, ATM, wallet, inventoryMoney as integers, and an operation that is atomic or absent
Feed of eventsLogger, notification service, display boardObserver, ordering, and what a slow subscriber does to the producer

A few of those terms:

  • Race: two operations reading the same value and both acting on it, so the last unit gets sold twice.
  • State machine: an object with a small set of named states and a fixed table of which moves between them are legal. An unpaid ticket may be paid; a paid one may not be paid again.
  • Composite: lets a folder and a file answer the same call so traversal code does not branch. Recursion that blows the stack is a deep tree walked by a function that calls itself once per level until the interpreter gives up.
  • Money as integers: storing 5.00 as 500 cents, because binary floating-point cannot represent 0.10 exactly and the errors accumulate. Atomic means the whole operation happens or none of it does.
  • Slow subscriber: a listener so slow the producer is stuck waiting for it, which is why the notification path usually needs a queue.

Two of the six put money or a shared resource under concurrent access, and those are the ones where “add a lock” is not an answer. Practice those first: the allocation shape in How to design a parking lot, then any state machine.

Scoring rubric

The weak column stops at the noun; the strong column says what each choice leads to. This is the whole sheet.

DimensionWeakStrong
ScopingStarts modelling immediatelyNames what is in and what is explicitly out, in one sentence
QuestionsTrivia with no consequenceEach question is followed by the fork its answer creates
Object selectionOne class per nounTriages nouns into classes, enums, value objects, and attributes, with a reason
RelationshipsUndirected linesCorrect composition / aggregation / association with cardinalities
DecisionsNames three patternsNames one or two, each tied to a specific expected change, each with its cost
CodePseudocode, or 200 lines of accessorsRuns, has asserts, shows the method where the design lives
Concurrency“Add a lock”The interleaving, the invariant it breaks, and where the lock goes
Extension“That is easy”Which file is new, which files are untouched, and why the design made that true
RestraintInterfaces everywhereRefuses at least one abstraction, because nothing is going to vary

Three relationship kinds sit behind that “Relationships” row, and they are claims about deletion, not about spelling. Draw composition when the part cannot outlive the whole (delete the level and its spots go with it); draw aggregation when the whole merely holds parts that survive it (delete the lot and the monthly-pass holders still exist); an association is the weakest: one class holds a reference to another and claims nothing about owning it. The notation is worked in OOP Fundamentals. In the “Code” row, accessors is the collective name for getters and setters, and pseudocode is code-shaped prose no interpreter accepts; both fill a screen without making a decision. The Restraint row is the one most people miss: refusing an abstraction (“one kind of spot, and a size enum covers the variation; if a spot ever needs its own behaviour, that is when the interface earns its keep”) is itself a strong answer.

Conclusion

  • OOD is deciding which classes exist and which one owns each decision, then writing code that runs.
  • The round is scored on one thing: whether your object boundaries absorb the next requirement by adding a class instead of editing several. The unit of cost is files touched per change, not QPS or GB.
  • Put the interface where change is expected. If you cannot name a second implementation, do not add the interface.
  • Deliver a class diagram, ~60–150 lines that run with asserts, and a short account of a change in terms of files.
  • Almost every prompt is one of six shapes; the two involving money or a shared resource are where concurrency is the real test.

One line to carry into the room: this round asks which object owns the decision, and scores you on whether that ownership survives the next requirement.

Further reading

  • Gamma, Helm, Johnson, Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (1994), the source of Strategy, State, Factory, Observer, and Composite.
  • Robert C. Martin, Agile Software Development, Principles, Patterns, and Practices, the fullest treatment of SOLID.
  • Martin Fowler, Refactoring: Improving the Design of Existing Code, on the code smells (large classes, feature envy) that motivate these boundaries.
  • Next in this track: OOP Fundamentals, which derives encapsulation, Liskov substitution, coupling, and why Singleton is usually wrong from code that fails without them.
Report a bug