See It Flow
System Design · Interview Guide · ~14 min read

URL Shortener System Design Interview Questions & Follow-Ups

How to reason through a URL shortener design in an interview — a clear opening sequence, the signals that separate strong candidates from memorized answers, a full question bank across every major area, and a 30-second recall card.

Mangesh MandlikLast updated:
Section 01

Interview Structure

A URL shortener interview rewards a clear reasoning sequence over a memorized architecture. The goal is to show you can scope, estimate, design, and reason about failures — not recite a fixed diagram.

1. Clarify product semantics (dedup? mutable destinations? custom aliases? expiry? analytics?). 2. Establish workload assumptions explicitly, rather than assuming fixed numbers. 3. Estimate mapping volume and redirect/write rates from those assumptions. 4. Start with a simple app + database design. 5. Identify the repeated-read pressure this creates. 6. Introduce a cache to address it directly. 7. Choose a short-code generation strategy based on the actual requirements (not a default). 8. Separate analytics from the redirect path. 9. Reason about failure modes and hot keys. 10. Evolve the architecture only when a genuine scale/SLO signal requires it.

The prompt itself may be phrased as "design TinyURL" or "design Bitly" rather than the generic "design a URL shortener" — these are the same problem with the same reasoning sequence above; don't let the brand name in the prompt change the fundamentals you reach for.

Editorial note: this guidance is the author's synthesis of common system-design interview practice, not any specific company's rubric or interviewer feedback.

Section 02

What Strong Candidates Do

This interview is used to separate candidates who memorize architectures from candidates who reason about systems under stated assumptions.

Strong signals

✅ State the read/write ratio and traffic-skew assumption explicitly before designing around it — naming it as an assumption, not a fixed universal fact. ✅ Calculate storage and explain why it usually isn't the bottleneck for this workload — while noting that this depends on the assumed mapping volume and retention policy. ✅ Describe precisely when and why the naive design breaks — 'hot rows get re-read on every redirect' is stronger than 'the DB can't scale.' ✅ Name multiple ID-generation approaches and justify a choice based on stated requirements, rather than defaulting to one. ✅ Raise the redirect-status trade-off (301 vs 302 vs other signals) unprompted — it signals product+infrastructure thinking. ✅ Trace the cache-hit path, cache-miss path, and write path separately. ✅ Reason about which component fails first under a specific stated failure scenario, rather than reciting a fixed failure order. ✅ Describe evolution stages by the signal that triggers each transition, not a memorized traffic number.

Common mistakes

❌ Jumping straight to a distributed architecture without first presenting the naive design and its actual breaking point. ❌ Recommending a database technology without justifying it against the workload's actual requirements. ❌ Treating analytics as an afterthought, then bolting it on synchronously. ❌ Reducing redirect-caching trade-offs to 'one status code caches, the other doesn't' — real HTTP caching behavior also depends on cache-control headers and client behavior. ❌ Asserting that one specific component 'always' fails first in a viral spike, instead of reasoning from the stated scenario.

Section 03

Question Bank: Likely Questions & Follow-Ups

A structured bank of the questions most likely to come up, organized by the area they probe. Each includes a strong-answer direction, likely follow-ups, and links into the deeper technical pages.

Scope & Requirements

Should the same long URL always produce the same short code?

Strong answer angle

State it as a product decision, not a default. Deterministic dedup (same input -> same code) is simpler to reason about and avoids redundant rows, but breaks if different owners/campaigns should get independent codes for the same URL. See Trade-offs -> Deterministic Deduplication vs Multiple Codes per URL.

Likely follow-ups
  • What data would you hash to define "the same URL" for dedup purposes? — Tests whether the candidate considers query-string/fragment normalization, not just raw string equality.
  • What if a user wants two separate short codes for the same destination for two different campaigns? — Tests whether the candidate recognizes dedup is a per-owner or per-context decision, not global.

Can the destination URL change after creation?

Strong answer angle

If yes, this is a small but consequential requirement: cache invalidation on update becomes mandatory, not optional, and analytics attribution must handle a mapping's destination changing mid-life.

