Free preview

Interview Walkthrough: Design DNS

"Design a system that maps domain names to IP addresses for the entire internet."

This is an unusually good interview problem because the functional requirement is a single lookup, and everything hard about it is scale, availability, and delegation. It is also a design whose real answer you already know — which makes it a test of whether you can justify it rather than recite it.

Key takeaway

The spine: Scope → FR → NFR → State → API → HLD → Deep Dives. The decision that generates this entire design is one ratio: reads outnumber writes by an enormous margin, and staleness is nearly harmless. Everything else follows.

Step 0 — Scope it before you design

  • Are we designing the global system, or a company's internal naming service? The scale differs by many orders of magnitude.
  • Do we own the whole namespace, or must independent organizations manage their own records?
  • What query volume should I assume?
  • How fresh must a record be after an update — seconds, or is minutes acceptable?
  • Do we need to support arbitrary record types, or just name-to-address?
  • Is this authoritative serving, recursive resolution, or both?

Then commit:

"I'll design the global system: a shared namespace where independent organizations control their own records, serving name-to-address lookups plus delegation and mail records. I'll assume updates can take minutes to propagate. Let me size it before I design anything."

Step 1 — A quick BOTEC

Using the method from the Foundations module:

Internet users                      = 5 billion
Distinct domains resolved/user/day  = 100        (most page assets are cached)

Uncached-ish queries/day = 5B * 100        = 500 billion
Queries/second           = 500B / 100,000  = ~5 million QPS globally

Step 2 — Functional Requirements

  • Resolve a domain name to an IP address.
  • Support multiple record types — address records, delegation, aliases, mail routing.
  • Allow organizations to manage their own records without central coordination.
  • Delegate authority for a subtree of the namespace to another operator.

Say the quiet part: "The functional surface is essentially one read operation. Everything interesting here is non-functional — and the delegation requirement is what forces the architecture."

Step 3 — Non-Functional Requirements — the heart of the problem

NFRTargetThe decision it forces
AvailabilityEffectively 100%No single point of failure anywhere; must answer even when parts are down
LatencyLow single-digit ms typicalAnswer from near the user — caching and geographic distribution
ScalabilityMillions of QPS, hundreds of millions of namesNo node can hold the whole dataset — partition by namespace
ConsistencyEventual is fineCache aggressively; bound staleness with a TTL
AutonomyOrgs manage their own recordsDelegation — the defining structural requirement
SecurityAnswers should be trustworthyResponse validation; encrypted transport for privacy

Step 4 — Core Entities & Key State

Split the planes, because they have completely different characteristics:

PlaneStateAccess patternConsistency
Data planeResource records being served and cachedMillions of reads/sec, globallyEventual, TTL-bounded
Control planeZone ownership, delegation, record editsThousands of writes/sec at mostStrong — ownership must never be ambiguous

The key entity:

Resource Record
  name      -- educative.io
  type      -- A | NS | CNAME | MX | ...
  value     -- 104.18.2.119
  ttl       -- 300 (seconds a resolver may cache it)

Zone
  suffix            -- the slice of namespace owned
  authoritative_ns  -- servers responsible for it
  delegations       -- NS records handing subtrees to others

The zone is the unit of ownership and the unit of partitioning at once — which is the elegant part of the design and worth pointing at explicitly.

Step 5 — API & Interfaces

Two surfaces, and confusing them is the classic mistake:

Query path (the data plane) — not REST. A compact binary request/response over UDP port 53, falling back to TCP for responses over 512 bytes or zone transfers:

QUERY  (name, type)  ->  (records[], ttl, authoritative_flag)

Management path (the control plane) — a normal authenticated API, used rarely:

PUT    /zones/{zone}/records    (name, type, value, ttl)
DELETE /zones/{zone}/records/{id}
POST   /zones/{zone}/delegate   (subdomain, nameservers)

Step 6 — High-Level Design

(a) The naive core

Say what breaks, in order: it cannot hold the whole namespace, it cannot serve millions of QPS, it is a global single point of failure, and — decisively — every organization would have to ask us to change their own records. That last one is not a scaling problem; it is an organizational impossibility.

