URL Shortener — Masterclass
Three additional artifacts a Staff/Principal candidate should be able to produce for this problem: an Architecture Decision Record, a business-driven design exercise, and a production incident scenario.
1. Architecture Decision Record
The format working architects use to document a decision so future teams understand context, options, and reversal conditions.
MySQL over DynamoDB for URL Shortener at L4/L5 (10K-100K RPS)
- Team size: 3 backend engineers, none with DynamoDB experience
- Timeline: MVP in 8 weeks
- Budget: <$10K/month infra
- Availability: 99.9% (~9 hours/year downtime)
- Latency: p99 redirect < 100ms
- Anticipated scale: 10K RPS launch → 100K RPS in 6 months
- Analytics: click counts, no real-time requirement
- Public URL shortener, so abuse prevention required
MySQL (RDS Multi-AZ)
- Team has 5+ years combined experience
- Ad-hoc SQL for analytics without engineering effort
- Familiar migration and backup story
- $170/mo for db.m6i.large Multi-AZ handles 10K RPS today, upgrades to db.r6i.4xlarge (~$1600/mo) at 100K
- Adding read replicas is one API call
- Rich ecosystem: ProxySQL, ProxySQL, Percona Toolkit, MySQL Shell.
- Single-primary write bottleneck — needs sharding or Aurora at 500K+ RPS (not in scope)
- Vertical scale ceiling ~1M QPS on read replicas
DynamoDB
- Infinite horizontal scale — never touch a database again
- Single-digit ms latency at any scale
- Fully managed — zero ops burden
- Multi-region replication built-in (Global Tables)
- Team learning curve is 2-4 weeks minimum (composite keys, single-table design)
- $$$ at low RPS: on-demand mode at 10K RPS = ~$1500/mo
- No ad-hoc SQL — every analytics query needs to be pre-designed as GSI or streamed to Athena
- Migration to different DB later is a multi-quarter project
MongoDB Atlas
- Familiar-looking to some engineers
- Auto-scaling
- Managed
- No team experience with production Mongo
- Weaker transactional guarantees vs MySQL
- Analytics with aggregation pipeline is a learning curve
- Doesn't clearly solve any problem MySQL doesn't already solve
Redis + async MySQL write
- Sub-ms redirect latency
- No DB on hot path
- Redis is not durable for source-of-truth data
- Race conditions between cache and durable write
- Complex operational story for an MVP
MySQL (RDS Multi-AZ)
- DynamoDB — 10x cost at MVP scale and team learning curve blocks the 8-week deadline
- MongoDB — doesn't solve a problem MySQL doesn't; adds unfamiliar tech risk
- Redis + async — introduces data-loss risk on primary write path; wrong tool for durability
- Accept single-primary write bottleneck. Solve later with Vitess-like sharding if we exceed 500K writes/sec.
- Accept manual failover coordination during Multi-AZ promotion (30-90s downtime once/year historical average)
- Accept that migrating to distributed DB (Spanner, Cockroach) is a future project if truly global scale becomes necessary
- MVP ships on time on infrastructure the team can debug at 3am
- Add read replicas at 100K RPS (~2 hours of work), Aurora migration at 500K RPS (~1 month of work)
- Total cost through year 1: <$25K, well under $120K budget
- Gained option: rich SQL analytics without any additional engineering effort
- Sustained write throughput exceeds 500K/sec — consider Aurora Serverless v2 or Vitess
- Cross-region strong consistency requirement — consider Spanner
- Team gains DynamoDB expertise AND workload pattern proves single-key access — consider migration
2. Business constraint exercise
Given real-world constraints (team size, budget, deadline), what architecture do you propose — and how do you push back when leadership asks for the wrong thing? This teaches engineering judgment.
You've just joined as the tech lead for a URL shortener startup. The CEO wants to launch in 8 weeks to piggyback off a marketing campaign. You have 3 engineers, $10K/month burn on infrastructure, and the executive team is worried about 'what if we go viral.' You're picking the architecture.
- 18-week deadline (blocked partnership deal on the marketing campaign date)
- 23 backend engineers total (2 with 5+ years, 1 junior)
- 3$10K/mo max burn on infrastructure
- 4SLA promised to partners: 99.9% availability
- 5The CEO read a blog post about 'the Netflix stack' and wants microservices from day 1
- 6You cannot hire more engineers for at least 6 months (runway constraint)
What architecture do you propose, and how do you push back on the microservices ask? Be specific about tech choices (LB, DB, cache, deployment).
3. Production incident scenario
You are on-call at 3:47am. p99 has spiked. Walk through the investigation, hypothesis, mitigation, and postmortem. This teaches real production reasoning — not just design.
PagerDuty alert at 3:47am. Redirect endpoint (/r/{code}) p99 latency has spiked from a baseline of 50ms to 8 seconds. Traffic is normal (1M req/hr, in line with the last week). Error rate is at 0% — nothing is failing, just slow. Users are complaining on Twitter. You are the on-call.
- app.redirect.latency.p99: 8.2s (was 50ms) — spike started 3:15am
- app.redirect.latency.p50: 45ms (normal)
- app.request_rate: 285 req/sec (normal for this hour)
- app.error_rate: 0.02% (normal)
- db.MySQL.cpu: 12% (normal)
- db.MySQL.active_connections: 45/200 (normal)
- db.MySQL.slow_queries: 2/sec (normal)
- cache.redis.hit_rate: 42% (was 95%!) — spike started 3:15am
- cache.redis.cpu: 89% (was 15%)
- cache.redis.evictions_per_sec: 12000 (was ~0)
- cache.redis.memory_used: 30GB / 32GB (was 22GB)
- 3:14am — normal traffic pattern, no errors
- 3:15am — deployment event: '`redis-config-v42`: memory limit lowered from 64GB to 32GB'
- 3:16am — Redis eviction rate jumps from 0 to 12K/sec
- 3:17am — Cache hit rate collapses from 95% to 42%
- 3:47am — PagerDuty alert fires (p99 threshold)
- Trace of a slow request:
- → LB: 2ms
- → App server: check Redis (cache-aside pattern)
- → Redis GET user:abc123: MISS (7ms — normally 1ms)
- → App server: fetch from MySQL
- → MySQL SELECT: 65ms
- → App server: write to Redis SET: 15ms (normally 1ms)
- → Response: 82ms typical when cache misses
- → But queue at Redis is 200 requests deep, adding ~7 seconds wait
- MySQL primary: healthy (CPU 12%)
- MySQL read replica: healthy (CPU 8%)
- Redis: DEGRADED (CPU 89%, eviction spike)
- ALB: healthy
- External DNS: healthy
You look at the metrics dashboard. Which single metric is telling you the most useful thing right now?
You correlate the cache hit rate drop with the deployment event at 3:15am — the Redis config change lowered memory from 64GB to 32GB. What's your working hypothesis?
What do you do RIGHT NOW to mitigate? You need to decide in 60 seconds — user pain is climbing.
After rollback, cache recovers in 5 minutes. What's your postmortem action item to prevent this?
You get out of the incident. In the retro, someone says 'we should have caught this in staging.' Would staging have caught it? Why or why not?
Learn these first
- URL Shortener L4 design (foundation)
- Cache-aside pattern
- Postgres + Redis operational basics
Where this appears in the curriculum
This is the Gold Standard.
Every other system will eventually have a masterclass tab like this one. The pattern proven here — ADR + business exercise + incident scenario — scales to all 50+ problems on the platform.