Full Mock Walkthrough
One problem, start to finish
Seven lessons of method. Now watch it run.
This is a complete forty-five minutes on one problem, minute by minute — what gets said, what goes on the board, where the forks are, and what a strong answer sounds like at each one. It is deliberately not a summary of the method; it's the method happening.
The problem is "design a URL shortener" — and we're using it on purpose, for two reasons. It's the classic deceptively-simple prompt, so if the method produces real depth here it will produce it anywhere. And you've already done its requirements phase: this is the same problem you scoped in Requirements & NFRs That Matter, so we pick up with the commitments you already made rather than starting cold.
Read it once end to end. Then run it yourself in the exercise partway down — because there's a fork in the middle, and the choice you make there decides which hard question you spend your deep dive on.
The whole run at a glance
Before the detail, the shape of the forty-five minutes — so you can see where each phase went and what it produced.

Two things to notice before we walk it.
The board went up at minute twenty, exactly on the checkpoint — not because anyone was watching a clock, but because the budget from The Delivery Framework was followed and it lands there naturally.
And the deep dive is the widest block, which is the whole argument of that lesson made concrete: sixteen minutes on two components, versus thirteen on the entire high-level sketch.
Minutes 0–7 · Requirements
"Design a URL shortener."
Four words that sound finished. The clock is running.
What gets asked — not everything, just the few that carry a decision:
- "Is the core just create-and-redirect, or also analytics, expiry, custom aliases?"
- "What's the read-to-write ratio?"
- "What latency should the redirect feel like, and how available does it need to be?"
- "When a link is created, must it resolve everywhere instantly?"
What comes back: core is create and redirect; analytics is a bonus. One hundred reads for every write. Redirects under 100 ms at p99. 99.99% availability — a dead link is a broken product. Eventual consistency for a brand-new link is fine.
Then the commit — one sentence, and it's the most important sentence of the phase:
"So: create and redirect are the core, a hundred to one reads, sub-100-millisecond redirects, four nines, and eventual consistency is acceptable. Analytics is out of scope. I'll design to that — sound right?"
Seven minutes, and everything after this points back at it.
Minutes 7–11 · Estimation
"Roughly what scale are we talking about?"
Four minutes, and only the numbers that change a decision — watch what each one buys:
Writes. A hundred million new links a month is about 38 writes a second. That is nothing. A single primary store handles it without breaking a sweat.
⭐ That number's entire value was giving permission not to build write sharding. An estimate that stops you building something is worth more than one that justifies building something.
Reads. About 10,000 a second at peak. Two orders of magnitude above the writes — this is the number that earns a cache tier, and now the cache has a reason on the board next to it.
Key space. We need maybe ten billion codes over a few years. Codes are base62 — the 62 characters you can safely put in a URL, a–z, A–Z, 0–9 — so six of them give 62⁶ ≈ 56 billion combinations. Plenty. One line of arithmetic that fixes a real design parameter.
And that's it. No bandwidth calculation, no server count, no storage-growth curve — because none of them would have changed anything. (Storage, if asked: ~500 bytes a row, so roughly 600 GB a year. Comfortably one machine. Also not a reason to shard.)
Minutes 11–24 · High-level design
Now the board, and the chain from The Design Template — nouns, then verbs, then boxes.
The nouns. Almost embarrassingly few: one entity, Link, holding the code, the long URL, and when it was created. That's the domain.
The verbs.
POST /links {long_url} → {short_url}
GET /{code} → 302 Location: <long_url>
⭐ 302, not 301 — and this is a genuinely senior detail that costs one sentence. A 301 says permanent, so browsers cache it aggressively: you could never revoke or repoint that link, and you'd never see the click again. A 302 keeps the redirect coming through you. Notice it also quietly preserves the analytics feature you scoped out — which is what "reversible decision" looks like in practice.
The boxes, built one endpoint at a time: client → edge → gateway → then a deliberate split into a read service and a write service → cache → primary store.
The split is the one structural choice here, and it's justified by the number from four minutes ago: at a hundred to one, the two paths scale on completely different axes. Splitting them lets you scale reads hard and leave writes alone.
Minute twenty. End-to-end design on the board — the checkpoint from The Delivery Framework, hit without watching a clock.
Then the last call of this phase, and it's the one that shapes everything after it.
The fork: how do you make a short code?
This is the decision the whole problem turns on, and it's worth slowing down for — because the three answers don't just differ in quality. They hand you different interviews.

Hashing the URL is deterministic and it works. But hashes collide, so before every insert you have to check whether that code is already taken — which puts a database round-trip on the write path and needs retry logic. Random strings have the same cost and don't even give you determinism.
A counter, base62-encoded, makes uniqueness structural: the next number has never been used, so there is no check, no retry, and nothing on the write path. You've traded a correctness problem for a coordination problem — and coordination is the easier one to solve cleanly.
Whichever you pick, the interviewer's next question is already determined:
- Counter → "where does that counter live? You have many write servers."
- Hash or random → "two URLs produce the same code. What happens?"
Neither branch is a trap. But notice what just happened: a decision at minute sixteen chose the hard problem you'll spend minutes twenty to thirty on. That's the thing you can't feel from reading a finished solution, and it's what the exercise below lets you feel.
Minutes 24–40 · Deep dive
The biggest block, on the two components that carry the risk. Everything else stays named and justified — this is the pick two and go further from The Questions Everyone Asks.
The counter, and where it lives
"You've got a dozen write servers. Where does the counter live?"
The naive answer is one Redis instance with INCR. It's actually safe — Redis is single-threaded, so the increment is atomic and there's no race. But it's a network round trip on every single write, and one box on the critical path for all creation.
The answer that reads as experience:
"Each write server takes a block of a thousand counter values with one atomic
INCRBY, then hands them out locally until it runs out. Redis sees one call per thousand writes instead of one per write."
And then the sentence that pre-empts the obvious follow-up before it's asked:
⭐ "If a server dies holding a block, we lose those codes — and that's fine. We need uniqueness, not continuity."
That one clause is the difference between someone who read about this and someone who has run it. It also opens the multi-region answer for free: give each region a disjoint range and cross-region coordination disappears entirely.
One more, unprompted, because naming your own weakness is a senior move: sequential codes are enumerable — anyone can walk your entire link space. The fix is to scramble the counter before encoding it, not to abandon the counter.
The read path, and the link that goes viral
"One link gets a billion clicks in an hour. What breaks?"
The cache is doing the work already — memory is roughly a thousand times faster than SSD, which is how a sub-100 ms p99 is met at all. And because a small minority of links carry most of the traffic, the working set is tiny and the hit rate is very high.
But a billion clicks on one link is a different problem: every request hashes to the same cache key, so one node takes all of it. Adding nodes does nothing.
The answer uses a property of the data: a code-to-URL mapping is immutable. So you can replicate that key across every cache node without any consistency worry — and better, push it to the CDN edge, where the redirect is served near the user and never reaches your infrastructure at all.
Cache invalidation, notably, is not a problem here — and saying so is worth ten seconds. It's only interesting when links expire or change, and we scoped both out.
Minutes 40–45 · Wrap-up
Five minutes to step back. Not to add anything — to make the last forty minutes countable.
"To summarise: we optimised for read latency first, and we paid for it with eventual consistency on newly-created links — which the requirements said was acceptable. The piece I'd harden next is the counter service: today a Redis failure stalls creation, though redirects keep serving. And if analytics came back into scope, it rides an async click stream off the redirect path — which is why the 302 mattered."
Four sentences. Priorities, the price paid, the honest weak point, and a decision from minute twelve paying off at minute forty-four.
Notice what wasn't done: no new boxes, no rate limiter bolted on at the end, no listing of things there wasn't time for. The wrap-up compresses; it doesn't grow.
Now run it yourself
Reading a good run is useful. Making the calls yourself is where it sticks.
Same prompt, same forty-five minutes. At each phase you choose what to say, and the model answer appears after you commit — so it teaches rather than tests. The clock is real, and the minute-twenty checkpoint is marked.
Then take the fork the other way. Choose hashing instead of the counter and the deep dive changes underneath you — you'll be defending collision handling instead of counter coordination. Same problem, same clock, a different hard question. That's the thing worth feeling twice.

What the board looked like at the end
Here's the whole thing as it would actually stand at minute forty-five.

It's small. One entity. Two endpoints. Seven boxes. Two things opened up.
Every arrow on it points from caller to callee and every one is solid — there isn't a single async edge, which is the conventions from Whiteboard & Diagram Craft being applied honestly rather than decoratively. Nothing here is queued because nothing here needed to be.
That's worth sitting with, because the instinct under pressure is that more boxes means a better answer. This board would beat a diagram with three times as much on it, for one reason: every element on it traces back to something that was agreed at minute seven. The cache is there because of 100:1. The read/write split is there because of 100:1. Six characters is there because of 62⁶. The 302 is there because analytics might come back.
Nothing on this board is decoration.
What actually made that run work
Read back over it and none of the individual facts were exotic. There was no clever trick and nothing you'd need to have memorised. What carried it was the method, doing exactly what the last seven lessons said it would:
- The scope was committed out loud at minute seven, so everything afterwards had something to point back at.
- Nothing was computed that didn't change a decision — and the very first number justified building nothing at all.
- The design went up end-to-end by minute twenty, so there was something to deepen.
- Two components got sixteen minutes; everything else stayed named and justified.
- Every box carried a because, so no challenge required improvisation.
- Nothing happened silently — every choice was narrated as it was made, which is the habit from Communicating While Designing doing its job under pressure.
- The weak point was volunteered rather than discovered.
That last one is worth naming on its own. At no point did the candidate claim the design was finished or perfect. They said what it traded away, where it would break, and what they'd do next — and that reads as far stronger than a design presented as flawless.
You now have the method and you've watched it run. The last thing left in this part of the course is to check that it's actually yours — a fresh prompt, and your own clock. That's the Checkpoint, next.