
The Architect's Mindset
When you walk into a building, you do not think about the 47 structural decisions that made it safe, fast to construct, and easy to expand. You just walk in and it works. That is the goal of High-Level Design to make technical decisions so thoughtfully that the resulting system feels effortless to its users.
High-Level Design (HLD) is the starting point of every system design conversation. Before you write code, before you design tables, before you define API endpoints you draw the map. Which components exist? How does a request enter and exit the system? Which components handle which responsibilities? Where does data live? What happens when demand spikes?
This guide covers every major HLD component, how they connect, what trade-offs they involve, and exactly how to practice building real architectures in ScaleDojo's Architecture Lab.
How to Read an Architecture Diagram
Before learning each component, understand the visual language. In HLD diagrams:
Boxes represent services or components (a CDN, a server, a database).
Arrows represent data flow. The direction of the arrow shows where data moves.
Dashed arrows typically represent asynchronous communication (fire-and-forget, queues).
Solid arrows represent synchronous calls (caller waits for response).
Numbers on arrows or labels indicate request types or data shapes.
Every time you see an architecture diagram, trace one request from entry to exit. Follow the arrows. Ask: where does this request start? Which component touches it? What data does each component produce or consume? Where does the response come from?
Component 1: The Client
Every architecture begins at the client the browser tab you have open, the Instagram app on your phone, the Slack desktop application, a Python script calling an API. The client initiates requests. It cannot push data to itself; it must ask.
Design questions to ask about clients:
Is this a browser (stateless between refreshes), native mobile app (background connectivity), or another server (server-to-server)?
Does the client need real-time data pushed to it? If yes, you need WebSockets or Server-Sent Events in your HLD.
Does the client need to work offline? If yes, consider service workers and a local database on-device (IndexedDB).
How much bandwidth does the client have? Mobile users on 4G need smaller payloads than desktop users on fiber.
Component 2: CDN- Content Delivery Network
Imagine you run a bakery in New York. Customers from Los Angeles, London, and Tokyo all want your bread. Shipping fresh bread across the world takes days and it would arrive stale. Instead you open satellite kitchens in each city that bake your recipe locally. Users get fresh bread fast.
A CDN works the same way. Your origin server lives in one data center. The CDN replicates your static files (images, CSS, JavaScript, fonts) to hundreds of edge nodes around the world. A user in Tokyo gets your JavaScript from a Tokyo edge node instead of traveling to Virginia.

What a CDN caches: static files (images, CSS, JS, fonts, videos), responses marked with cache-control headers, HTML pages that do not change per user.
What a CDN does NOT cache: personalized responses (your user feed, private messages), anything requiring authentication to produce, real-time data.
Secondary benefits: CDNs absorb DDoS attacks at the edge before they reach your origin servers. They compress assets automatically. They handle TLS termination. Examples: Cloudflare, AWS CloudFront, Fastly.
Component 3: Load Balancer
A supermarket opens 12 checkout lanes not because one cashier cannot handle it, but because 12 cashiers working in parallel serve customers 12x faster. A load balancer is the supervisor who looks at all the queues and directs each new customer to the shortest one.
In software: your app server can handle, say, 2,000 requests per second before response times degrade. At peak, you receive 20,000 rps. You run 10 identical server instances. The load balancer distributes incoming requests across all 10. Every instance handles 2,000 rps. Users get fast responses.
Load Balancing Algorithms
Round Robin: Send requests to servers in rotation (server 1, 2, 3, 1, 2, 3...). Simplest. Works when all servers are identical and requests take similar time.
Least Connections: Route to the server with the fewest active connections. Smarter for variable-length requests. A slow request on server 1 means new requests go to server 2.
IP Hash / Consistent Hash: Route the same user to the same server every time (based on hashing their IP or session ID). Useful when server-side state is unavoidable (though stateless is preferred). Also used in database sharding.
Weighted Round Robin: Some servers handle more (maybe they have more RAM). Weight them accordingly faster servers get more traffic.
Load balancers also perform health checks pinging each server every 5-10 seconds. If a server fails to respond, the load balancer removes it from rotation automatically. Traffic continues flowing to healthy servers. This is how you survive server failures without users noticing.
Component 4: App Server (Compute)
After a request passes through the CDN and Load Balancer, it reaches the Application Server, often called the Compute Layer.
The Application Server is the brain of the system. It contains the business logic that processes requests, makes decisions, interacts with databases and caches, and generates responses.
While components like CDN and Load Balancer help route traffic efficiently, the Application Server is where the actual work happens.
The most important rule in HLD: make your app servers stateless. This means:
No user data stored in the server's memory between requests.
No local file system writes that need to persist.
Session data stored in a shared external cache (Redis), not in-process memory.
Uploaded files sent immediately to blob storage (S3), not stored locally.
Why? If user A's session data is on server #3, and then server #3 crashes, user A loses their session. Worse, if the load balancer now routes user A to server #5, their session is gone. Stateless servers mean any server can handle any request. Adding or removing servers becomes trivial just change numbers in the load balancer config.