Likely follow-ups
  • How would you propagate an update to a cached mapping across multiple cache nodes? — Tests whether the candidate reaches for direct invalidation rather than relying on TTL expiry alone.

Are custom aliases required, and do they interact with the generated-code namespace?

Strong answer angle

If yes, custom aliases and generated codes share one namespace and one uniqueness constraint, so schema length must accommodate the longer of the two, and a race between a user claiming an alias and the system generating the same string must be handled by the database constraint, not assumed away. See Failure & Scale -> Custom Alias Race.

Likely follow-ups
  • Would you reserve a namespace of short generated codes so they never collide with human-chosen aliases? — Tests awareness that a shared namespace needs an explicit collision policy, not just a bigger keyspace.

Do links expire, and does analytics need to be exact or can events be sampled/lost?

Strong answer angle

Both change the design meaningfully: expiry requires distinguishing 'not found' from 'expired' end-to-end (cache, database, response), and accepting approximate analytics allows a much simpler, higher-throughput event pipeline than an exact-count requirement would.

Likely follow-ups
  • If analytics must be exact, what does that do to your event-delivery guarantees? — Tests whether the candidate connects an exactness requirement to explicit deduplication and at-least-once handling, not just 'use Kafka'.

Capacity

What is the redirect-to-create ratio, and why does it matter?

Strong answer angle

Frame it as a stated assumption (a common illustrative range is 100:1 to 1000:1 for public-link products), not a fact — but whatever ratio is assumed, it should directly justify a cache-first architecture, since the overwhelming majority of traffic is reads against a small, highly skewed set of hot mappings.

Likely follow-ups
  • What would change if the ratio were closer to 2:1 instead of 100:1? — Tests whether the candidate recognizes that a low read/write ratio would materially weaken the case for aggressive read caching as the central design decision.

When does storage actually become a concern for this system?

Strong answer angle

Storage for URL mappings themselves is usually small relative to modern infrastructure even at large mapping counts — but this is workload-dependent (it changes with average URL length, metadata retained per mapping, and retention policy), and analytics-event storage is a completely separate, much larger-volume concern that should be estimated separately.

Likely follow-ups
  • How does analytics-event storage volume compare to mapping storage volume? — Tests whether the candidate treats these as two distinct estimates rather than one number.

How does cache hit ratio change database read load, and what assumptions go into that number?

Strong answer angle

Present it as a formula (effective DB load ≈ (1 - hit_ratio) × total reads) with an explicitly labeled illustrative hit ratio, not a fixed universal percentage — the real ratio depends on traffic skew, TTL policy, and cache sizing. See Complete Design -> Caching Strategy.

Likely follow-ups
  • What traffic pattern would make cache hit ratio much lower than assumed? — Tests whether the candidate connects hit ratio to the underlying access-pattern assumption (Zipf-like skew) rather than treating it as a given constant.

ID Generation

Why Base62, and how many codes does 6/7/8 characters actually provide?

Strong answer angle

Base62 ([0-9a-zA-Z]) packs more information per character than hex or Base10, keeping codes short and URL-safe. Namespace size grows exponentially with length (62^6 ≈ 56.8B, 62^7 ≈ 3.5T, 62^8 ≈ 218T) — the right length is chosen from expected volume, desired occupancy, and acceptable retry rate, not memorized as a fixed number. See Architecture Deep Dive -> Short-Code Generation.

Likely follow-ups
  • If you expect 10 billion mappings over the system's lifetime, how would you pick a length? — Tests whether the candidate reasons from occupancy ratio rather than reciting a fixed 7-character answer.

Random vs sequential code generation — what's the real trade-off?

Strong answer angle

Sequential (auto-increment + Base62) is simple and collision-free but trivially enumerable — someone can iterate every code ever issued. Random Base62 is non-enumerable but introduces a small, manageable collision probability that must be handled with a uniqueness constraint and retry. See Trade-offs -> Random vs Enumerable Codes.

