LLD (Low-Level Design)

  1. Parking Lot System đź”—
  2. Tiny URL / URL Shortener đź”—
  3. Splitwise / Expense Sharing đź”—
  4. BookMyShow / Movie Ticket Booking đź”—
  5. Cab Booking / Ola-Uber like Ride Service đź”—
  6. Snake and Ladder Game đź”—
  7. Elevator System đź”—
  8. Vending Machine đź”—
  9. ATM Machine đź”—
  10. Library Management System đź”—
  11. Chess Game đź”—
  12. Restaurant Reservation System đź”—

_________________________________________________________________

Preferred structure for LLD 

  • A. PROBLEM STATEMENT
  • B. HAPPY PATH STORY (SIMPLE REAL LIFE STORY)
  • C. 6 MANDATORY STEPS FOR LLD DESIGN
  • D. COMPLETE CODE IMPLEMENTATION (Full working java code, instead of just method signatures). Because, complete code implementation helps me understand better
  • E. POSSIBLE FOLLOW-UP QUESTIONS FROM INTERVIEWERS (WITH ANSWERS)

Basic mandatory steps for LLD design 

StepWhat to DoWhy It's ImportantTips for You
1. Clarify RequirementsListen carefully. Ask clarifying questions (functional + non-functional).Interviewers judge how well you understand the problem before jumping into design.Ask about: scale (users/requests), constraints (read-heavy? write-heavy?), edge cases (empty string, special chars), custom aliases, expiration, analytics.
2. Identify Entities & RelationshipsList main classes (nouns) and operations (verbs). Think about relationships.This shows you can break down the problem.Write on paper/whiteboard: "Main entities: UrlShortenerService, UrlMapping, User, UrlStats"
3. Design Classes & Method SignaturesCreate classes with fields + key method signatures (no full implementation). Use PlantUML comments in IntelliJ.Interviewers want to see clean class design, not production code.Focus on: fields, constructors (if needed), public methods, enums. Keep it concise.
4. Draw Class DiagramGenerate diagram using PlantUML (or draw manually). Add arrows for relationships (composition, association, inheritance).Visual clarity is crucial. Interviewers love seeing relationships.Use *-- for composition, -- for association, multiplicity (1, *, 0..1). Export as SVG.
5. Explain the Design VerballyWalk through the diagram aloud: classes, relationships, how main operations work.This is the most important part — they test communication + thinking.Speak clearly: "ParkingLot has many Floors (composition). Floor contains many ParkingSpots..."
6. Discuss Trade-offs & AlternativesTalk about choices you made and why. Mention alternatives and their pros/cons.Shows depth and real-world thinking.Always cover: Why composition over inheritance? Hash vs counter for short URL? Collision handling? Scalability? DB choice?
Extra Important Points You Should Add

7. Handle Edge Cases (briefly mention)

=> Empty input, null values, special characters, expiration logic, rate limiting, etc.

8. Scalability & Future Extensions (if time allows)

=> If we need to scale to 1M URLs/day, I'd use Redis for caching and sharding on shortUrl hash.

9. SOLID Principles (optional but impressive) 

=> I kept UrlShortenerService single responsibility by separating UrlMapping and UrlStats.

How to generate LLD diagram with puml code online?

=> Prepare the puml code
=> Add the entire puml code here : https://www.plantuml.com/plantuml and submit to generate LLD diagram instantly

What is Composition vs Inheritance ?

1. Inheritance ("Is-A" Relationship)

=> One class inherits from another class.
=> The child class is a type of the parent class.
=> Uses the extends keyword.

Example : 

class Animal {
    void eat() { ... }
}

class Dog extends Animal {   // Dog IS-A Animal
    void bark() { ... }
}

When to use Inheritance:

=> When there is a clear "Is-A" hierarchy.
=> When you want to reuse code and behavior from parent class.
=> Example: Car extends Vehicle, Square extends Shape 

Disadvantages:

=> Tight coupling (changing parent affects all children)
=> Breaks encapsulation
=> Difficult to change later (Fragile Base Class Problem)
=> Multiple inheritance not allowed in Java 

2. Composition ("Has-A" Relationship)

One class contains (has) an instance of another class.
The child class has a reference to the other class.
No extends — just a private field.