Component 5: Cache (Redis / Memcached)
Your database is a filing cabinet. Finding a record requires opening the right drawer, flipping through folders, finding the document. A cache is a whiteboard next to your desk. Before opening the filing cabinet, you check the whiteboard. Information that is needed frequently lives on the whiteboard. It takes one glance instead of the filing cabinet trip.

Cache Patterns
Cache-Aside (Lazy Loading): On request, check cache. If miss, query DB, store in cache, return. Simple. Handles cold starts. Risk: thundering herd on cache expiry.
Write-Through: Write to cache AND DB at the same time on every write. Cache always has fresh data. Higher write latency but cache is always warm.
Write-Behind (Write-Back): Write to cache immediately, sync to DB asynchronously later. Fastest writes but risk of data loss if cache crashes before sync.
Read-Through: Cache sits in front of DB. Reads always go through cache. Cache fetches from DB on miss and populates itself. Good for read-heavy workloads.
Cache Invalidation- The Hard Problem
There is a famous saying in computer science: 'There are only two hard things cache invalidation and naming things.' When the underlying data changes, how do you update or expire the cached copy?
TTL (Time-To-Live): Cache entries expire after N seconds. Simple. Some staleness is acceptable.
Event-driven invalidation: When DB writes happen, emit an event that clears the related cache keys. More complex, fresher data.
Versioned keys: Include a version number in cache keys. Increment version on write. Old keys become orphaned and expire naturally.
Component 6: Database- The Heart of Durability
Unlike the cache (which loses data on restart), the database persists data to disk. It survives power failures, crashes, and restarts. This durability comes with a cost: disk I/O is orders of magnitude slower than memory.
SQL vs NoSQL The Decision Framework
This is one of the most debated questions in system design. Here is a practical decision matrix:
Use SQL when: You need ACID transactions (bank transfers, order payments), data is relational (users have orders, orders have items), you need complex joins and aggregations, or data schema is stable and well-understood.
Use NoSQL Document DB when: Schema is volatile and changes often, you store JSON-like hierarchical data, you need to scale horizontally across many servers, eventual consistency is acceptable.
Use NoSQL Key-Value when: You need sub-millisecond lookups by a single key, use case is caching, session storage, or rate limiting.
Use NoSQL Wide-Column when: You have extremely high write throughput (millions/second), time-series or event log data, and you can tolerate eventual consistency.
Use NoSQL Graph when: Your queries are fundamentally about traversing relationships (friends-of-friends, product recommendations, fraud paths).
Database Scaling Patterns
Read Replicas: One primary DB handles all writes. Multiple replica copies synchronize and handle read queries. Most systems are 10:1 read:write replicas dramatically reduce primary load.
Vertical Scaling: Give the DB machine more RAM and CPU. Simplest. Has limits you cannot scale infinitely with bigger machines.
Sharding (Horizontal Partitioning): Split data across multiple DB instances. User IDs 1-1M on shard 1, 1M-2M on shard 2, etc. Complex to implement. Used at Twitter, Instagram scale.
Connection Pooling: DB connections are expensive to create. Keep a pool of open connections (PgBouncer, ProxySQL) and reuse them. Essential in production.
Component 7: Message Queue (Kafka / RabbitMQ / AWS SQS)
You go to a restaurant and order food. The waiter does not stand in front of you while the chef cooks for 20 minutes. The waiter takes your order (writes it down), confirms it, and goes to serve other tables. The kitchen picks up your order when ready. You got a fast acknowledgment. The work happened asynchronously.
Message queues do the same thing in software. The producer (server) drops a message and returns immediately. The consumer (worker) picks it up and processes it at its own pace. This decoupling provides:
Speed: The API responds in 200ms instead of 8,000ms. Users do not wait for background work.
Resilience: If the consumer crashes, messages stay in the queue. Nothing is lost. The consumer restarts and picks up where it left off.
Scale: Add more consumer instances to parallel-process the queue. Scale consumers independently from the API.
Backpressure: If consumers are overwhelmed, messages queue up rather than crashing the service.

Component 8: Blog Storage (AWS S3, Google Cloud Storage)
Think of blob storage as an infinitely large, incredibly cheap hard drive operated by AWS or Google. You put files in, you get a URL back. The service handles replication, durability, redundancy, and CDN integration.
Rule: Never store binary files (images, videos, PDFs, exports) in your relational database. Store the file in blob storage, store the URL in the database.