Likely follow-ups
  • Does hiding the code space actually stop someone from discovering private links? — Tests whether the candidate distinguishes non-enumerability from true access control — random codes reduce casual discovery, they do not replace authorization if links are meant to be private.

How do you handle collisions?

Strong answer angle

A database UNIQUE constraint on short_code plus a bounded retry loop on conflict. Distinguish per-attempt collision probability (n/N, the operationally relevant number) from the birthday-bound probability that any collision has ever occurred historically — they answer different questions. See Architecture Deep Dive -> Collision Handling.

Likely follow-ups
  • At what occupancy ratio would you increase the code length instead of just retrying more? — Tests whether the candidate recognizes that retry rate rising past a comfortable threshold is the actual signal, not a fixed percentage memorized in advance.

Does deploying multiple app servers require Snowflake-style ID generation?

Strong answer angle

No — this is a common but incorrect assumption. A central database sequence or a random-Base62-with-retry scheme both work fine with many concurrent app servers, because uniqueness is enforced by the database, not by the app tier. Snowflake-style generation solves a different problem: decentralized generation without any database round-trip.

Likely follow-ups
  • So when would you actually reach for Snowflake here? — Tests whether the candidate can name specific reasons — removing DB dependency for ID allocation, genuine multi-region local generation, or extremely high creation throughput — rather than treating it as a mandatory upgrade.

What happens when a Snowflake node's clock moves backward?

Strong answer angle

It can produce duplicate or out-of-order IDs for the affected window, since the scheme relies on monotonically advancing time. Production implementations typically detect clock rollback and refuse to generate IDs (or wait) until the clock catches up. See Architecture Deep Dive -> Distributed ID Generation Mechanics.

Likely follow-ups
  • Are Snowflake IDs non-enumerable once Base62-encoded? — Tests a subtle but important point: encoding does not equal obfuscation. A Snowflake ID's timestamp/worker structure is still present in the encoded value, so it remains predictable unless further permuted.

Redirect Semantics

301 vs 302 — which should a URL shortener use, and why?

Strong answer angle

Not a fixed universal answer — a genuine product trade-off. 301/308 signal permanence and let browsers/CDNs cache the redirect, reducing origin load but making the destination effectively harder to change and hiding the click from analytics on cached hits. 302/307 signal 'not permanent,' keep the origin in the loop for every redirect (accurate analytics, immediate effect of destination changes), at the cost of higher redirect-serving load. See Trade-offs -> Permanent vs Temporary Redirect.

Likely follow-ups
  • If a link's destination can be edited after creation, does that change your answer? — Tests whether the candidate connects the earlier requirements question (mutable destinations) to this specific redirect-status decision.

Can a CDN cache redirects, and how does that affect analytics?

Strong answer angle

Yes for permanently-cacheable, publicly shareable links — but an edge-cached redirect never reaches the origin, so that click is invisible to server-side analytics unless a separate edge-logging mechanism exists. See Complete Design -> Production Evolution.

Likely follow-ups
  • How would you invalidate an edge-cached redirect if the mapping is updated or expires? — Tests whether the candidate has an explicit invalidation path rather than assuming edge caches simply expire in time.

What should happen for an expired link — is it 404 or 410?

Strong answer angle

A meaningful distinction: 404 says 'not found' (ambiguous — never existed, or is it gone?), while 410 Gone explicitly communicates that the resource existed and was intentionally/permanently removed, which is more precise for an expired-by-design mapping. Either can be a reasonable product choice, but it should be a stated choice.

Likely follow-ups
  • Should the response body differ for 'never existed' vs 'expired'? — Tests UX awareness beyond raw HTTP status: a clearer user-facing message can be given even if the underlying status code is shared.

Cache

Why use a cache at all here?

Strong answer angle

Because the workload is read-dominated with skewed (hot-key) access patterns — a small fraction of mappings receive the large majority of redirect traffic. Caching those hot mappings removes the majority of read load from the database with a comparatively small, well-understood correctness surface. See Complete Design -> Caching Strategy.

