Systems · 7 min read
Anatomy of a modern web app
When someone types your URL and hits enter, six layers of machinery wake up. Each one has its own job, its own failure modes, and its own way of pointing the finger at the next one.
The six layers
This is the path a single HTTP request takes from a browser tab to your database and back. Latencies are typical worst-case for a global request.
DNS
DNS resolution
Browser → resolver → root → TLD → authoritative → IP
20–200ms (cached: 0ms)
CDN
Edge / CDN
Nearest PoP terminates TLS, serves cached static assets, forwards dynamic requests
5–50ms from browser
LB
Load balancer / reverse proxy
Health checks, rate limits, routing rules, TLS re-termination if needed
<1ms internal
APP
Application server
Your code: parse request, run middleware, hit business logic, format response
5–500ms depends on you
CCH
Cache (Redis / Memcached)
Read-heavy data, session state, rate-limit counters. Skip if not hot.
0.5–2ms per call
DB
Database
Persisted state. Indexed reads, transactional writes, the source of truth.
1–20ms indexed reads
The pipeline, end to end
Read this top to bottom. Every line is real work the system does.
1DNS→ resolve app.example.com → CNAME chain → A record
2TCP→ 3-way handshake to the CDN edge
3TLS→ ClientHello → cert → key exchange → encrypted channel
4HTTP→ GET /dashboard with cookies and headers
5CDN→ cache lookup. Static? Serve from edge. Dynamic? Forward to origin.
6LB→ pick a healthy app server, attach X-Forwarded-For, forward
7APP→ parse JWT, run auth middleware, dispatch to /dashboard handler
8CACHE→ GET user:42:dashboard → MISS
9DB→ SELECT * FROM widgets WHERE user_id = 42 (indexed)
10CACHE→ SET user:42:dashboard, TTL 60s
11APP→ render JSON, return 200
12LB→ stream response back to CDN
13CDN→ stream to browser, store in edge cache if cacheable
14BROWSER→ parse, paint, hydrate, fire window.onload
The compounding rule: each layer's "fast" is the next layer's "slow." A 50ms app response is fine in isolation. Add DNS (200ms first hit) + TLS (100ms) + edge (40ms) + LB (5ms) + DB (15ms) and you've already spent 410ms before the user sees pixels. Optimize the right layer.
Where things actually break
DNS
TTL too high, propagation delay, wildcard not configured. Stale cached resolution after a migration.
CDN
Cache poisoning, wrong cache-control header, edge serving stale 5xx for 60 seconds.
LB
Health check timeout too aggressive. Healthy backends marked dead during a slow query.
APP
N+1 queries, blocking I/O on the request thread, unhandled exception crashing the worker.
Cache
Stampede: 10k requests all miss at once and hammer the DB. Mitigate with single-flight or random TTL jitter.
DB
Missing index, lock contention, connection pool exhausted, query plan flip after stats update.
The four numbers worth memorizing
Order-of-magnitude latency, from Jeff Dean's classic:
-
L1 cache reference
0.5 ns
-
Main memory reference
100 ns
-
SSD random read
150 µs
-
Network round-trip same datacenter
500 µs
-
Network round-trip US ↔ EU
150 ms
Memory is 200× faster than SSD. SSD is 3,000× faster than a cross-continent network call. Every architectural decision is a bet about which of these you can avoid.