(b) Delegate: introduce the hierarchy

Each tier holds a small slice and points to the next. This solves autonomy and scale with one mechanism: organizations run their own authoritative servers, and no node needs the full dataset.

"Delegation is the core insight. It's not primarily a scaling trick — it's what lets millions of independent organizations operate one shared namespace without a central authority in the write path."

(c) Cache aggressively

With a 1,000:1 read/write ratio and staleness being cheap, caching is the highest-leverage move available. Most queries never leave the user's machine, and the overwhelming majority never reach an authoritative server. TTL bounds how wrong a cached answer can be.

(d) Replicate and anycast

Each logical server address is served by many physical instances sharing one IP, with network routing sending each query to the nearest. This delivers proximity, capacity, and automatic failover from a single mechanism — and it is why "13 root servers" describes addresses rather than machines.

(e) The mature design

Hierarchy for delegation and partitioning · multi-layer caching with TTL · anycast replication per tier · UDP with retransmit for stateless resilience · eventual consistency accepted deliberately.

Deep Dives & Follow-up Questions

"Why UDP for something this important?"

Because a lookup is a single small idempotent request, and TCP's handshake would triple the latency for no benefit. There's no connection state to recover, so a lost query is just re-sent — often to a different server, which turns packet loss into automatic failover. We do fall back to TCP when a response exceeds 512 bytes or for zone transfers, where reliable ordered delivery actually matters. Modern clients may also use DoH or DoT, but those trade latency for privacy and are a client-side choice, not a change to the core protocol.

"An organization changes its IP. How long until everyone sees it?"

Bounded below by TTL and unbounded above. Resolvers holding the old record serve it until expiry; some clamp short TTLs, and long-lived processes may hold an address until restart. The playbook is: lower the TTL well in advance, wait out the old TTL before making the change, dual-run both addresses during the drain, then raise the TTL back. Better still, don't change the address — CNAME to a load balancer and swap backends behind a stable name, so propagation isn't in the critical path at all.

"How do you handle a massively popular domain?"

It largely handles itself, and that's the point of the caching design — the more popular a name, the more likely it's already cached at every layer, so popularity reduces load on the authoritative servers rather than increasing it. Beyond that I'd anycast the authoritative servers so load spreads geographically, and keep the record small so responses fit in a single UDP datagram. The pathological case isn't a popular domain; it's a flood of queries for names that don't exist, which defeats caching entirely — that's what negative caching is for.

"Someone floods you with queries for random subdomains."

That's a deliberate cache-busting attack — every query misses and reaches the authoritative server. Defenses: negative caching so repeated bogus names are absorbed, rate limiting per source, and response rate limiting at the authoritative tier. I'd also want anycast so the flood is absorbed by the instance nearest the attacker rather than concentrating globally.

"How do I know the answer I got is genuine?"

With classic DNS you don't — responses are unauthenticated and unencrypted, which makes cache poisoning possible: an attacker races a forged response to a resolver, and once accepted it's served to every user behind that resolver until the TTL expires. The mitigation is DNSSEC, which cryptographically signs records so resolvers can verify a chain of trust from the root. Note it provides authenticity, not confidentiality — queries are still visible on the wire, which is what DoH and DoT address separately.

"Why 13 root servers? Why not 50?"

The number comes from a protocol constraint rather than a capacity one: the root server list had to fit in a single 512-byte UDP response. It isn't a capacity limit because each of the 13 is an anycast address backed by many physical instances — capacity is added by deploying more instances, not more addresses. It's a nice example of an old protocol detail shaping a design that has scaled far beyond its original assumptions.

"What if a whole TLD's servers go down?"

Every domain under that TLD becomes unresolvable for anyone whose cache has expired — which is why TLD operators run heavily replicated anycast fleets. Caching is the saving grace: resolvers holding cached authoritative-server addresses can skip the TLD tier entirely and still resolve. It's a good illustration of caching functioning as an availability mechanism rather than merely a performance one.

Now do it live

The next section drills these as standalone probes.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue