Skip to content
HLD vs LLD vs API Design: How They Work Together 6 min

HLD vs LLD vs API Design: How They Work Together

SD
ScaleDojo
May 11, 2026
6 min read1,395 words
HLD vs LLD vs API Design: How They Work Together
How HLD, LLD, and API Design Work Together

Why They Feel Separate but Are Actually OneSystem

HLD vs LLD-most beginners learn these as separate topics,along with API Design,in three separate courses. They finish all three and still cannot connect them. They can describe a CDN, draw an ER diagram, and write a REST endpoint — but they cannot explain how those three interlock when building a real system. This guide builds that bridge.

The simplest mental model: HLD draws the map, LLD designs the buildings, API Design specifies the roads between them.

  • HLD (High-Level Design): Which services exist? Where does data live? How does a request flow from user to response? Think in systems, boxes, and arrows.

  • LLD (Low-Level Design): Inside each service, how is the database structured? What tables, columns, types, and indexes? What classes and methods?

  • API Design: How do services talk to each other and to clients? What contracts govern communication? Endpoints, request/response schemas, authentication.

They are not sequential phases. Changes in one force changes in all three. Add a new feature (HLD decision) and you need a new table (LLD change) and a new endpoint (API change). Delete a database column (LLD change) and you must update every API that returned that field (API change) and re-assess whether the service that used it still makes architectural sense (HLD change).

Full Twitter-Like System — Designed Across All Three Layers

HLD Layer — The Map

Requirement: Users can register, post tweets, follow other users, and see a home timeline of tweets from people they follow. Support 100M monthly users, 1M tweets per day.

HLD Components:

  Client (Browser / Mobile App)
    ↓
  CDN (static assets: images, JS, CSS)
    ↓
  API Gateway (rate limiting, auth verification, routing)
    ↓
  ┌─────────────────────────────────────────────────────┐
  │  Load Balancer                                       │
  │  ┌─────────────────┐   ┌──────────────────────────┐ │
  │  │  User Service   │   │  Tweet Service           │ │
  │  │  (auth, profile)│   │  (create, read tweets)   │ │
  │  └───────┬─────────┘   └─────────┬────────────────┘ │
  └──────────│─────────────────────  │───────────────────┘
             │                       │
         PostgreSQL              PostgreSQL
         (user data)             (tweet data)
                    ↘         ↙
                     Redis Cache
                  (home timelines,
                   user sessions)
                         │
                   Message Queue (Kafka)
                   (fanout: distribute new
                    tweets to followers'
                    timeline caches)

LLD Layer — The Buildings

Now we go inside each service and design the database schema:

-- User Service database
CREATE TABLE users (
    id            BIGSERIAL PRIMARY KEY,
    username      VARCHAR(50)  NOT NULL UNIQUE,
    email         VARCHAR(255) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    display_name  VARCHAR(100),
    bio           TEXT,
    avatar_url    TEXT,
    follower_count INT  DEFAULT 0,    -- denormalized for fast reads
    following_count INT DEFAULT 0,
    created_at    TIMESTAMPTZ  NOT NULL DEFAULT now()
);

CREATE TABLE follows (
    follower_id   BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    following_id  BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (follower_id, following_id)
);

-- Tweet Service database
CREATE TABLE tweets (
    id          BIGSERIAL PRIMARY KEY,
    author_id   BIGINT       NOT NULL,  -- no FK (different DB/service!)
    content     VARCHAR(280) NOT NULL,
    like_count  INT          DEFAULT 0,
    reply_count INT          DEFAULT 0,
    reply_to_id BIGINT       REFERENCES tweets(id),  -- for threads
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT now()
);

CREATE INDEX idx_tweets_author_created ON tweets(author_id, created_at DESC);
CREATE INDEX idx_tweets_created        ON tweets(created_at DESC);

API Design Layer — The Roads

# Contracts between clients and services:

# --- User Service Endpoints ---
POST   /v1/auth/register     {username, email, password}  → 201 {id, username, token}
POST   /v1/auth/login        {email, password}            → 200 {access_token}
GET    /v1/users/{username}                               → 200 {profile object}
PATCH  /v1/users/{id}        {display_name?, bio?}        → 200 {updated}
POST   /v1/users/{id}/follow                              → 204
DELETE /v1/users/{id}/follow                              → 204
GET    /v1/users/{id}/followers  ?limit&cursor             → 200 {paginated}
GET    /v1/users/{id}/following  ?limit&cursor             → 200 {paginated}

# --- Tweet Service Endpoints ---
POST   /v1/tweets            {content, reply_to_id?}      → 201 {tweet}
GET    /v1/tweets/{id}                                    → 200 {tweet}
DELETE /v1/tweets/{id}                                    → 204
GET    /v1/tweets/{id}/replies ?limit&cursor              → 200 {paginated}

# --- Timeline Endpoint (reads from Redis cache) ---
GET    /v1/timelines/home     ?limit&cursor&user_id=me     → 200 {paginated tweets}