Likely follow-ups
  • What would happen to this argument if traffic were uniformly distributed across all mappings instead of skewed? — Tests whether the candidate understands caching's effectiveness is tied to the skew assumption, not caching in the abstract.

What is cache-aside, concretely, for this system?

Strong answer angle

Check the cache first; on a miss, read the authoritative database, populate the cache with an expiry-bounded TTL, then return. On write, eagerly populate the cache right after the durable database write completes, so the first redirect after creation is very likely already a hit.

Likely follow-ups
  • Why populate the cache on write instead of waiting for the first read to populate it? — Tests understanding of why write-time population matters specifically for freshly-created, soon-to-be-viral links (e.g. links shared on social media).

What happens when the cache is completely down?

Strong answer angle

Every redirect falls back to the database — latency rises and database load increases substantially, but redirects should still succeed (correctness over performance) as long as the database itself can absorb the load, possibly with reduced availability targets during the incident. See Failure & Scale -> Redis / Cache Unavailable.

Likely follow-ups
  • Does the app need any code path that doesn't assume the cache is present? — Tests whether the candidate designed the read path to genuinely tolerate cache absence, not just optimize for the happy path.

What happens when the cache restarts empty?

Strong answer angle

A burst of cache misses hits the database simultaneously for previously-hot keys — a cold-cache stampede. Request coalescing (one in-flight DB lookup per key) and gradual/prioritized warming for known-hot keys reduce the impact. See Failure & Scale -> Cold Cache / Cache Stampede.

Likely follow-ups
  • How would you identify which keys to pre-warm before traffic resumes? — Tests whether the candidate would use recent access logs/analytics to prioritize warming rather than warming blindly or not at all.

How do you stop a stampede on a single popular key becoming hot again?

Strong answer angle

Request coalescing/miss locking ensures only one request performs the actual database lookup for a given key while concurrent requests wait for that result, rather than all issuing redundant duplicate queries. See Architecture Deep Dive -> Cache Stampede.

Likely follow-ups
  • Does jittering TTLs help here too, and why? — Tests whether the candidate understands jitter prevents many keys from expiring in the same instant, which is a related but distinct cause of stampede-like load spikes.

How do you handle one extremely hot URL beyond normal caching?

Strong answer angle

A shared distributed cache still concentrates one hot key's traffic on whichever node/shard owns it. A local, process-local cache on each app server for the small set of currently-hottest keys removes that traffic from the shared cache entirely. See Architecture Deep Dive -> Hot-Key Protection.

Likely follow-ups
  • Why doesn't clustering the shared cache automatically solve this? — Tests the key distinction: clustering spreads different keys across nodes, but one single key's traffic still lands on one owning node/shard regardless of cluster size.

How do you ensure a cached link never outlives its own expiry?

Strong answer angle

Bound the cache TTL by the mapping's remaining lifetime: cache_ttl = min(configured_ttl, expires_at - now), rather than applying a fixed TTL regardless of expiry. On explicit update or deletion, invalidate directly instead of waiting for TTL.

Likely follow-ups
  • What happens if a request arrives in the narrow window after expiry but before the cache entry naturally expires? — Tests whether the candidate has a consistent answer here — this is exactly what the bounded-TTL approach is designed to prevent.

Would you negative-cache nonexistent codes, and what's the risk?

Strong answer angle

Yes — it protects the database from repeated lookups of codes that will never resolve, which matters against both random guessing and scanning traffic. The risk: a negative entry must not make a just-created mapping or newly-claimed alias appear falsely missing, so creation should invalidate any matching negative entry or the negative TTL should be kept short.

Likely follow-ups
  • How would a malicious scanner exploit negative caching if it weren't handled carefully? — Tests connecting this to the abuse/scanning failure scenario, not just the caching mechanism in isolation.

Database

Why a relational database here rather than NoSQL?

Strong answer angle

Frame it as workload-dependent rather than universally correct: the access pattern is simple key lookups with a strict uniqueness requirement, which a relational database with a primary-key index handles well and gives strong consistency for. A distributed KV/NoSQL store is a reasonable alternative if write throughput needs to scale beyond a single primary far more than this workload assumes, or if the team already operates one at scale. See Trade-offs -> Database Choice.