Example : 

class Engine {
    void start() { ... }
}

class Car {                  // Car HAS-A Engine
    private Engine engine;   // Composition

    public Car() {
        this.engine = new Engine();
    }
}

When to use Composition (Preferred in most modern designs):

When one class contains or owns another class.
When you want flexibility and loose coupling.
Example: ParkingLot has Floors, Floor has ParkingSpots, Car has Engine

Advantages of Composition:

Loose coupling — easier to change or replace parts
Better encapsulation
More flexible (can change behavior at runtime)
Follows "Favor Composition over Inheritance" principle (from Gang of Four)

Real Example from Parking Lot

Inheritance (not recommended here):

class ParkingSpot extends Vehicle { ... }   // Wrong! A spot is not a vehicle

Composition (correct):

class ParkingSpot {
    private Vehicle currentVehicle;   // ParkingSpot HAS-A Vehicle
    ...
}

Another good example: 

class ParkingLot {
    private List<Floor> floors;   // ParkingLot HAS-A Floor (Composition)
}

Summary
=> Inheritance is an 'Is-A' relationship, where a class extends another class to reuse behavior (e.g., Dog extends Animal).
=> Composition is a 'Has-A' relationship, where a class contains an instance of another class (e.g., Car has an Engine). 
=> Use Inheritance when the relationship is "Is-A" and the hierarchy is stable
=> Use Composition when the relationship is "Has-A" or when you want flexibility (most real-world cases).
=> "Favor Composition over Inheritance" is a very famous design principle

Is okay to use in-memory storage (for example, consider TinyURL LLD) ?

=> In TinyURL we used the following maps to store the data in memory. It is okay for LLD purpose. 
=> In real world systems, we use Redis or database persistence. 

public class UrlShortenerService {

    private final Map<String, UrlMapping> shortToLong = new HashMap<>();
    private final Map<String, UrlMapping> longToShort = new HashMap<>();

    // ... methods
}

Let’s understand what actually happens:

  • When the server starts:
    • One instance of UrlShortenerService is created (usually as a Spring @Service bean).
    • The two empty HashMaps are created once when the object is instantiated.
  • When you call shortenUrl():
    • Data (shortUrl ↔ longUrl mapping) is added into these two maps.
    • The data stays in these maps as long as the application is running.
  • When you call redirect():
    • It looks up data from these same maps (in memory).
    • It does not get data from any "session" .
  • Important Reality Check

    These maps are in-memory only. That means:

    • All short URLs are stored only in RAM (Heap memory) of this service.
    • If you restart the server, all data in these maps will be lost.
    • If you have multiple instances of this service (load balancing), each instance will have its own separate maps → inconsistency.

    This is why in real systems we don't use plain HashMap for production. We use a database (like Redis or PostgreSQL) for persistence and scalability.

    Summary

    => The maps are not session-based.
    => They are instance variables (in-memory) of the service class.
    => Data stays as long as the application is running.
    => They are lost when server restarts.
    => For LLD, this is fine. Just mention the limitation. 
     

    What is Redis?

    => Redis is an in-memory data store that is extremely fast.
    => It is often called a "super fast cache" or "in-memory database".

    Normal Database (PostgreSQL / MySQL)Redis
    Stores data on hard diskStores data in RAM (memory)
    Slow (because disk access)Extremely fast (memory access)
    Good for permanent storageGood for temporary, high-speed access
    Why do we use Redis in TinyURL?

    In TinyURL, we have two very frequent operations:

    1. Shorten URL → Check if long URL already exists → Create short URL
    2. Redirect → Given short URL, quickly find long URL

    These operations need to be super fast (milliseconds).

    If we use only HashMap (in-memory in our service):

    • Fast, but data is lost when server restarts
    • Not shared between multiple instances

    Redis solves both problems:

    • Data is stored in memory → very fast
    • Data persists even if server restarts (if configured)
    • Can be shared across multiple instances of your service

    Real-world usage of Redis in TinyURL

    // Instead of using HashMap:
    private Map<String, UrlMapping> shortToLong = new HashMap<>();

    // We use Redis:
    private RedisTemplate<String, UrlMapping> redisTemplate;