The single most common interview question in tech — “what happens when you type google.com?” — answered properly, one layer at a time.
Prerequisites: Computer Fundamentals Time to read: ~20 minutes
You write fetch("https://api.example.com/users") and data comes back. Between those two moments,
roughly a dozen systems cooperated across thousands of kilometres, any of which can fail, be slow,
or lie to you.
You cannot design distributed systems without a mental map of that journey, because every box you draw in an architecture diagram is an arrow that crosses a network, and every one of those arrows can be slow, drop, duplicate, or reorder your data.
Sending a letter from Lahore to Tokyo:
| Postal | Network |
|---|---|
| The letter you wrote | Your HTTP request (application data) |
| The envelope with an address | TCP segment (ports = which mailbox at that address) |
| The address on the envelope | IP packet (source and destination IP) |
| Trucks, planes, sorting centres | Routers, switches, cables |
| The postal worker’s route decisions | Routing tables, BGP |
| Registered mail with delivery confirmation | TCP (guaranteed, ordered, retried) |
| Dropping a postcard in a box and hoping | UDP (fast, no guarantees) |
| Looking up the address in a directory | DNS |
| Sealing it in a tamper-proof pouch | TLS |
Notice: the post office doesn’t read your letter. Routers don’t understand HTTP. Each layer only knows about its own envelope. That’s the whole idea of layering, and it’s why you can invent a new application protocol tomorrow without asking anyone to upgrade the internet.
The textbook OSI model has 7 layers. In practice, engineers use 4–5 and it looks like this:
flowchart TB
A["Application — HTTP, gRPC, DNS, SMTP<br/>'GET /users HTTP/1.1'"]
B["Transport — TCP, UDP, QUIC<br/>ports, reliability, ordering"]
C["Network — IP<br/>addressing and routing between networks"]
D["Link — Ethernet, Wi-Fi<br/>getting a frame to the next hop"]
A --> B --> C --> D
D --> E[("The wire / the air")]
Each layer wraps the one above it. Your 20-byte JSON body becomes:
[ Ethernet header ][ IP header ][ TCP header ][ HTTP headers ][ {"id":42} ][ Ethernet trailer ]
14 bytes 20 bytes 20 bytes ~200–800 bytes 20 bytes
📐 Note the overhead: a 20-byte payload can travel inside a 900-byte packet. This is why chatty protocols are expensive, why HTTP/2 compresses headers, and why binary protocols like gRPC win for internal high-volume traffic.
What you need to remember about each layer:
| Layer | Unit | Addresses by | Your design concern |
|---|---|---|---|
| Application | message | URL / service name | API design, payload size |
| Transport (L4) | segment | port | TCP vs UDP; L4 load balancing |
| Network (L3) | packet | IP address | Routing, subnets, region placement |
| Link (L2) | frame | MAC address | Almost never (unless you do networking for a living) |
🚨 “L4 vs L7 load balancer” is a common interview term and it just means which layer it makes decisions at. L4 sees IPs and ports and forwards blindly (fast). L7 parses HTTP and can route on URL path, cookies, or headers (flexible, slower). → Load Balancers
https://api.example.com/usersThe browser checks its own HTTP cache. A valid cached response means zero network. Free is the fastest possible request. → Caching
api.example.com means nothing to the network. It needs 93.184.216.34.
The OS checks its cache, then asks a recursive resolver (your ISP’s, or 8.8.8.8, or 1.1.1.1),
which walks the hierarchy: root servers → .com TLD servers → example.com’s authoritative
nameservers → an answer, cached for the TTL.
📐 A cold DNS lookup costs 20–120 ms. A cached one costs ~0 ms. DNS is also your first, crudest tool for load balancing and failover — and TTLs mean changes propagate slowly, which is why DNS failover is measured in minutes, not seconds. → DNS
Before a byte of HTTP moves, TCP does a three-way handshake:
Client Server
│ ── SYN (seq=x) ──────────────►│ "can we talk?"
│ ◄──── SYN-ACK (seq=y,ack=x+1) │ "yes, can we talk?"
│ ── ACK (ack=y+1) ────────────►│ "yes"
│ │
│ ── HTTP GET /users ──────────►│ finally
That’s one full round trip before any data. Karachi→Virginia: 230 ms spent on saying hello.
This is why connection reuse matters enormously. HTTP keep-alive, connection pools, and HTTP/2 multiplexing all exist to avoid paying this repeatedly. → TCP, UDP, IP
For HTTPS, another handshake on top: agree on a cipher, verify the server’s certificate against a trusted CA, exchange keys.
📐 So a cold HTTPS request to a distant server costs: DNS (1 RTT) + TCP (1 RTT) + TLS (1–2 RTT) + the request itself (1 RTT) = 4–5 round trips before you see a byte. At 230 ms per RTT, that’s over a second of pure protocol overhead.
This arithmetic — not your code — is why CDNs, keep-alive, and HTTP/3 exist. → HTTP, HTTPS, TLS
Your packet doesn’t know the route. It goes to your default gateway (your router), which asks its upstream, and so on. Each router looks at the destination IP, consults a routing table, and forwards it one hop closer. Typically 10–30 hops.
Between networks (ISPs, cloud providers), BGP decides the paths. BGP is based on trust and announcements, which is why BGP misconfigurations occasionally take large chunks of the internet offline for an afternoon.
Practical consequence: you do not control the route, and it changes. Latency between two fixed
points varies over time. Design for variance, not for the average.
Run traceroute google.com to watch this happen.
The packet reaches the destination machine’s NIC. The kernel looks at the port (443), finds the process listening on it, and hands it the data. Your application code finally runs, produces a response, and the whole journey reverses.
sequenceDiagram
participant B as Browser
participant R as DNS Resolver
participant C as CDN / Edge
participant L as Load Balancer
participant S as App Server
participant D as Database
B->>R: api.example.com?
R-->>B: 93.184.216.34 (20–120 ms, or 0 if cached)
B->>C: TCP handshake (1 RTT)
B->>C: TLS handshake (1–2 RTT)
B->>C: GET /users
Note over C: cache miss — forward to origin
C->>L: GET /users
L->>S: GET /users (picks a healthy server)
S->>D: SELECT … (0.5 ms in-datacenter)
D-->>S: rows
S-->>L: 200 OK + JSON
L-->>C: 200 OK
C-->>B: 200 OK (and maybe caches it)
Packets get lost, duplicated, and reordered. TCP hides this from you — but hiding it costs retransmissions and delay. A 1% packet loss rate can cut TCP throughput by an order of magnitude. This is why “the network is fine, ping works” is not a diagnosis.
MTU and fragmentation. The largest frame most networks carry is ~1500 bytes. Bigger messages are split. If a middlebox blocks the ICMP messages that negotiate this, you get the classic bug where small requests work and large ones hang forever.
NAT and private IPs. Your laptop has a private IP (192.168.x.x); a router rewrites it to a
public one. Consequences you’ll actually meet: inbound connections don’t work without port
forwarding, NAT tables expire (idle connections die silently after ~5 minutes — hence TCP
keep-alives), and inside a VPC everything has private IPs.
Firewalls and security groups. In the cloud, a service being “down” is very often just a security group that doesn’t allow the port. Check this before you debug anything clever.
Bandwidth vs latency are independent. A satellite link can have huge bandwidth and 600 ms latency. Bandwidth is the width of the pipe; latency is its length. More bandwidth does not make a small request faster.
| Choice | Gain | Cost |
|---|---|---|
| Keep-alive / connection pooling | Skip handshakes (~1–3 RTT saved per request) | Idle connections hold server resources |
| More round trips (chatty API) | Simpler, more granular endpoints | Latency multiplies, brutally, across regions |
| Fewer, larger responses | Fewer RTTs | Wasted bandwidth for clients that need a subset |
| TLS everywhere | Security, integrity, privacy | CPU cost + handshake latency (mostly solved by TLS 1.3 + resumption) |
| UDP instead of TCP | No handshake, no head-of-line blocking | You must handle loss and ordering yourself |
Run these and read every line of the output:
dig +trace google.com # watch the full DNS hierarchy resolve
traceroute google.com # every router hop, with latency
curl -v https://example.com # see the TLS handshake and HTTP headers
curl -w "@-" -o /dev/null -s https://example.com <<'EOF'
dns: %{time_namelookup}s
tcp: %{time_connect}s
tls: %{time_appconnect}s
firstbyte: %{time_starttransfer}s
total: %{time_total}s
EOF
That last one gives you the exact breakdown of where a request’s time went. Run it against a local service and a distant one and compare. Then run it twice in a row against the same host and watch DNS drop to zero.