DEV Community

Cover image for Designing a vending machine: the object-oriented design interview, explained
Gowtham Potureddi
Gowtham Potureddi

Posted on

Designing a vending machine: the object-oriented design interview, explained

Designing a vending machine: the object-oriented design interview, explained

"Design a vending machine" looks like the easiest prompt in a low-level design interview, and that is exactly why candidates fumble it. There is no clever algorithm to reach for, so the whole round rests on one thing: can you model a machine whose behaviour depends on what just happened to it? Insert a coin and the machine should accept more money; press "dispense" before paying and it should refuse; select a product that just sold out and it must hand your money back. That is a state machine, and turning it into clean object-oriented design — not a tangle of if checks on a status flag — is the skill the interview is actually testing.

This guide walks the vending machine the way a strong candidate works it in an OOD interview: clarify the requirements, recognise that the machine is a state machine, model it with the State pattern, sketch the core classes, and then defend the decisions the interviewer always pushes on — why State beats a giant conditional, how you make change, and what happens the moment the happy path breaks. In a low-level design round you write these classes out in the editor, not as a finished program but as a design you can explain and extend out loud, one transition at a time.

Title card for an object-oriented design interview explainer on designing a vending machine, showing a state-machine diagram with Idle, HasMoney, Dispensing and OutOfStock states and the transitions between them, alongside a class diagram with a VendingMachine delegating to a State interface and an Inventory of Products, with tags for the State and Strategy patterns, on a dark citrus-and-lavender background.

Why the vending machine comes up in object-oriented design interviews

The vending machine earns its place because it is the cleanest small problem that forces a state machine. The domain is trivial — everyone has used one — so there is nowhere to hide behind jargon; the entire conversation is about whether your model handles the fact that the same button means different things depending on what the machine is currently doing. An interviewer can watch you discover the states, choose how to represent them, and then stress the transitions, all in well under half an hour.

The interviewer rarely lets a tidy class diagram stand on its own. "A customer inserts $2.00, selects slot B4, and in the instant between the selection and the dispense, B4's last can is taken by someone else — your inventory now says zero. Walk me through exactly what state your machine is in, what it does next, and what happens to the $2.00 the customer already put in." That question — an illegal transition triggered by a real-world race — is the one a happy-path design can never survive, and the one the vending machine is really testing.

The rounds on Zynter are modelled on how the loop runs, not scraped from any candidate's recall. What matters for prep is the shape: you name a state, and the next sentence out of the interviewer's mouth either adds a transition or attacks one — a refund, an out-of-stock, a machine that can't make exact change, or two actions arriving in the wrong order.

What the interviewer is really testing

A low-level design answer is read on the same four dimensions as any design round. Problem solving — did you recognise the state machine and model it cleanly, or scatter the logic across if (status == ...) checks? Communication — can you justify why each state is its own class rather than a branch in one giant method? Technical execution — do you handle the illegal transitions and edge cases (out of stock after payment, insufficient funds, no change), or only the perfect purchase? Time management — did you clarify and settle on the states before coding, rather than perfecting the coin-counter? Reading this guide teaches the design; it does not teach you to defend it while someone says "now it dispenses before the money clears" mid-sentence.

Step 1: Clarify the requirements

The single biggest mistake in an LLD round is writing a class before asking a question. Spend the first two minutes scoping out loud — it is graded, and it stops you building the wrong thing.

Functional requirements to confirm:

  • The machine holds products in slots, each with a price and a stock count.
  • A user inserts money (coins and/or notes in fixed denominations), selects a product, and the machine validates that the selection is in stock and the money is sufficient.
  • On success the machine dispenses the product and returns change; it can also cancel and refund the inserted money before a purchase completes.
  • The machine must handle out of stock, insufficient funds, and insufficient change.

Non-functional / constraints to surface (this is where senior candidates separate themselves):

  • The machine's behaviour depends on its current state — the same "select product" action is valid after money is inserted and invalid before — so the model must make state explicit.
  • Extensibility — new coin/note denominations and new products should be addable without rewriting existing logic; adding a new state (say, a maintenance mode) should not force edits across every method.
  • Scope boundaries — are we handling card payments, a maintenance/restock flow, multiple concurrent buyers on one machine? Agree what is in and out, out loud, so you are not marked down for "missing" something you deliberately deferred.

State your assumptions explicitly: "I'll assume cash only in fixed denominations, one buyer at a time, and restocking handled by a separate maintenance flow I'll stub — tell me if you want card payments in scope." That one sentence shows you prioritise, which is itself part of the score.

Step 2: Identify the core entities

