InfoWok

URL Shortener Follow-Ups: Failures, API Detail and Cost (2026)

The production half of the URL shortener problem: clarifying questions, an API that is safe to retry, a failure table with arithmetic, consistency, alerts, cost, key rotation and the deep dives interviewers probe.

SK
Sukhveer Kaur
Published October 24, 2026
9 min read
Open in ChatGPT Open in Claude
URL shortener follow-ups, system design problem 1 companion: a client reaches an edge and a service, with failure, API and cost probes branching off, beside the line what breaks, what it costsSoftware Architecture
URL SHORTENER FOLLOW-UPS
On this page +

The core design is done and the diagram looks right. Then the interviewer asks what happens when the cache restarts at peak, how a retried POST avoids making two links, and what the design costs per month. None of this changes the boxes. All of it decides whether the design survives a bad Tuesday.

This is the second half of Design a URL shortener, which builds the design itself. It holds the questions that fill the last fifteen minutes of the round.

Intermediate35 min
Before you start
  • Design a URL shortener: number blocks, scrambled base62 codes, cache-aside reads and the 302 decision.
  • Caching strategies and the CAP theorem help with the failure and consistency sections.
Remember from the core lesson

Why does the design return 302 instead of 301, and why is the number scrambled before it is encoded? A 302 keeps every click, expiry and takedown under the service’s control. The scramble stops anyone walking the codes in order.

Key takeaways
  • State assumptions before drawing. An unstated assumption looks like a missing requirement.
  • Make creates safe to retry with an idempotency key, answer a taken alias with 409, and page through links with a cursor.
  • For each part, say what breaks, by how much, and what saves you. Arithmetic beats adjectives.
  • Redirects choose availability. Alias creation chooses consistency. Say both, and why.
  • Alert on the error budget, not on single errors, and watch the cache hit rate because it is the database’s real load.
  • Count clicks off the redirect path. Rotate the scramble key by versioning number ranges.
After this lesson you can

Extend a working URL shortener for production: ask the clarifying questions, design a retry-safe API and data model, tabulate failures with numbers, state the consistency stance, set alerts and a cost estimate, rotate the scramble key safely, and answer the usual deep dives.

Ask Before You Draw#

Spend the first two minutes on questions. Where the interviewer shrugs, state an assumption out loud and move on.

QuestionAssumption if no answer
Anonymous creates, or accounts?Accounts, so quotas and listing work
Custom aliases?Yes, with a minimum length
Default expiry?None, but a user can set one
Can a link be edited?No, create and disable only
Clicks counted?Yes, daily totals, a few minutes late
How many regions?One first, a second as a follow-up
Is abuse in scope?Yes, at creation and by report

These are judgement calls, not facts. The point is that each one changes a later decision: accounts enable rate limits per user, and “no editing” is what makes 302 caching safe.

API Detail That Interviewers Probe#

The core lesson had two endpoints. A production contract needs more.

POST /links is not idempotent by definition: RFC 9110 section 9.2.2 lists only safe methods, PUT and DELETE as idempotent. A request that times out and is sent again can create two links. Two fixes:

  • Idempotency key. The client sends an Idempotency-Key header, and the server stores the first response under (owner_id, key). Stripe may prune keys once they are 24 hours old and compares the parameters of a retry with the original. The header is still an Internet-Draft, not an RFC.
  • Deterministic alias. Derive the code from the owner and the normalised long URL, so the same request always lands on the same row.
CaseStatusNote
Created201Body has code and short_url
Retry with same key201 or 200Stored first response
Alias taken409Conflict with current state
Malformed URL400 or 422Validate before storing
Not logged in401Blocked destination: 403
Over quota429Add Retry-After (RFC 6585)

For GET /links?limit=20&cursor=..., use a cursor over (owner_id, created_at, code), not an offset. Assume 20 links a page: page 500 makes an offset query skip 10,000 rows. A cursor seeks straight to the next row. Limit creates with a rate limiter.

Data Model and Indexes#

Assume a 500-byte row, as in the core lesson.

text
links(code PK, long_url, host,
      owner_id, created_at,
      expires_at, status)
idx_owner(owner_id,
  created_at DESC, code)
idx_host(host)    -- takedown
idx_expiry(expires_at)
idem(owner_id, key PK,
  response, created_at)
  • code serves the redirect. idx_owner serves the listing cursor.
  • host disables every link to one bad domain in one statement.
  • idx_expiry lets cleanup skip a table scan.