Likely follow-ups
  • What specifically would push you toward a distributed KV store instead? — Tests whether the candidate can name the actual triggering condition (write throughput beyond a single primary, existing operational expertise) rather than treating one database family as inherently correct.

What index is required, and why?

Strong answer angle

A unique index on short_code, since every read and every write conflict-check depends on fast, exact lookup by that column. Additional indexes (e.g. on owner_id, if accounts are in scope) are added only if a query pattern actually needs them.

Likely follow-ups
  • Would you index long_url or a hash of it? — Tests whether the candidate connects this to the dedup decision — a dedup lookup needs an index on the URL (or its hash); a purely generative design does not.

Do cache misses read from the primary or a replica?

Strong answer angle

A genuine trade-off, not a default: reading from a replica offloads the primary but risks replica lag returning a stale or momentarily-missing result for a very recently written mapping; reading from the primary guarantees freshness but adds load to the single most critical write path. See Trade-offs -> Primary vs Replica Reads.

Likely follow-ups
  • How would you mitigate the staleness risk if you chose replica reads? — Tests whether the candidate proposes something concrete — e.g. a short grace period reading from primary right after creation, or accepting brief staleness as a stated trade-off.

What can replica lag actually break?

Strong answer angle

A cache miss hitting a lagging replica right after a write can return 'not found' for a mapping that was just created, or a stale long_url after an update — this is worse than a slow response, it's an incorrect one. See Failure & Scale -> Read Replica Lag.

Likely follow-ups
  • How would a client experience this bug in practice? — Tests whether the candidate can describe the concrete symptom — e.g. 'link works for the creator immediately but 404s for the first few other visitors' — not just the abstract mechanism.

What happens if the database primary fails?

Strong answer angle

Depends on the failover mechanism in place: with automatic failover, a replica is promoted and writes resume after a bounded gap during which writes fail or queue; without it, writes fail until manual intervention. Reads can often continue from replicas throughout. Treat any specific failover-time number as illustrative, not a hard guarantee. See Failure & Scale -> Database Primary Failure.

Likely follow-ups
  • Do redirects (reads) need the primary at all in steady state? — Tests whether the candidate recognizes reads can be served from cache and replicas, so primary failure primarily threatens the write path (new URL creation), not existing redirects.

How do custom-alias races get resolved?

Strong answer angle

The database UNIQUE constraint is the actual source of truth for who wins — the first successful INSERT/commit wins, and the loser receives an explicit conflict response, even if both requests appeared to pass an earlier availability check. See Failure & Scale -> Custom Alias Race.

Likely follow-ups
  • Why isn't an earlier read-then-write check on the alias enough? — Tests understanding of the check-then-act race condition — only a database-enforced constraint at write time is airtight.

Analytics

Why not write analytics synchronously on the redirect path?

Strong answer angle

Because it would couple redirect latency and availability to the analytics store's health, and would make the database (or analytics store) absorb write volume proportional to all redirect traffic rather than just creation traffic — a significant and unnecessary risk for a metric that doesn't need to be immediately consistent.

Likely follow-ups
  • Is there any analytics signal that genuinely needs to be synchronous? — Tests whether the candidate can articulate a boundary — e.g. abuse/rate-limit counters might need tighter consistency than click analytics, and would be architected separately.

If the event broker (Kafka/SQS) is unavailable, do redirects fail?

Strong answer angle

They should not — the analytics-emission step must have an explicit bounded failure policy (a short publish timeout, non-blocking producer, and an explicit choice to drop/sample/buffer within strict bounds on failure) so that redirect serving never waits indefinitely on the broker. See Failure & Scale -> Analytics Broker Unavailable.

Likely follow-ups
  • What's the actual cost of choosing to drop events during an outage? — Tests whether the candidate can reason about the trade-off explicitly (some click data permanently lost) rather than assuming 'async' means no cost at all.

Do we need strict ordering of analytics events per URL?

Strong answer angle

Usually not, if the analytics product needs commutative aggregates (counts, counts-over-time) rather than an exact event sequence — partitioning purely by short_code buys per-URL ordering at the cost of concentrating a viral URL's entire event volume on one partition. See Architecture Deep Dive -> Analytics Partitioning.

Likely follow-ups
  • What would break if you spread a viral URL's events across partitions instead? — Tests whether the candidate recognizes that spreading breaks per-URL ordering but is fine if aggregation happens downstream regardless of arrival order.

What happens to a viral URL's events if they're keyed strictly by short_code?

Strong answer angle

All of that URL's events land on a single partition, creating a hot partition and, in turn, a hot consumer — this is exactly the same hot-key problem seen in caching, but manifesting in the event-streaming layer instead.

Likely follow-ups
  • Is this the same underlying problem as the Redis hot-key scenario? — Tests the candidate's ability to recognize the same conceptual pattern (skewed access to one key) recurring in a different subsystem.

How do you prevent duplicate click counting?

Strong answer angle

Include a stable event ID in each analytics event and define explicit deduplication semantics at query/aggregation time — at-least-once delivery can produce duplicates, and a storage engine's built-in merge behavior should not be assumed to handle this automatically unless verified.

Likely follow-ups
  • Is exact deduplication always necessary? — Tests whether the candidate recognizes approximate analytics can be an explicitly acceptable trade-off for cost/throughput, if the product doesn't need exact counts.

How do you recover from an analytics consumer backlog?

Strong answer angle

Scale out consumers to increase drain rate, and confirm the broker's retention window is longer than the time needed to catch up so no events are lost while backlogged — the redirect path itself is unaffected since analytics is fully decoupled from it. See Failure & Scale -> Analytics Queue Backlog.

Likely follow-ups
  • What if the backlog grows faster than you can add consumers? — Tests whether the candidate considers sampling or temporarily dropping lower-priority event fields as a release valve, rather than assuming infinite consumer scaling is always available.

Failure Scenarios

Redis (or the shared cache) becomes completely unavailable — walk through what happens.

Strong answer angle

Reads fall through to the database for every request, raising latency and database load; writes are unaffected since the database is authoritative. Redirects should keep working, just slower, as long as the database can absorb the load. See Failure & Scale -> Redis / Cache Unavailable.

Likely follow-ups
  • At what point would degraded-but-working become an outage? — Tests whether the candidate identifies the actual limiting resource — database connection pool or CPU saturation — rather than giving a vague answer.

Redis comes back, but the cache is completely cold — is that the same as Redis being down?

Strong answer angle

No — it's a distinct scenario: the cache is reachable and correct, but empty, so it briefly behaves like an all-misses cache, which is the cold-cache-stampede scenario rather than the cache-unavailable scenario. See Failure & Scale -> Cold Cache / Cache Stampede.

Likely follow-ups
  • Would your mitigation for cache-down also help here? — Tests whether the candidate recognizes coalescing/warming addresses this scenario specifically, while the cache-down scenario's mitigation is simply 'tolerate reading from the DB'.

The database primary is unavailable — what's actually affected?

Strong answer angle

New URL creation (writes) is affected; existing redirects (reads) can often continue from cache and read replicas. The exact recovery path depends on whether automatic failover is configured. See Failure & Scale -> Database Primary Failure.

Likely follow-ups
  • What if a code was just created seconds before the primary failed, and isn't cached or replicated yet? — Tests whether the candidate identifies this narrow, genuinely lost-availability window rather than claiming everything keeps working.

A read replica is stale — what does that break, specifically?

Strong answer angle

It can make a cache miss return 'not found' for a mapping that was just created, or return a stale destination after an update — an incorrect answer, not just a slow one. See Failure & Scale -> Read Replica Lag.

Likely follow-ups
  • How would you detect this is happening in production? — Tests whether the candidate names a concrete signal — replication lag metric, or an elevated not-found rate immediately following creation events.

One URL suddenly becomes viral — walk through which component is under the most pressure, and when.

Strong answer angle

There's no single fixed answer — it depends on the current cache state and deployment. If the URL is already cached, the shared cache absorbs almost all of it and the app tier's request-handling capacity becomes the limiting factor. If it's freshly created and not yet cached, the first burst can hit the database directly until the cache populates, making cache-population speed and stampede protection the deciding factor.

Likely follow-ups
  • Why is 'the app tier always fails first' not a safe universal claim? — Tests explicitly against the memorized wrong answer — the real bottleneck depends on the scenario's specific starting conditions, not a fixed component ranking.

An analytics queue becomes unavailable — do redirects fail?

Strong answer angle

No, if the design correctly isolates analytics emission with a bounded failure policy — redirects should keep working with some data loss/degradation in analytics, not an availability incident in the redirect path. See Failure & Scale -> Analytics Broker Unavailable.

Likely follow-ups
  • What would indicate the isolation wasn't actually implemented correctly? — Tests whether the candidate can name the failure symptom of a design mistake here — e.g. redirect latency rising in step with broker latency.

A random-code scanning attack causes millions of cache misses — what protects the system?

Strong answer angle

Negative caching for nonexistent codes absorbs repeated lookups of the same guessed code; rate limiting by IP/client bounds the request rate regardless of hit/miss; and monitoring for an abnormal not-found rate helps detect the pattern early. See Failure & Scale -> Malicious Random-Code Scan / Abuse.

Likely follow-ups
  • Why doesn't a large keyspace alone prevent this? — Tests whether the candidate recognizes a large keyspace makes success unlikely per-guess, but a scanner can still generate enormous request volume regardless of eventual success rate — the defense is rate/negative-caching, not keyspace size.

A mapping has expired but a stale cache entry still redirects successfully — how does this happen, and how do you prevent it?

Strong answer angle

It happens when the cache TTL isn't bounded by the mapping's own expiry (a fixed TTL outliving expires_at). Prevented by cache_ttl = min(configured_ttl, expires_at - now) and explicit invalidation on any expiry-relevant mutation. See Failure & Scale -> Mapping Expiry vs Stale Cache.

Likely follow-ups
  • Is there a race between checking expiry and serving from cache? — Tests careful reasoning about a narrow timing window and whether the candidate's fix actually closes it or just narrows it.

Scale Curveballs

The system needs to grow from 1k to 1M redirects/sec — what changes?

Strong answer angle

Not a single lever — cache hit ratio and cache-node capacity become the dominant scaling factors (most traffic should never reach the database at this point), the app tier needs to scale horizontally behind a load balancer, and hot-key protection (local caching) becomes far more important as concentration increases at higher absolute volume. See Complete Design -> Production Evolution.

Likely follow-ups
  • Does the database need to scale by the same factor? — Tests whether the candidate recognizes the database mainly serves cache misses and writes — its required scale-up is much smaller than the raw redirect-rate growth, if caching is effective.

90% of traffic suddenly targets one URL — what's your response?

Strong answer angle

This is the hot-key scenario, not a general capacity problem: a shared-cache hit already helps a lot, but concentrated traffic on one key benefits specifically from a local process-cache layer and possibly explicit rate-shaping if the origin (analytics, verification steps) can't absorb the volume. See Architecture Deep Dive -> Hot-Key Protection.

Likely follow-ups
  • Would adding more cache nodes help here? — Tests the hot-key vs. general-capacity distinction again — more nodes spread other keys' traffic, not this one key's traffic.

The product needs to deploy across multiple regions — what's the hardest part?

Strong answer angle

Not the redirect-serving path (that can be replicated read-heavy infrastructure per region relatively simply) — the hard part is write-side: where new codes are generated, how global uniqueness is guaranteed if multiple regions can create mappings, what a redirect in another region sees immediately after creation (before replication completes), and how caches are invalidated cross-region. See Complete Design -> Production Evolution.

Likely follow-ups
  • Could you avoid most of this complexity if only one region needs to accept writes? — Tests whether the candidate recognizes a single-write-region-plus-multi-region-read-replicas design sidesteps most of the hard distributed-writes problems.

The product needs to support editable destinations — what does that touch?