With requirements agreed, name the classes before writing any. The nouns in the problem are your first draft of the object model — but the real insight here is that the machine's verbs behave differently per state, so the states become first-class objects too:

  • VendingMachine — the top-level object (often a singleton); holds the inventory, the money inserted so far, and a reference to its current state. It exposes the actions — insertCoin, selectProduct, dispense, refund — and delegates each to the current state.
  • State — an interface (or abstract class) with one method per action; concrete states implement them. This is the heart of the design.
  • Concrete states — IdleState (no money inserted), HasMoneyState (money inserted, awaiting selection), DispensingState (valid selection made, vending in progress); many designs also name an OutOfStockState or fold it into the checks.
  • Inventory — tracks each slot's product and stock count, and answers "is slot B4 available?".
  • Product (or Item) — name, price, and its slot code.
  • Coin / Note — denomination enums for the money the machine accepts and dispenses.

Naming the relationships out loud matters as much as the classes: a VendingMachine has one current State (which it swaps as events occur), has an Inventory, and the Inventory has many Products. That "has-a," plus "the machine delegates to its state," is the vocabulary an interviewer listens for.

A class diagram for a vending machine object-oriented design, showing a VendingMachine holding a reference to a State interface and delegating insertCoin, selectProduct, dispense and refund to it, with concrete IdleState, HasMoneyState and DispensingState classes implementing the interface, an Inventory of Product objects with slot codes and stock counts, and Coin and Note denomination enums, with the has-a relationships drawn as composition arrows and the state classes drawn implementing the interface.

Step 3: The State pattern — model the transitions

Before writing classes, draw the state machine out loud, because the transitions are the design. The machine moves between a small set of states, and each action is only legal in some of them:

  • Idle → insert money → HasMoney
  • HasMoney → insert more money → HasMoney; select an in-stock, affordable product → Dispensing; cancel → refund → Idle
  • Dispensing → dispense product, return change → Idle
  • An illegal action for the current state (press dispense while Idle, insert money while Dispensing) is rejected by that state, not handled by a scattered if.

A state-machine diagram for a vending machine, showing the Idle state transitioning to HasMoney when money is inserted, HasMoney looping on more money and transitioning to Dispensing on a valid in-stock affordable selection or back to Idle on cancel and refund, and Dispensing transitioning back to Idle after it dispenses the product and returns change, with rejected illegal transitions such as dispensing while idle marked as blocked.

Now sketch the classes — as skeletons you can talk through, not a finished program. Lead with the State interface and the delegation, because they carry the whole design.

enum Coin { PENNY, NICKEL, DIME, QUARTER, DOLLAR }

interface State {
    void insertCoin(VendingMachine m, Coin c);
    void selectProduct(VendingMachine m, String slot);
    void dispense(VendingMachine m);
    void refund(VendingMachine m);
}

class VendingMachine {
    private State state = new IdleState();
    private Inventory inventory;
    private int balance = 0;            // cents inserted so far

    // actions simply delegate to the current state
    public void insertCoin(Coin c)        { state.insertCoin(this, c); }
    public void selectProduct(String slot){ state.selectProduct(this, slot); }
    public void dispense()                { state.dispense(this); }
    public void refund()                  { state.refund(this); }

    public void setState(State s) { this.state = s; }       // transition
    // ... balance + inventory accessors
}

class IdleState implements State {
    public void insertCoin(VendingMachine m, Coin c) {
        m.addBalance(c.value());
        m.setState(new HasMoneyState());        // Idle -> HasMoney
    }
    public void selectProduct(VendingMachine m, String slot) {
        throw new IllegalStateException("insert money first");  // rejected
    }
    public void dispense(VendingMachine m) {
        throw new IllegalStateException("nothing selected");
    }
    public void refund(VendingMachine m) { /* nothing to refund */ }
}

class HasMoneyState implements State {
    public void selectProduct(VendingMachine m, String slot) {
        Product p = m.getInventory().get(slot);
        if (p == null || p.stock() == 0) {                 // out of stock
            m.refundAll(); m.setState(new IdleState()); return;
        }
        if (m.getBalance() < p.price()) return;            // need more money
        m.setState(new DispensingState(slot));             // HasMoney -> Dispensing
    }
    // insertCoin adds balance and stays in HasMoney; refund returns money -> Idle
}
Enter fullscreen mode Exit fullscreen mode

Talk through the choices as you write them: each state owns the rules for every action, so an illegal action is rejected in one obvious place rather than guarded by conditionals sprinkled across the machine; a transition is a single setState call; and adding a new state (maintenance mode) means a new class, not an edit to every method.

Step 4: The design decisions interviewers probe

