Episode 9 — System Design / 9.1 — LLD Foundations
9.1.d — UML Diagrams
In one sentence: UML (Unified Modeling Language) diagrams are the visual language of LLD — class diagrams show static structure (what exists), sequence diagrams show dynamic behavior (what happens over time), and knowing both is essential for communicating designs in interviews and teams.
Table of Contents
- 1. What Is UML?
- 2. Class Diagrams
- 3. Sequence Diagrams
- 4. When to Use Each Diagram
- 5. How to Draw UML in Interviews
- 6. More ASCII-Based Examples
- 7. Key Takeaways
1. What Is UML?
UML (Unified Modeling Language) is a standardized visual language for describing software designs. It is NOT a programming language — it is a communication tool.
┌─────────────────────────────────────────────────────────┐
│ UML DIAGRAM TYPES │
│ │
│ STRUCTURAL (Static) BEHAVIORAL (Dynamic) │
│ ───────────────── ──────────────────── │
│ • Class Diagram ◄─── • Sequence Diagram ◄──── │
│ • Object Diagram │ • Activity Diagram │ │
│ • Component Diagram │ • State Diagram │ │
│ • Package Diagram │ • Use Case Diagram │ │
│ │ │ │
│ Most used Most used │
│ in LLD in LLD │
└─────────────────────────────────────────────────────────┘
For LLD interviews, you primarily need two diagram types:
- Class Diagrams — show the structure (classes, attributes, methods, relationships)
- Sequence Diagrams — show the flow (how objects interact over time for a specific scenario)
2. Class Diagrams
A class diagram shows the static structure of a system: what classes exist, what they contain, and how they relate.
2.1 Anatomy of a Class Box
Every class in a UML class diagram is drawn as a rectangle divided into three compartments:
┌─────────────────────────────┐
│ ClassName │ ← 1. Class Name (bold, centered)
├─────────────────────────────┤
│ - privateField: Type │ ← 2. Attributes (fields/properties)
│ # protectedField: Type │
│ + publicField: Type │
├─────────────────────────────┤
│ + publicMethod(): RetType │ ← 3. Methods (operations)
│ - privateMethod(): void │
│ # protectedMethod(): Type │
└─────────────────────────────┘
Rules:
- The class name goes in the top compartment. Abstract classes use italics or the label
<<abstract>>. - Attributes go in the middle compartment with visibility, name, and type.
- Methods go in the bottom compartment with visibility, name, parameters, and return type.
2.2 Visibility Modifiers
| Symbol | Modifier | Meaning | Who Can Access |
|---|---|---|---|
+ | public | Open to all | Any class |
- | private | Hidden | Only the class itself |
# | protected | Family-only | The class and its subclasses |
~ | package | Neighbors-only | Classes in the same package/module |
2.3 Attributes and Methods
Attribute notation:
visibility name: Type = defaultValue
Examples:
- id: string
- name: string = "Unknown"
- balance: number = 0
+ readonly email: string
- items: OrderItem[]
Method notation:
visibility name(param1: Type, param2: Type): ReturnType
Examples:
+ getName(): string
+ setName(name: string): void
- validateEmail(email: string): boolean
+ findById(id: string): User | null
+ processPayment(amount: number, currency: string): PaymentResult
# calculateDiscount(): number
Static members are shown with an underline:
┌───────────────────────────┐
│ Logger │
├───────────────────────────┤
│ - instance: Logger │ ← underlined = static
├───────────────────────────┤
│ + getInstance(): Logger │ ← underlined = static
│ + log(msg: string): void │
└───────────────────────────┘
Abstract classes and methods use italics or <<abstract>>:
┌───────────────────────────┐
│ <<abstract>> │
│ Shape │
├───────────────────────────┤
│ # color: string │
├───────────────────────────┤
│ + area(): number │ ← italic = abstract method
│ + perimeter(): number │ ← italic = abstract method
│ + getColor(): string │ ← regular = concrete method
└───────────────────────────┘
Interfaces use the <<interface>> stereotype:
┌───────────────────────────┐
│ <<interface>> │
│ Serializable │
├───────────────────────────┤
│ │ ← no attributes (typically)
├───────────────────────────┤
│ + serialize(): string │
│ + deserialize(s: string): │
│ void │
└───────────────────────────┘
2.4 Relationship Arrows
┌──────────────────────────────────────────────────────────────────┐
│ UML RELATIONSHIP ARROWS │
│ │
│ Association: A ──────────── B (solid line) │
│ │
│ Aggregation: A ◇─────────── B (hollow diamond at A) │
│ │
│ Composition: A ◆─────────── B (filled diamond at A) │
│ │
│ Dependency: A ┄┄┄┄┄┄┄┄┄┄► B (dashed arrow) │
│ │
│ Inheritance: B ─────────────▷ A (solid + hollow tri) │
│ child parent │
│ │
│ Implementation: B ┄┄┄┄┄┄┄┄┄┄┄▷ A (dashed + hollow tri) │
│ class interface │
│ │
│ Directed Assoc: A ─────────────► B (solid + open arrow) │
│ A knows B, not vice versa │
└──────────────────────────────────────────────────────────────────┘
Adding multiplicity to arrows:
┌──────────┐ 1 * ┌──────────┐
│ Customer │───────────────│ Order │
└──────────┘ └──────────┘
Read: "One Customer has many (*) Orders"
Read: "Each Order belongs to one (1) Customer"
Adding role names:
┌──────────┐ manager 1 * ┌──────────┐
│ Employee │───────────────────│ Project │
└──────────┘ manages └──────────┘
2.5 Complete Class Diagram Example
Scenario: Online Bookstore
┌───────────────────┐
│ <<interface>> │
│ Searchable │
├───────────────────┤
│ +search(q): Book[]│
└────────▲──────────┘
┆ implements
┆
┌─────────────────┐ ┌───────┴───────────┐ ┌──────────────────┐
│ <<abstract>> │ │ BookCatalog │ │ Author │
│ BaseEntity │ ├───────────────────┤ ├──────────────────┤
├─────────────────┤ │ - books: Book[] │ │ - name: string │
│ # id: string │ ├───────────────────┤ │ - bio: string │
│ # createdAt: Date │ + addBook(b): void│ ├──────────────────┤
├─────────────────┤ │ + search(q): Book[] │ + getName(): str │
│ # generateId() │ │ + findByISBN(isbn)│ └────────┬─────────┘
│ + getId(): str │ │ : Book | null │ │
└────────▲────────┘ └───────────────────┘ │
│ extends ◆ │
┌────┴────┐ │ composition │ 1
│ │ │ 1..* │
┌───┴──────┐ ┌┴──────────┐ ┌─────┴──────────┐ │
│ Customer │ │ Order │ │ Book │──────────────────┘
├──────────┤ ├────────────┤ ├────────────────┤ association
│ - name │ │ - total │ │ - isbn: string │ (book knows author)
│ - email │ │ - status │ │ - title: string│
├──────────┤ ├────────────┤ │ - price: number│
│ +getEmail│ │ +addItem() │ │ - author: Author
│ +getName │ │ +getTotal()│ ├────────────────┤
└──────────┘ │ +checkout()│ │ +getTitle(): str
└─────┬──────┘ │ +getPrice(): num
│ └────────────────┘
│ ◆ composition
│ 1..*
┌─────┴──────────┐
│ OrderLineItem │
├────────────────┤
│ - book: Book │
│ - quantity: num│
│ - unitPrice:num│
├────────────────┤
│ +getSubtotal() │
└────────────────┘
3. Sequence Diagrams
A sequence diagram shows how objects interact over time for a specific scenario or use case. It captures the dynamic behavior of the system.
3.1 Components of a Sequence Diagram
┌──────────────────────────────────────────────────────────────────┐
│ SEQUENCE DIAGRAM COMPONENTS │
│ │
│ ACTORS / OBJECTS: │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │ User │ │ Server│ │ DB │ ← boxes at the top │
│ └───┬───┘ └───┬───┘ └───┬───┘ │
│ │ │ │ ← LIFELINES (dashed vertical) │
│ │ │ │ │
│ │
│ MESSAGES: │
│ ──────────────► Synchronous call (solid arrow) │
│ ┄┄┄┄┄┄┄┄┄┄┄┄► Asynchronous call (dashed arrow) │
│ ◄─ ─ ─ ─ ─ ─ Return (dashed line, open arrow) │
│ │
│ ACTIVATION BARS: │
│ ┌──┐ │
│ │ │ ← thin rectangle on lifeline = object is processing │
│ └──┘ │
│ │
│ TIME flows DOWNWARD ↓ │
└──────────────────────────────────────────────────────────────────┘
3.2 Message Types
| Arrow | Type | Meaning | Example |
|---|---|---|---|
──────► | Synchronous | Caller waits for response | API call, method invocation |
┄┄┄┄┄► | Asynchronous | Caller does NOT wait | Event emission, queue message |
◄─ ─ ─ ─ | Return | Response back to caller | Return value, HTTP response |
──────► (to self) | Self-call | Object calls its own method | Internal validation |
3.3 Activation Bars
Activation bars show when an object is actively processing a request:
┌──────┐ ┌──────┐
│Client│ │Server│
└──┬───┘ └──┬───┘
│ │
│ request() │
│────────────────►│
│ ┌┴┐ ← activation starts (Server is processing)
│ │ │
│ │ │ (server does work here)
│ │ │
│ response │ │
│◄─ ─ ─ ─ ─ ─ ─ ─┤ │
│ └┬┘ ← activation ends
│ │
3.4 Complete Sequence Diagram Example
Scenario: User places an order in an e-commerce system
┌──────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ ┌─────┐
│ User │ │ Controller │ │OrderService│ │PaymentSvc│ │ DB │
└──┬───┘ └─────┬──────┘ └─────┬──────┘ └────┬─────┘ └──┬──┘
│ │ │ │ │
│ POST /orders │ │ │ │
│─────────────►│ │ │ │
│ │ │ │ │
│ │ createOrder(dto)│ │ │
│ │────────────────►│ │ │
│ │ │ │ │
│ │ │ validate() │ │
│ │ │───┐ │ │
│ │ │ │ (self-call) │ │
│ │ │◄──┘ │ │
│ │ │ │ │
│ │ │ save(order) │ │
│ │ │────────────────────────────► │
│ │ │ │ │
│ │ │ orderId │ │
│ │ │◄─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
│ │ │ │ │
│ │ │ charge(amount) │ │
│ │ │────────────────►│ │
│ │ │ │ │
│ │ │ │ save(txn) │
│ │ │ │────────────►│
│ │ │ │ txnId │
│ │ │ │◄─ ─ ─ ─ ─ ─│
│ │ │ │ │
│ │ │ PaymentResult │ │
│ │ │◄─ ─ ─ ─ ─ ─ ─ ─│ │
│ │ │ │ │
│ │ │ updateStatus() │ │
│ │ │────────────────────────────► │
│ │ │ ok │ │
│ │ │◄─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
│ │ │ │ │
│ │ OrderResponse │ │ │
│ │◄─ ─ ─ ─ ─ ─ ─ ─│ │ │
│ │ │ │ │
│ 201 Created │ │ │ │
│◄─ ─ ─ ─ ─ ─ │ │ │ │
│ │ │ │ │
Reading this diagram:
- User sends a POST request to the Controller.
- Controller calls
createOrder()on the OrderService. - OrderService validates the order (self-call).
- OrderService saves the order to the DB and gets back an orderId.
- OrderService calls the PaymentService to charge the amount.
- PaymentService saves the transaction to the DB.
- PaymentService returns a PaymentResult.
- OrderService updates the order status in the DB.
- OrderService returns the OrderResponse to the Controller.
- Controller sends 201 Created to the User.
4. When to Use Each Diagram
| Diagram | Shows | Use When | Interview Usage |
|---|---|---|---|
| Class Diagram | Static structure — classes, attributes, methods, relationships | You need to show WHAT exists in the system | Almost always — this is the primary LLD artifact |
| Sequence Diagram | Dynamic behavior — how objects interact for a specific flow | You need to show HOW a particular operation works step-by-step | When interviewer asks "walk me through the flow of X" |
Decision Guide
┌────────────────────────────────────────────────────────────┐
│ WHICH DIAGRAM TO DRAW? │
│ │
│ Interviewer asks: Draw: │
│ ────────────────── ───── │
│ "Design the classes for X" → Class Diagram │
│ "What classes and methods?" → Class Diagram │
│ "Show me the structure" → Class Diagram │
│ │
│ "Walk me through the flow" → Sequence Diagram │
│ "What happens when a user does X?" → Sequence Diagram │
│ "How do objects interact?" → Sequence Diagram │
│ │
│ "Design a parking lot system" → Class Diagram FIRST, │
│ then Sequence for │
│ key flows │
└────────────────────────────────────────────────────────────┘
5. How to Draw UML in Interviews
5.1 Whiteboard / Paper Tips
- Start with class boxes — draw them as simple rectangles with three sections. Do not worry about perfect alignment.
- Add attributes first — list the most important fields. Skip getters/setters unless specifically asked.
- Add key methods — focus on the 3–5 most important methods per class. Mark visibility with +/-/#.
- Draw relationships — use the correct arrows. Label with multiplicity (1, , 1..).
- Annotate — write relationship type names near the arrows if clarity helps.
5.2 Speed Tricks for 45-Minute Interviews
| Trick | How |
|---|---|
| Abbreviate types | str for string, num for number, bool for boolean |
| Skip obvious getters | Do not write getName() if you have - name: str — interviewer assumes it |
| Group related classes | Draw them near each other on the board |
| Use consistent spacing | Leave room between boxes for arrows |
| Draw sequence diagrams vertically | They naturally flow top-to-bottom |
| Start simple, add detail | Begin with 3–4 core classes, then expand |
5.3 Common Interview Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| Drawing ALL classes at once | Overwhelming, hard to modify | Start with core entities, expand |
| No visibility modifiers | Shows lack of encapsulation awareness | Always use +/-/# |
| Missing relationships | Diagram is just isolated boxes | Connect every class that interacts |
| Wrong diamond direction | Diamond goes on the WHOLE side | Memorize: ◆ sits on the "owner" |
| Skipping multiplicity | Loses important design information | Add 1, , 1.. to every relationship |
| Too much detail on sequence | Interviewer loses track | Focus on the happy path first |
5.4 Minimal Interview Class Diagram Template
┌──────────────┐ ┌──────────────┐
│ ClassA │ │ ClassB │
├──────────────┤ ├──────────────┤
│ - field: Type│ │ - field: Type│
├──────────────┤ ├──────────────┤
│ + method() │ │ + method() │
└──────┬───────┘ └──────┬───────┘
│ 1 * │
│◆────────────────────│
│ composition
6. More ASCII-Based Examples
6.1 Chat Application — Class Diagram
┌────────────────────┐
│ <<interface>> │
│ MessageSender │
├────────────────────┤
│ +send(msg): bool │
└────────▲───────────┘
┆ implements
┌────┴────────┐
│ │
┌───┴──────────┐ ┌┴───────────────┐
│ TextSender │ │ MediaSender │
├──────────────┤ ├────────────────┤
│ +send(): bool│ │ +send(): bool │
│ │ │ +compress(): │
│ │ │ void │
└──────────────┘ └────────────────┘
┌──────────────────┐ 1 * ┌──────────────────┐
│ User │─────────│ ChatRoom │
├──────────────────┤ ├──────────────────┤
│ - userId: str │ │ - roomId: str │
│ - username: str │ │ - name: str │
│ - status: Status │ │ - members: User[]│
├──────────────────┤ ├──────────────────┤
│ + sendMessage() │ │ + addMember() │
│ + joinRoom() │ │ + removeMember() │
│ + leaveRoom() │ │ + broadcast() │
└──────────────────┘ └────────┬─────────┘
│ ◆ 0..*
┌────────┴─────────┐
│ Message │
├──────────────────┤
│ - msgId: str │
│ - sender: User │
│ - content: str │
│ - timestamp: Date│
│ - type: MsgType │
├──────────────────┤
│ + getContent() │
│ + getTimestamp() │
└──────────────────┘
6.2 Login Flow — Sequence Diagram
┌──────┐ ┌────────────┐ ┌──────────┐ ┌──────┐ ┌────────┐
│ User │ │ AuthCtrl │ │ AuthSvc │ │ DB │ │ JWT │
└──┬───┘ └─────┬──────┘ └────┬─────┘ └──┬───┘ └───┬────┘
│ │ │ │ │
│ POST /login │ │ │ │
│ {email, pwd} │ │ │ │
│─────────────►│ │ │ │
│ │ │ │ │
│ │ login(email, │ │ │
│ │ password) │ │ │
│ │───────────────►│ │ │
│ │ │ │ │
│ │ │ findByEmail │ │
│ │ │────────────►│ │
│ │ │ │ │
│ │ │ user │ │
│ │ │◄─ ─ ─ ─ ─ ─│ │
│ │ │ │ │
│ │ │ verifyHash │ │
│ │ │──┐ │ │
│ │ │ │ │ │
│ │ │◄─┘ │ │
│ │ │ │ │
│ │ │ sign(payload) │
│ │ │─────────────────────────►│
│ │ │ │
│ │ │ token │
│ │ │◄─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
│ │ │ │ │
│ │ AuthResponse │ │ │
│ │ {token, user} │ │ │
│ │◄─ ─ ─ ─ ─ ─ ─ │ │ │
│ │ │ │ │
│ 200 OK │ │ │ │
│ {token, user}│ │ │ │
│◄─ ─ ─ ─ ─ ─ │ │ │ │
│ │ │ │ │
6.3 ATM System — Class Diagram
┌──────────────────┐
│ <<abstract>> │
│ Transaction │
├──────────────────┤
│ # txnId: str │
│ # amount: num │
│ # timestamp: Date│
├──────────────────┤
│ + execute(): bool│ ← abstract
│ + getReceipt(): │
│ str │
└────────▲─────────┘
│ extends
┌────┼─────────────┐
│ │ │
┌───┴────┴──┐ ┌──────┴──────┐ ┌───────────┐
│ Withdrawal│ │ Deposit │ │ Transfer │
├───────────┤ ├─────────────┤ ├───────────┤
│ +execute()│ │ +execute() │ │ +execute()│
│ +getReceipt │ +getReceipt() │ +getReceipt
└───────────┘ └─────────────┘ │ -toAccount│
└───────────┘
┌──────────────┐ 1 1 ┌──────────────┐
│ ATM │─────────│ CardReader │ composition
├──────────────┤ ├──────────────┤
│ - atmId: str │ │ +readCard() │
│ - location │ │ +ejectCard() │
├──────────────┤ └──────────────┘
│ +authenticate│
│ +selectTxn() │ 1 1 ┌──────────────┐
│ +printReceipt│◆────────│ CashDispenser│ composition
└──────┬───────┘ ├──────────────┤
│ │ +dispense() │
│ ┄┄┄► uses │ +getBalance()│
│ └──────────────┘
┌──────▼───────┐
│ Account │
├──────────────┤
│ - accNum: str│ 1 * ┌──────────────┐
│ - balance:num│◇────────│ Transaction │ aggregation
│ - pin: str │ └──────────────┘
├──────────────┤
│ +getBalance()│
│ +debit() │
│ +credit() │
│ +verifyPin() │
└──────────────┘
7. Key Takeaways
- Class diagrams show WHAT exists — classes, their attributes, methods, and relationships. They are the primary LLD artifact.
- Sequence diagrams show HOW things happen — the step-by-step interaction between objects for a specific scenario.
- Visibility matters — always use
+(public),-(private),#(protected) in your class boxes. - Relationship arrows have distinct meanings — solid line (association), hollow diamond (aggregation), filled diamond (composition), dashed arrow (dependency), triangle (inheritance/implementation).
- Multiplicity (1, , 1..) communicates how many instances relate and should be on every relationship line.
- In interviews, start simple — draw 3-4 core classes first, then expand. Do not try to draw the perfect diagram on the first attempt.
- Sequence diagrams flow downward — time progresses from top to bottom. Activation bars show when an object is processing.
- You do not need formal UML tools in interviews — ASCII diagrams, whiteboard sketches, or even verbal descriptions of the structure are perfectly acceptable.
Explain-It Challenge
Draw a class diagram and a sequence diagram for a simple Vending Machine. The class diagram should have at least 4 classes (VendingMachine, Product, Inventory, Coin). The sequence diagram should show the flow of a user inserting coins and selecting a product. Try it on paper before looking at any reference.
Previous → 9.1.c — Class Relationships Next → 9.1.e — LLD Interview Approach