Skip to main content

Getting Ready: ATM System

Getting Ready: ATM System

Why This Problem Matters

There are over 3.5 million ATMs deployed worldwide, processing billions of transactions every month. Companies like NCR, Diebold Nixdorf, and Hyosung build ATM software stacks that must handle card authentication, balance inquiries, cash withdrawals with multi-denomination dispensing, deposits, fund transfers — all while maintaining strict transaction integrity. A single bug in an ATM's business logic can dispense incorrect amounts, lock customer cards, or leave accounts in inconsistent states.

From an engineering perspective, an ATM is a state-driven transaction machine combined with a denomination-based cash dispensing pipeline. At any moment, the ATM is in exactly one state — idle waiting for a card, authenticating a PIN, selecting a transaction, processing a withdrawal, or dispensing cash. Each user action transitions the machine between states, and not every action is valid in every state: you can't select a transaction before authenticating, and you can't insert a card while a transaction is already in progress.

This is why the ATM System is a frequently asked LLD interview question at companies like Amazon, Microsoft, Goldman Sachs, and JP Morgan Chase. It tests two patterns simultaneously: the State Pattern for managing the ATM's operational lifecycle, and the Chain of Responsibility Pattern for dispensing cash across multiple denominations (e.g., dispensing 270using270 using 100 bills first, then 50,then50, then 20). Interviewers use this problem to gauge whether a candidate can model a physically constrained system with real-world business rules — not just draw boxes and arrows.

Beyond patterns, this problem introduces critical design skills: handling PIN validation with attempt limits, maintaining transaction atomicity (what if the network goes down mid-withdrawal?), managing physical cash inventory across denominations, and designing for extensibility when new transaction types are added.

What You'll Learn

By the end of this design problem, you will:

  • Model an ATM as a state machine — identify states (Idle, CardInserted, Authenticated, TransactionSelected, CashDispensing), transitions, and the events that drive them
  • Apply the State Pattern to eliminate fragile if/else chains for state management, making the ATM's behavior clean and extensible (Fundamentals S12)
  • Apply the Chain of Responsibility Pattern to build a denomination-based cash dispensing pipeline where each handler (e.g., 100handler100 handler → 50 handler → $20 handler) processes what it can and passes the remainder to the next (Fundamentals S12)
  • Draw a State Machine Diagram showing every valid state transition, event, guard, and action
  • Draw a Class Diagram deriving each class, attribute, and relationship directly from requirements
  • Draw a Sequence Diagram tracing the complete cash withdrawal flow from card insertion to cash dispensing
  • Implement the complete system in Python and Java with full state management, PIN validation, and denomination-aware cash dispensing
  • Test edge cases — insufficient ATM cash, invalid PIN lockout, non-dispensable amounts, concurrent access scenarios

Prerequisites

Before starting this problem, make sure you're comfortable with:

  • OOP Fundamentals — classes, objects, encapsulation, and polymorphism (Fundamentals S2)
  • OOP Relationships — especially Composition and Association; you'll use both to model ATM components (Fundamentals S4)
  • State Pattern — how an object's behavior changes based on its internal state. The ATM's lifecycle is driven entirely by state transitions. If you're not confident with State, review Fundamentals S12 first.
  • Chain of Responsibility Pattern — how a request travels through a chain of handlers where each handler either processes it or passes it along. This models multi-denomination cash dispensing. Review Fundamentals S12 if needed.
  • UML State Machine Diagrams — how to read states, transitions, guards, and events (Fundamentals S5)
  • UML Class Diagrams — how to read classes, attributes, methods, and relationship arrows (Fundamentals S5)
  • UML Sequence Diagrams — how to trace message flow between objects for a specific use case (Fundamentals S5)

You should also have completed the Vending Machine (DP6), which uses the same State Pattern in a simpler setting. The ATM builds on that foundation by adding authentication, a multi-step transaction flow, and the Chain of Responsibility for cash dispensing.

Problem Scope

We will design: The core domain logic of an ATM system — card and PIN authentication, transaction selection (withdraw, deposit, balance inquiry, transfer), cash dispensing with multi-denomination support, transaction recording, and the state machine that governs the entire user session.

We will NOT design: Database persistence, network communication with the bank's core banking system, hardware drivers (card reader, cash dispenser motors, receipt printer), distributed ATM networks, a graphical user interface, or fraud detection systems. This is a Low Level Design (object-oriented) exercise, not a System Design (infrastructure) problem.

Difficulty: Medium — Compared to the Vending Machine (Easy), the ATM has more states, a multi-step authentication flow, and the Chain of Responsibility for denomination handling. Compared to Hard problems like Chess or Uber, the class count is moderate (8-12 classes) and there's a single user interacting with the machine at a time.

How a Real ATM Works

Before designing the software, let's understand the physical system we're modeling:

  1. Idle state — The ATM displays a welcome screen ("Insert your card"). No session is active.
  2. Card insertion — The user inserts their debit/credit card. The ATM reads the card number from the magnetic stripe or chip. The machine transitions to a state requiring PIN entry.
  3. PIN authentication — The user enters their Personal Identification Number (typically 4-6 digits). The ATM validates the PIN against the bank's records. Most banks allow 3 attempts before locking the card. Each failed attempt decrements the remaining attempts counter.
  4. Transaction selection — After successful authentication, the ATM displays a menu: Withdraw Cash, Deposit Cash, Check Balance, Transfer Funds. The user selects one.
  5. Transaction processing — For a withdrawal: the user enters an amount. The ATM checks (a) the user's account balance is sufficient, (b) the ATM has enough physical cash, and (c) the amount is dispensable using available denominations. For a balance inquiry: the ATM retrieves and displays the current balance. For a deposit: the ATM accepts cash/cheque and credits the account. For a transfer: the user specifies a destination account and amount.
  6. Cash dispensing — For withdrawals, the ATM's dispenser uses a denomination-based algorithm. If the user requests 270,andtheATMhas270, and the ATM has 100, 50,and50, and 20 bills, it dispenses 2×100+1×100 + 1×50 + 1×$20. Each denomination slot has a finite count of bills.
  7. Receipt and session end — The ATM offers to print a receipt, ejects the card, and returns to the idle state.
  8. Error handling — If the network goes down, the ATM may operate in offline mode with reduced limits. If the cash dispenser jams, the machine goes out of service. If the user takes too long, the session times out and the card is ejected.

Notice how these steps naturally form a state machine — each step corresponds to a state, and user actions trigger transitions between them. The cash dispensing step naturally fits a chain of handlers — one handler per denomination.