The Hidden Connections — Where Layers Constrain Each Other

This is the insight that separates strong candidates from average ones: the three layers are not independent. Each layer's decisions create constraints and requirements in the other two.

HLD decision forces LLD change

HLD: 'We will cache the home timeline in Redis to avoid expensive database joins for every page load.'

LLD implication: This means the tweet data stored in Redis must be self-contained (include author's username and avatar_url). If a user changes their display name, you now need a cache invalidation strategy. The LLD must add a 'display_name_updated_at' timestamp to track this.

LLD change forces API change

LLD: 'We deleted the bio field from the users table to simplify the schema.'

API implication: The GET /v1/users/{username} endpoint currently returns bio in its response. Every mobile client that was rendering that field now receives null. This is a breaking change. You must version the API (introduce /v2/) or communicate the deprecation with advance notice and a sunset date.

API change reveals HLD gap

API: 'We want to add a search endpoint: GET /v1/search?q=cats'

HLD implication: A full-text search endpoint cannot be served efficiently from the existing tweets PostgreSQL table unless we add full-text indexing (expensive and limited in PostgreSQL). This changes the HLD: we need to add Elasticsearch as a separate component, set up tweet indexing via the message queue, and design the security boundary around who can query the search index.

Integration Mistakes — Real Bugs That Come From Ignoring One Layer

  • You designed an API field called 'last_seen' (API layer) but never added a last_seen column to the users table (LLD layer). The API always returns null. Found in production three weeks after launch.

  • HLD says: cache user profiles in Redis for 1 hour. API does not have a cache invalidation mechanism (no endpoint to force-expire a profile). User changes display name, waits an hour for cache to expire. Users see stale name everywhere.

  • LLD uses an auto-incrementing integer for user IDs (1, 2, 3...). API exposes these in URLs as /v1/users/3. Security problem: crawlers can enumerate all users. HLD fix: use UUIDs or obfuscated IDs.

  • HLD decision: split into microservices. LLD of Tweet Service stores author_id but not author data (correct, separate service). But the API for timeline joins tweet data with author data and returns them together. Who does this join? The API Gateway? A new Timeline Service? The frontend with two separate requests? Nobody decided. Product breaks at launch.

The 45-Minute System Design Interview — Full Timeline

Most system design interviews give you 45-60 minutes. Here is how to allocate that time across all three layers while keeping the conversation structured:

  1. Minutes 0-5: Requirements Clarification. Ask: How many users? Read-heavy or write-heavy? What features are in scope? What devices? Any specific latency requirements? What does success look like? Write the agreed requirements visibly.

  2. Minutes 5-15: HLD Sketch. Draw the high-level architecture: client, CDN, API gateway, core services, databases, cache, queue. Name each box. Describe data flow for the 1-2 most critical user journeys (the happy path).

  3. Minutes 15-25: Deep Dive 1 (LLD / Database Design). The interviewer will often say 'Let's go deeper on the database.' Design the core tables: columns, types, relationships, primary keys, important indexes. Explain your choices.

  4. Minutes 25-35: Deep Dive 2 (Scaling / API). Address bottlenecks. How does the system handle peak load? Introduce sharding, read replicas, queue-based processing. Define 3-4 key API endpoints with request/response format.

  5. Minutes 35-45: Discuss trade-offs and open questions. What would you do differently with more time? What are the weak points? What metrics would you monitor?

Cross-Lab Practice in ScaleDojo

ScaleDojo has three separate labs — Architecture Lab, LLD Lab, API Design Lab. The most effective practice technique is designing the same system in all three labs:

  1. Start in the Architecture Lab. Design the HLD: services, data stores, traffic flow for a URL shortener.

  2. Switch to the LLD Lab. Design the database schema for the URL shortener: the urls table, user table, click events table. Add the right indexes.

  3. Switch to the API Design Lab. Design the API: POST /v1/urls, GET /{shortcode} (redirect), GET /v1/analytics/{shortcode}. Define status codes, pagination for analytics, authentication.

  4. Connect them mentally: Notice how the schema columns become API response fields. Notice how the HLD's choice of read-replica affects which database the API reads from. Notice how the analytics use case requires a time-series append-only table (LLD) exposed through a paginated endpoint (API).

Practice Systems — Design These Across All Three Layers

  • URL Shortener (classic beginner system: bit.ly clone). ~4 hours to design fully.

  • Ride-sharing Service (complex: Uber/Lyft clone). Location updates, matching, surge pricing.

  • Social Media Feed (Instagram/Twitter timeline). Fan-out, caching, media upload.

  • E-Commerce Cart and Checkout (Amazon clone). Inventory, payment, order state machine.

  • File Storage Service (Google Drive / Dropbox). Chunked upload, deduplication, sharing.

System design mastery is not memorizing architectures. It is learning to see how HLD, LLD, and API Design constrain and inform each other — and making trade-offs explicitly.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Related Articles

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.

ScaleDojo Logo
Initializing ScaleDojo