The diagram is the easy half. The follow-ups live in a handful of decisions, and having an answer ready for each is what the round rewards.

  • Why State, not a status flag? The tempting first design is one VendingMachine with a status enum and a big if (status == HAS_MONEY) … else if … in every method. It works for three states and rots the moment you add a fourth, because every method must be touched and the illegal-transition checks are scattered. The State pattern localises each state's behaviour in its own class — open to extension, closed to modification — and makes the transitions explicit. Saying why you reached for it is the point of the problem.
  • Change-making — after a sale the machine returns balance − price in coins. The simple approach is greedy, from the largest denomination down, while it has stock of each coin. If it cannot make exact change, it must decide before vending: refuse the sale and refund, rather than short-change the customer. You can model this behind a ChangeStrategy so the algorithm can vary (greedy vs an exact-change optimiser).
  • The out-of-stock-after-payment race — the follow-up from the opening scenario. The customer has paid and selected, but stock is now zero. The clean answer: the transition into Dispensing re-checks and atomically decrements stock; if that fails, the machine refunds fully and returns to Idle. The money is never kept for a product that cannot be delivered.
  • Extensibility — new denominations are enum additions; new products are inventory entries; a new state is a new State class. If a new requirement forces edits across every method, your states are not carrying their own weight — say that out loud and adjust.
  • Edge cases — insufficient funds (stay in HasMoney, wait or refund on cancel), a jammed dispenser (refund, go to a fault state), and concurrent buyers if the machine is shared (guard balance and inventory). You will not design all of them; naming them shows you see past the happy path.

A diagram of the vending machine design decisions, showing on one side a rejected status-flag design with scattered if-checks versus the State pattern with each state's rules in its own class, in the middle a greedy change-making routine dispensing from the largest denomination down with a no-exact-change refuse-and-refund branch, and on the other side the out-of-stock-after-payment race resolved by an atomic stock re-check and decrement on the transition into dispensing with a full refund on failure.

Low-Level Design
LLD · Vending Machine
Design the state machine and defend the out-of-stock refund out loud

Practise on Zynter →

Design patterns that fit the vending machine

You do not need to force patterns in, but naming the ones that fit — and why — is a strong signal in an OOD round.

  • State — the machine itself. The standout pattern for this problem: each state (Idle, HasMoney, Dispensing) is a class owning the behaviour of every action in that state, and transitions are explicit setState calls. This is the decision the whole interview hinges on.
  • Strategy — change-making. A ChangeStrategy interface lets the coin-return algorithm vary (greedy today, an exact-change optimiser later) without touching the states.
  • Singleton — a physical machine is usually a single shared instance; note the trade-off that a singleton can complicate testing and must be thread-safe if the machine is accessed concurrently.
  • Factory — optional, to create products or states from a type, keeping construction out of the business logic.

Say it crisply: State is the one that wins this problem — it turns a rotting conditional into a set of small, closed classes; Strategy keeps change-making swappable; Singleton models the single physical machine but I'd keep it thread-safe and injectable for tests. Naming the trade-off, not just the pattern, is what reads as experience.

Low-Level Design
LLD · Core skills
Apply the State and Strategy patterns out loud

Practise on Zynter →

How to talk about a low-level design in an interview

The probes from every section rhyme. Master these five and you can defend any LLD, not just the vending machine.

  • "What are the core classes here?" → Name the nouns — VendingMachine, Inventory, Product, Coin — and then the insight that the states are objects too: a State interface with Idle, HasMoney and Dispensing implementations the machine delegates to. (communication)
  • "Why the State pattern and not a status flag?" → A status enum with big conditionals rots as states grow; State puts each state's rules in its own class, open to extension and closed to modification, and makes transitions explicit. (problem solving)
  • "The product goes out of stock after the customer pays — what happens?" → The transition into Dispensing re-checks and atomically decrements stock; on failure the machine refunds fully and returns to Idle, so money is never held for an undeliverable product. (technical execution)
  • "The machine can't make exact change — now what?" → It decides before vending: refuse the sale and refund rather than short-change the customer; I'd put change-making behind a Strategy so the algorithm can vary. (technical execution)
  • "Add a maintenance mode — what changes?" → A new State class and a transition into it; if that forced edits across every method, my states would not be carrying their own weight. (time management)

Each maps to a debrief dimension. Reading them teaches the answers; it does not teach you to deliver them while someone says "now the dispenser jams mid-vend" — that only comes from saying them out loud.

How to prepare: a short plan