Do not index long_url: nothing queries by it, and each index adds write cost.

What Breaks, By How Much#

Every number here is an assumption carried over from the core lesson: 120 writes a second and 12,000 reads a second at peak, a 90 percent cache hit rate, and replicas that serve about 2,000 reads a second each.

Predict first

The cache cluster restarts at peak. How much load reaches the database, and how does it compare with three replicas? Answer, then read the first table row.

PartWhat happensArithmeticWhat saves you
Cache downEvery read misses12,000 against 3 x 2,000 = 6,000 a second: twice capacity, against 1,200 a second normallyCoalescing, local cache, load shedding
Counter store downNo new blocksRunway = block size / per-instance rateBigger blocks, alert on blocks left
Primary downWrites fail until failoverAssume 30 s: 30 x 120 = 3,600 failed creates. Assume 1 s of lag: up to 120 links lostReplica reads, retries, semi-sync
One replica downCapacity falls2 x 2,000 = 4,000, still above the 1,200 normalSpare replica

Cache down is the stampede#

Assume one hot code gets 2,000 reads a second and a database fetch takes 5 ms. That puts 2,000 x 0.005 ≈ 10 fetches in flight for that one key. With request coalescing it is one. Also keep a short local cache in each server and cap concurrent database reads, so overload becomes a few slow answers instead of an outage.

Counter down is a sizing question#

Assume 4 instances share 120 writes a second, so 30 each.

  • Assume a block of 1,000: it lasts ≈ 33 seconds at peak.
  • Assume a block of 10,000: it lasts ≈ 333 seconds, or 5.5 minutes.
  • Assume a crash wastes at most one block: 10,000 of 3.52 trillion codes.

Alert when an instance holds under half a block.

It loses data if replication is asynchronous. Sentinel’s documentation says acknowledged writes are not guaranteed to survive a failover. For links, that means a user holds a code that later returns 404. Because writes are only 120 a second, a semi-synchronous write path is affordable. State the RPO (about zero for links) and RTO (about 30 seconds, an assumption) you chose.

Failed-over counters repeat numbers

A failed-over counter can reissue numbers it already handed out. The UNIQUE constraint on code rejects the duplicate and the service takes a fresh number.

Consistency: Two Answers, Not One#

The honest stance is split by operation. In CAP terms:

OperationChoiceWhy
Redirect readAvailable, may be staleA broken link breaks pages. Staleness is bounded by the cache TTL
Alias createConsistentA conditional insert on the primary. During a partition it fails instead of giving one alias to two users
TakedownFast, not instantStale for at most the shorter of TTL and edge max-age

In PACELC terms, with no partition, reads trade consistency for latency (replicas and caches), while alias creation pays latency for consistency. This is design judgement, so say it as a choice you made, not a law.

Watch the Right Numbers#

Pick a few signals and give each a threshold.

  • p99 redirect latency at the read service, against the 100 ms target assumed in the core lesson.
  • Cache hit rate. The database load is (1 - hit rate) x reads. Assume 90 percent: it is 1,200 a second. At 80 percent it doubles to ≈ 2,400. Alert at 85 percent (an assumption), where the load is 1,800, well inside the 6,000 limit.
  • Assumed redirect availability SLO of 99.9 percent. Assume 4,000 reads a second over 2.5 million seconds: ≈ 10 billion redirects a month, so the budget is 10 million failures, or 43.2 minutes of full outage in a 30-day month.
  • Replication lag, blocks left per instance, abuse reports waiting.

Page on budget burn, not on single errors. The Google SRE Workbook recommends pairing a long and a short window: for a 99.9 percent SLO, a burn rate of 14.4 over one hour (confirmed over five minutes) pages, because it consumes 2 percent of the budget. Treat its numbers as a starting point to tune.

What It Costs#

Prices change, so these are dated and the rest are labelled assumptions. Assume the ≈ 10 billion redirects a month from above.

ItemArithmeticResult
Edge requestsCloudflare Workers paid plan, checked 2026-10-24: $5 a month, 10 million included, $0.30 per extra million. (10,000 - 10) x $0.30 + $5about $3,000 a month, request charge only
EgressAssume 400 bytes per 302 and $0.09 per GB. 10 billion x 400 B = 4 TBabout $360 a month
Cache memoryAssume the hottest 1 percent of 6 billion links, 600 bytes each: 60 million x 600 Babout 36 GB

