</> MAANG.io
coding interview ยท 101

Foundations

Master coding interviews with comprehensive coverage of data structures, algorithms, and problem-solving techniques. Progress from fundamentals to advanced topics with expertly curated content.

0/255 solved 0% complete

Class Design

What is this?

These problems ask you to model a small real-world thing in code โ€” a bank, a coffee machine โ€” where the operations themselves are easy but the organization is the whole game. It is like drawing the floor plan of a building before anyone moves in: where does each responsibility live, and how do the rooms connect? Nobody grades your speed here; they grade whether your design is clean, safe, and easy to extend when a new feature shows up.

flowchart TD A["Model the domain as classes"] --> B["Give each class one clear job"] B --> C["Validate fully before mutating anything"] C --> D["Add new cases as data not new code"]

๐Ÿ’ก Fun fact: "Validate before you mutate" isn't just interview advice โ€” it's the backbone of how real banks process transfers. A money move that half-completes is a genuine financial incident, which is why production systems treat the whole operation as all-or-nothing.

๐Ÿ”“ The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


Core idea: These problems have no clever algorithm โ€” the operations are trivial. What's tested is how you model the domain: clean classes, a single source of truth, validating before you mutate, and a design where adding a new case is data, not a rewrite. Get the shape right and the code almost writes itself.


What's actually being graded

 Not: "what's the fastest algorithm?"
 But: "is this design correct, safe, and extensible?"
  • Correctness & safety โ€” never leave the system in a half-valid state. Validate fully, then mutate. A half-applied transfer is a financial incident.
  • Single source of truth โ€” centralize rules (e.g. one _valid_account helper) instead of copy-pasting checks into every method, where they drift and develop off-by-ones.
  • Separation of concerns โ€” split responsibilities so each class has one job; the orchestrator coordinates them.
  • Extensibility โ€” adding a drink / account type / rule should be registering data, not editing core logic.

The two problems

Class design splits into two practice problems: Design a Bank System (validate-before-mutate) and Design a Coffee Machine (separate inventory / recipes / machine)

  • Design a Bank System โ€” transfer/deposit/withdraw with invariants (valid account range, sufficient funds). The lesson: centralize validation, validate before mutating, and express transfer as validated withdraw-then-deposit. The "algorithm" is nothing; the discipline is everything.
  • Design a Coffee Machine โ€” model Inventory, Recipe (as data), and a Machine that mediates. dispense = check availability, then deduct atomically. Adding a drink is a new recipe entry, not new branching. This is the open-ended OOP question where the interviewer watches your clarifying questions and your class boundaries.

The design checklist

Principle In practice
Validate before mutate check all preconditions, then apply; never partially
Single source of truth one validation/rule method reused everywhere
Separation of concerns distinct classes, each one job; orchestrator wires them
Data over code new cases are registered, not hard-coded
Clear API contracts what does each method return on failure? (False vs raise)

The insight: in class design the win is legibility and safety. State the invariants, put each rule in exactly one place, and make the common extension (a new account/drink/rule) a one-line data change.


๐Ÿ““ Draw it yourself

  1. Class diagram. Sketch the Coffee Machine as Machine โ†’ Inventory + {Recipe} and trace a dispense that checks inventory, then deducts.
  2. Validate-then-mutate. For the Bank, draw a transfer: validate both accounts + funds first, then move money โ€” show the rejected path leaving balances untouched.

Snap photos and embed them with the /host-diagrams skill.


Key takeaways

  • No algorithm โ€” it's modeling. Clean classes, clear responsibilities, safe mutations.
  • Validate before mutating; never leave half-applied state (especially money).
  • One source of truth for each rule; don't scatter validation.
  • Make extension data, not code โ€” new drinks/accounts shouldn't touch core logic.
  • Why interviewers use these: they reveal whether you can model a domain and write maintainable, safe code โ€” the day-job skill algorithms don't test.

Order: Design a Bank System โ†’ Design a Coffee Machine.

Design a Bank System

Core idea: The arithmetic is trivial โ€” the whole problem is validation discipline: check everything in one place, before you touch a single balance, and let transfer reuse the very same checks as withdraw and deposit.

This is a pure class-design problem. There's no clever algorithm hiding here โ€” adding and subtracting numbers is something you learned in grade school. What an interviewer is really watching is whether you can build a small object whose rules live in one place, whose methods don't copy-paste the same guard four times, and whose 1-indexing doesn't quietly produce an off-by-one bug. Get the shape right and the code almost writes itself. Get it wrong and you'll be debugging a negative balance at 4 p.m.


Problem, rephrased

You're building the core ledger for a small credit union. When it opens, it has a fixed roster of member accounts, numbered the way humans number things โ€” starting at 1 โ€” each with a starting balance handed to you up front.

You must support three teller operations, and every one of them answers a single yes/no question: "Was this a legal move? If so, I did it. If not, I changed nothing."

  • deposit(account, money) โ€” add money to account. Returns True if the account exists, else False.
  • withdraw(account, money) โ€” remove money from account. Returns True only if the account exists and has at least money; else False.
  • transfer(from_account, to_account, money) โ€” move money from one account to another. Returns True only if both accounts exist and the source has enough; else False.

The non-negotiable invariant: a balance may never go negative, and a rejected operation must leave every balance exactly as it was.

Suppose the credit union opens with 3 accounts: [10, 100, 20] (so account 1 has $10, account 2 has $100, account 3 has $20).

Operation Reasoning Result Balances after
withdraw(3, 10) account 3 exists, has $20 โ‰ฅ $10 True [10, 100, 10]
transfer(2, 3, 40) both exist, account 2 has $100 โ‰ฅ $40 True [10, 60, 50]
deposit(1, 20) account 1 exists True [30, 60, 50]
transfer(1, 3, 90) account 1 only has $30 < $90 False [30, 60, 50] (unchanged)
withdraw(10, 5) account 10 doesn't exist (n = 3) False [30, 60, 50] (unchanged)

Notice the last two rows: a rejected operation is a no-op. Nothing moved.


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account โ€” no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.