You do not need to memorise one reference solution. You need the OOD process automatic, so any "design X" prompt becomes requirements → entities → classes → decisions.

  • Day 1: Drill the process on the vending machine — clarify, draw the state machine, model it with the State pattern — until you can get the states and transitions on the board in ten minutes.
  • Day 2: Drill the decisions — why State beats a flag, change-making and the no-exact-change branch, and especially the out-of-stock-after-payment transition. These are where the follow-ups live.
  • Day 3: Do it out loud, timed, with someone adding a requirement mid-answer (card payments, a maintenance mode, a jammed dispenser), then move to a second LLD (parking lot, elevator) to prove the process transfers.

This explainer feeds the broader low-level design interview track — the vending machine is the cleanest way to learn the State pattern, and the process it teaches transfers directly to the elevator, the ATM and the rest.

Frequently asked questions

What is a vending machine design interview actually testing?

It is testing object-oriented design, and specifically whether you can model behaviour that depends on state. There is no algorithm to crack; instead the interviewer watches how you turn a familiar machine into a clean set of classes where the same action — selecting a product — behaves differently depending on whether money has been inserted. The strongest answers recognise that this is a state machine and reach for the State pattern, keep the model open to new states and denominations, and handle the edge cases like out-of-stock-after-payment and no-exact-change. The vending machine is popular precisely because the domain is trivial, so the whole conversation is about the quality of your model and your ability to justify it out loud.

What are the main classes in a vending machine design?

A standard model has a VendingMachine as the top-level object (often a singleton) that holds an Inventory, the balance inserted so far, and a reference to its current State. State is an interface with one method per action — insertCoin, selectProduct, dispense, refund — and concrete classes IdleState, HasMoneyState and DispensingState implement it, each owning the rules for those actions in that state. Inventory tracks slots, products and stock counts; Product carries a name, price and slot code; and Coin and Note are denomination enums. The key is to state the central relationship — the machine delegates every action to its current state and swaps state on each event — not just list the classes.

Why use the State pattern for a vending machine?

Because the machine's behaviour depends entirely on what state it is in, and the naive alternative does not scale. If you model state as a single status enum and write a big if (status == HAS_MONEY) … else if … in every method, the logic for each state is scattered across the whole class, and adding a new state forces you to touch every method and risk missing an illegal-transition check. The State pattern instead gives each state its own class implementing a common interface, so all the rules for "what happens when you insert a coin while idle" live in one place. It makes transitions explicit (a single setState call), and adding a state such as maintenance mode is a new class rather than edits everywhere — the open/closed principle in action.

How does a vending machine handle making change and running out of change?

After a successful selection the machine owes the customer the inserted balance minus the product price, and it returns that amount in coins. The common approach is greedy — dispense from the largest denomination down while stock of each coin lasts — which is simple and works for standard denominations. The important edge case is when the machine cannot make exact change from the coins it holds: a good design checks this before vending and refuses the sale, refunding the customer, rather than dispensing the product and short-changing them. Modelling change-making behind a ChangeStrategy interface lets you swap the greedy algorithm for an exact-change optimiser later without touching the rest of the design.

What happens if a product sells out after the customer has paid?

This is a classic follow-up because it is an illegal real-world transition. The customer has inserted money and selected a slot, but the stock for that slot is now zero. The clean answer is that the transition into the dispensing state re-checks availability and atomically decrements the stock count; if that check fails, the machine does not proceed to dispense — it refunds the full inserted balance and returns to the idle state. The principle to state out loud is that money is never retained for a product the machine cannot deliver, and that the stock re-check-and-decrement must be atomic so two near-simultaneous buyers cannot both claim the last unit.

How do I practise a low-level design interview out loud?

Reading a vending machine solution is not the same as defending your state transitions while an interviewer sells out a slot after payment or jams the dispenser mid-vend. On Zynter, the LLD track runs live voice rounds modelled on how real low-level design loops run — you write your classes and methods in the editor and a voice interviewer follows up one question at a time ("why State and not a status flag?", "the product just sold out after they paid — what happens to their money?", "now it can't make exact change"), then gives you a debrief on problem solving, communication, technical execution and time management. The score is a practice signal, not a hiring decision.

Practise it on Zynter

You can name the State pattern. The round is won on defending the out-of-stock refund out loud.

Pick the Vending Machine low-level design round, write your state classes in the editor, and defend them out loud to a voice interviewer that keeps asking "why State and not a status flag?" and "the slot just sold out after they paid — what does your machine do?" — then read the debrief. Zynter.ai is on-demand mock interviews modelled on real company loops, with 150+ rounds across 20 companies including the low-level design track (Sep 2026).

Practise the Vending Machine LLD round →
Browse all interview tracks

Top comments (0)