The lesson is the shape, not the totals. Edge serving bills every redirect, so use it for the hot codes and let origin caches take the rest. Cloudflare’s page also bills CPU time separately, so read it before quoting a total.

Deep Dive: Counting Clicks at Scale#

Assume 4,000 clicks a second and 50 bytes per event: 200 KB a second, about 17 GB a day. Writing a database row per click would put a write on the hot path.

  • Emit and move on. If the log is slow, drop the event. A redirect never waits for analytics.
  • Aggregate before writing. Assume a link with 2,000 clicks a second: it becomes one write every 10 seconds per code.
  • Expect duplicates. Logs deliver at least once, so dedupe on an event ID where counts feed billing. Charts can tolerate small error.

See event-driven architecture and Kafka vs RabbitMQ for the log choice.

Deep Dive: Abuse and Takedown at Scale#

At creation, check the destination against a reputation service such as Google Web Risk, which says its lists can miss risky sites and flag safe ones. So add layers:

  1. Rate limit and quota per user and per address.
  2. Re-scan popular links on a schedule, hottest first, because a page can turn bad later.
  3. Take down by host. idx_host disables every link to one domain in one statement.
  4. Purge every layer: mark the row disabled, delete the cache entry, purge the edge.

The worst-case exposure after a takedown is the cache TTL or the edge max-age, whichever is shorter. Assume 60 seconds for both and say so. A 301 would make this unbounded, which is why the core lesson chose 302.

Rotating the Scramble Key#

The key is a secret, so sooner or later someone asks how to change it. Existing links survive, because a redirect looks up the stored code and never reverses the scramble.

The one place that does reverse it is the cross-region trick from the core lesson, which decodes a code to a number to find its home region. That is what depends on the key.

Rotation protects codes made after the change. It cannot un-leak codes already issued under a leaked key. Moving from 6 to 7 characters works the same way: codes are strings, so old ones stay valid.

Practice: Size the Block#

Exercise · Survive a ten-minute counter outage10 min

Assume 4 instances, 120 writes a second at peak, and a counter store that can be down for 10 minutes. Work out:

  1. The block size each instance needs.
  2. The most codes wasted if all four crash.
  3. One alert that gives warning.
Hint

Per-instance rate is 120 / 4. Runway is block size divided by that rate.

One reasonable answer
text
rate     = 120 / 4 = 30 per second
10 min   = 600 s
block    >= 30 x 600 = 18,000
choose   = 20,000 (a round number)
wasted   <= 4 x 20,000 = 80,000
share    = 80,000 / 3.52e12 ~ 2e-8

A block of 20,000 covers the outage with some margin, and a crash of every instance wastes about two billionths of the 7-character space. Alert when any instance holds under 10,000 numbers, which at 30 a second is about five minutes of warning. The trade-off is that bigger blocks waste more on a crash, which costs almost nothing here.

The 45-Minute Plan#

MinutesDo
0 to 5Questions, assumptions, requirements, out of scope
5 to 12Entities, API with idempotency and errors, estimate
12 to 20Simple design, then the read path and cache
20 to 32Deep dives: codes, redirect status, one failure table
32 to 40Consistency stance, abuse and takedown, regions
40 to 45Observability, cost, recap, follow-ups

This is a suggestion. Cut cost before cutting the failure table.

What Each Level Should Show#

LevelExpected in the follow-up round
Mid-levelNames the cache stampede and the 409 for a taken alias when prompted
SeniorBrings the failure table and idempotent creates unprompted, with numbers
Staff+Splits consistency by operation, rotates the key safely, sets SLO burn alerts, prices the edge
In an interview

Expect one-line follow-ups. Answer with a trade-off, not a tool name:

They askAnswer in one line
Client retries a create?Idempotency key, stored response
Two users want one alias?Conditional insert, loser gets 409
Cache restarts?Coalesce, local cache, cap database reads
Counter store dies?Block size sets the runway
Primary dies?Promote a replica, state RPO and RTO
A link goes viral?Replicate the hot entry, edge absorbs it
Count clicks?Log, aggregate, write per window
Phishing link reported?Disable, purge every layer, ban the host
Leaked scramble key?Rotate by range version, new codes only
How do you know it works?Latency, hit rate, SLO burn