Strong answer angle

Cache invalidation on update becomes mandatory (not just TTL-based), and analytics needs to record which destination a given click was actually attributed to if a mapping's destination changes over its lifetime and that history matters to the product.

Likely follow-ups
  • Should an update be versioned, or should it simply overwrite the row? — Tests whether the candidate considers audit/history requirements as a distinct, separately-scoped decision.

The product needs to support private/expiring links — what does that touch?

Strong answer angle

Authentication/authorization on the redirect path (not just creation), a 3-way expiry distinction (never / TTL-based / manually revoked) implemented consistently across cache and database, and negative-cache handling that doesn't leak whether a private link exists to an unauthorized requester. See Complete Design -> Requirements.

Likely follow-ups
  • How would negative caching need to change if link existence itself is sensitive? — Tests a subtle privacy point: a naive negative cache could still let an unauthorized client distinguish 'doesn't exist' from 'exists but you can't see it' via timing or response shape.

The product needs to support billions of retained mappings — what changes?

Strong answer angle

Storage volume for mappings themselves grows but is usually still manageable on modern infrastructure; the more material changes are index size/lookup performance at that row count, and whether the ID-generation keyspace and code length remain comfortably sized for that occupancy.

Likely follow-ups
  • Would you need to increase code length at that scale? — Tests whether the candidate connects this back to the collision-math discussion — occupancy ratio, not the raw count alone, determines whether length needs to grow.

The product needs very high URL-creation throughput — what changes?

Strong answer angle

This is exactly the scenario where a central database sequence's throughput ceiling (not its correctness) becomes the real constraint, making decentralized ID generation (Snowflake-style, or pre-allocated ranges) genuinely worth adopting rather than optional. See Architecture Deep Dive -> Distributed ID Generation Mechanics.

Likely follow-ups
  • Would random-Base62-with-retry still work at this throughput? — Tests whether the candidate recognizes retry-rate under contention rises with concurrent creation throughput, and reasons about when that becomes a real bottleneck rather than a theoretical one.

The product needs exact global analytics across regions — what changes?

Strong answer angle

This conflicts with the earlier 'approximate analytics is fine, keep it fully decoupled' default — exact global counts require either a single global aggregation point (a consistency/latency cost) or a carefully designed idempotent, deduplicated multi-region pipeline, which is meaningfully more complex than the single-region async design.

Likely follow-ups
  • Is this requirement compatible with dropping events during a broker outage? — Tests whether the candidate recognizes 'exact' and 'best-effort drop-on-failure' are in direct tension, and that satisfying both requires a different, more expensive design (e.g. durable buffering instead of dropping).
Section 04

Quick Reference Card

Everything to recall in 30 seconds — framed as assumptions and decision points, not universal facts.

Key takeaways
  • Read/write ratio and traffic skew are stated assumptions, not fixed universal numbers — but for a public-link product they typically justify a cache-first design
  • Storage for mappings is rarely the bottleneck; analytics-event volume is a separate, larger estimate
  • Random Base62 with a UNIQUE constraint + bounded retry is a reasonable simple default; Snowflake-style generation is for decentralized high-throughput needs, not mandatory once multiple app servers exist
  • Redirect status (301/302/etc.) is a product trade-off between origin caching load and analytics/update freshness — never reduce it to 'one caches, one doesn't'
  • Cache-aside with eager population on write, TTL bounded by the mapping's own expiry, explicit invalidation on update/delete
  • Negative caching + request coalescing/stampede protection + a local hot-key cache layer round out cache correctness
  • Analytics is fully decoupled and asynchronous — but the async producer still needs an explicit bounded failure policy, not an assumption of automatic safety
  • Failure order under a viral spike depends on cache state and deployment specifics — reason through the scenario, don't recite a fixed component order
  • Stage transitions (single-server -> cache -> LB/replicas/queue -> multi-region) are triggered by measured SLO/saturation signals, not memorized traffic thresholds
  • Multi-region's hard part is the write path (global uniqueness, cross-region cache invalidation) — not redirect serving itself