Component 9: Search Engine (Elasticsearch)
When you type 'coffee shops near me with wifi' in a search bar, the system must find posts, locations, reviews, and tags that are semantically close to your query even if none of them contain that exact phrase. Relational databases cannot do this efficiently. They use B-tree indexes optimized for exact matches.
Elasticsearch uses inverted indexes the same structure used by book indexes. Instead of scanning every post, it jumps directly to the list of documents that contain 'coffee' or 'wifi' and takes the intersection. Latency: milliseconds across billions of documents.
The pattern: write to your primary DB first, then sync to Elasticsearch (either near-real-time via change data capture, or via a background worker reading from a queue). Reads go to Elasticsearch for search, to the primary DB for everything else.
Component 10: API Gateway
In a microservices system with 30+ internal services, exposing every service's IP/port to the internet is a security nightmare and a maintenance nightmare. The API Gateway is the single, hardened front door. Features:
Authentication: Verify JWT tokens or API keys before forwarding requests. Services behind the gateway can trust that any request reaching them is already authenticated.
Rate Limiting: Block clients making too many requests per second. Protect services from abuse and overload.
Request Routing: POST /v1/payments → PaymentService. GET /v1/users → UserService. The client calls one URL; the gateway fans out internally.
Request Transformation: Translate between protocols (HTTP/REST externally, gRPC internally). Aggregate multiple internal calls into one external response.
Logging: Centralized request/response logging, tracing, and analytics.
Putting It Together: URL Shortener HLD
Let us design a complete HLD for a URL shortener (like bit.ly). Simple enough to fit in one blog section, complex enough to show real architecture thinking:

Non-Functional Requirements: What to Clarify Before Drawing Anything
Never start drawing boxes until you answer these questions:
Scale: How many daily active users? Peak concurrent requests? Reads vs writes ratio?
Latency: What is acceptable p99 latency? (p99 means 99% of requests complete in under N ms)
Availability: 99.9% = 8.7 hours downtime/year. 99.99% = 52 minutes/year. 99.999% = 5 minutes/year.
Consistency: Must every user see exactly the same data at all times (strong consistency) or is showing data that is a few seconds old acceptable (eventual consistency)?
Durability: If we lose a write, is that catastrophic (financial transaction) or acceptable (social media 'like')?
Geography: Where are users? Single region or multi-region?
Data volume: How much data is written per day? How long is it retained?
Trade-Off Thinking: The Heart of HLD
System design is not about finding the perfect solution. There is no perfect solution. It is about making explicit trade-offs and explaining your reasoning:
Consistency vs Availability: In a distributed system, when a network partition occurs, you must choose. Banks choose consistency (your balance must be accurate, even if the service is temporarily unavailable). Twitter chooses availability (show a slightly stale timeline rather than an error).
Read performance vs Write complexity: Adding a cache makes reads faster but requires a cache invalidation strategy adding write complexity.
Simplicity vs Scalability: A monolith is simpler to build, deploy, and debug. Microservices scale components independently but add operational overhead. Start monolith, extract services when you have genuine scaling evidence.
Cost vs Performance: More servers cost more money. More caching reduces DB load but adds operational cost. Faster CDNs are pricier.
How to Practice HLD in ScaleDojo's Architecture Lab
From the main dashboard, click Architecture Lab.
You land on the Level Map. Read the unlock progression complete levels sequentially.
Click Level 1 (Static Website Delivery). Read the scenario brief carefully. What does the system need to do? What constraints exist?
Open the component palette on the left sidebar. Drag relevant components onto the canvas.
Draw connections between components using click-and-drag on the connection points.
Hit the Deploy button. The engine scores your design and explains every point gained or missed.
Read the feedback, adjust your design, and deploy again. The best learning is in the iteration.
Use AI Hints when stuck for 10+ minutes. Use AI Analysis after completing a level to understand your design more deeply. Do levels 1-5 before moving to the next blog.
Common Beginner Mistakes
Putting microservices in Level 1. Start with a monolith. Add complexity only when requirements demand it.
Forgetting the cache. Almost every read-heavy system needs one. The database will become the bottleneck.
Single database with no read replicas. At scale, one instance cannot handle all reads.
Storing files in the database. Always use blob storage for binary files.
No message queue for slow operations. If a task takes more than 200ms, make it asynchronous.
Not specifying why. Saying 'I added Redis' is incomplete. Say 'I added Redis as a cache to reduce database load for the read-heavy feed query.'
Continue Reading
LLD In Depth: /blogs/lld-low-level-design-complete-beginner-blueprint
API Design In Depth: /blogs/api-design-complete-beginner-blueprint-rest-to-production
How All Three Connect: /blogs/how-hld-lld-and-api-design-work-together
Every system at Netflix, Uber, Google, and Amazon started with someone asking: 'What boxes do we need and how do they connect?' That question is HLD. Start there.