Check Yourself#

Check yourself4 questions · answer, then open
1.Why is a database row per click a bad idea on the redirect path, and what replaces it?

It adds a write to the hottest path and a slow database can slow every redirect. Emit an event to a log, aggregate per code per window, and write the totals, so the redirect never waits.

2.The hit rate falls from 90 to 80 percent. What happens to database load?

It doubles from 1,200 to 2,400 reads a second at peak, because load is (1 - hit rate) times reads. That is why the hit rate gets an alert before the database does.

3.Why can the scramble key be rotated without breaking old links?

Redirects look up the stored code and never reverse the scramble. Only cross-region routing decodes a code, so it tries the new key, then the old, then asks other regions.

4.Why does alias creation choose consistency while redirects choose availability?

A stale redirect costs one slightly old answer, bounded by the TTL. Two users holding the same alias is a correctness bug, so the conditional insert fails during a partition rather than risk it.

Learning tip

Cover the table in the failure section and rebuild it from memory with the numbers. Do it again tomorrow. The arithmetic, not the part names, is what interviewers listen for.

In One Picture#

text
Ask first
  accounts? aliases? regions?
API
  idempotency key, 409, 429,
  cursor paging
Failures
  cache x2 over capacity;
  block size = runway; RPO/RTO
Consistency
  reads available,
  alias consistent
Watch
  p99, hit rate, SLO burn
Cost
  edge bills every request
Rotate
  version the number ranges

Conclusion#

The follow-up answer is everything around the read-path machine: a create that is safe to retry, a failure table with numbers, a split consistency stance, budget-based alerts, an honest cost line and a way to change the key. State the assumption each time and the answers stay short.

Which follow-up would catch you out? Tell us in the comments.

Where to go from here

Frequently asked questions

How do you make "create short link" safe to retry? +
Let the client send an idempotency key, and store the first response under it for a day or so. A retry with the same key returns that response instead of creating a second link. A second option is a deterministic alias derived from the user and the long URL, so the same request always maps to the same code.
Which status code should a taken custom alias return? +
409 Conflict. The request is well formed, but it clashes with the current state of the resource. Return 400 or 422 for a malformed URL, 401 or 403 for authentication and policy failures, and 429 with Retry-After when the caller is rate limited.
What happens to a URL shortener when its cache goes down? +
Every redirect misses and reaches the database at once, which can exceed what the replicas serve. Coalesce concurrent misses for the same key, keep a few seconds of local cache in each server, and cap concurrent database reads so the database sheds load instead of collapsing.
Is a URL shortener consistent or available? +
Both, for different operations. Redirects favour availability and low latency, and may serve a slightly stale link. Creating a custom alias favours consistency, so it uses a conditional insert on the primary and fails during a partition rather than hand the same alias to two users.
Can you rotate the key that scrambles short codes? +
Yes, without breaking old links, because reads look up the stored code and never reverse the scramble. Tag each number range with its key version, issue new blocks under the new key, and keep the old key until no live range needs it.

References

  1. RFC 9110, HTTP Semantics, section 15.5.10 (409 Conflict)
  2. RFC 9110, HTTP Semantics, section 9.2.2 (idempotent methods)
  3. RFC 6585, Additional HTTP Status Codes, section 4 (429 Too Many Requests)
  4. IETF Internet-Draft, The Idempotency-Key HTTP Header Field (revision 08, October 22, 2026)
  5. Stripe API reference, Idempotent requests
  6. Redis documentation, High availability with Redis Sentinel
  7. Google SRE Workbook, Alerting on SLOs
  8. Cloudflare Workers documentation, Pricing (updated October 2, 2026)
  9. Google Cloud, Web Risk overview (updated October 22, 2026)
Written by
Sukhveer Kaur
Sukhveer KaurSoftware Developer & AI Engineer

Sukhveer is a software developer specialising in AI systems and backend engineering. She has hands-on experience designing agentic AI applications, working with large language model pipelines, autonomous agent frameworks, and cloud-native services in Java and Python. At InfoWok, she bridges the gap between cutting-edge AI research and practical implementation — helping developers understand and apply emerging technologies through clear, experience-backed writing.

New AI engineering guides, the day they ship

Real Python, production depth. No digest spam.

Comments