Video summary
Beginner System Design Interview: Design Bitly w/ a Ex-Meta Staff Engineer
Main summary
Key takeaways
Video purpose / audience
- Beginner-friendly system design walkthrough of the classic interview question: “Design a URL shortener (like Bitly)”.
- The speaker (Evan) slows down the process and explains concepts typically assumed in advanced system design interviews.
Suggested interview framework (technology/process)
- Define requirements
- Functional: core features users need.
- Non-functional: qualities like latency, scalability, durability, security, fault tolerance, and CAP theorem decisions.
- Outline core entities (high-level tables/collections).
- Design the user-facing API contract (endpoints mapped 1:1 to functional requirements).
- High-level design (whiteboard): draw a simple working system that satisfies functional requirements first.
- Deep dives: revisit each non-functional requirement one-by-one, evolving the design until all requirements are met.
The speaker argues that up-front back-of-the-envelope math is often unhelpful if it doesn’t directly change a decision; instead, do calculations when they inform architecture choices.
Functional requirements for the URL shortener
- Create: users can create a short URL from a long/original URL.
- Redirect: users can access the short URL and be redirected to the original URL.
Optional features added to the design
- Custom alias: user-provided short code (e.g., “Evan”), producing
bit.ly/Evanif it doesn’t already exist. - Expiration time: short links can expire; redirects after expiry return an error.
Non-functional requirements discussed (and decisions)
- Low latency on redirects
- Target: ~200 ms as a human-perceived “real-time” bound.
- Scale
- Example target: 100M DAU and 1B total URLs.
- Redirect traffic estimate leads to around 100K–10K–100K req/s peak range (speaker uses exponent-based math intuition).
- Uniqueness of short codes
- Must avoid collisions to prevent incorrect redirects.
- CAP theorem
- Decision for URL shortening: prioritize high availability over strong consistency.
- Use eventual consistency rather than strong read-after-write consistency.
- Rationale: brief inconsistency can be handled with user-facing retry/error (“still saving—try again”) and isn’t catastrophic like banking/ticketing.
API design (endpoint mapping to requirements)
Create shortened URL
- Example REST-ish:
POST /urls - Returns the short URL and includes:
- the original URL
- optional custom alias
- optional expiration time
Redirect
- Example REST-ish:
GET /{shortCode} - The server looks up the original URL and responds with an HTTP redirect.
Redirect choice: 302 vs 301 (feature/analytics trade-off)
- 302 (temporary redirect)
- Browser goes back to your service each time → better for logging/analytics/monitoring and detecting breakage.
- 301 (permanent redirect)
- Can be cached by clients/DNS → reduces hits to your service but reduces visibility and may make debugging harder.
- Speaker’s rationale in this interview: choose 302.
High-level design (baseline system)
- Client → primary backend server → database
- After
POST /urls:- Backend generates a short code, stores a mapping in a URL table.
- The table conceptually includes:
- short URL/code
- long/original URL
- creation time
- user id
- (optionally) custom alias, expiration time
- For
GET /{shortCode}:- Backend queries DB by short code and issues 302 redirect to the original URL.
- Notes: the baseline initially “black-boxes” the short-code generation complexity, then revisits it in deep dive.
Deep dive #1: generating unique short codes (collision/uniqueness)
The speaker evaluates several strategies:
- Prefix-based (bad)
- Taking the first 5–7 chars of the long URL causes many collisions → many-to-one mapping.
- Random number generator + Base62 encoding
- Encode a random integer in Base62 (0-9, A-Z, a-z).
- Produces short codes (e.g., ~6 chars).
- Collision risk is analyzed via the birthday paradox.
- Mitigation option:
- Check DB for collision (read first; insert only if not present).
- Hash long URL → Base62 + slice
- Deterministic hashing (MD5/Murmur/sha variants) and take prefix.
- Collision probability behaves similarly to random approach; DB collision check still recommended.
- Sequential counter + Base62 encoding (no collisions, but predictability risk)
- Use an incrementing counter, Base62 encode it to produce unique codes.
- Pros: guaranteed uniqueness; avoids collision checks.
- Cons: predictable (scraping/enumeration risk).
- Mitigations:
- rate limiting
- product warning (“don’t shorten private URLs”)
- bijective/obfuscation function (example mentioned: SquidsDog) to map sequential numbers to Bitly-like codes non-incrementally.
Deep dive #2: low latency on redirects
To reduce redirect lookup time:
- Database indexing
- Use the short code as a primary key → DB automatically builds an index (example: PostgreSQL uses B-tree).
- Optional secondary indexes (hash indexing) discussed but stated as often unnecessary in practice.
- Caching
- Use a read-through LRU cache (examples: Redis/memcache).
- Key: short code → Value: long/original URL
- On cache miss: fetch from DB, populate cache.
- Hot URLs remain cached; cold URLs are evicted.
CDN option (trade-off)
- CDN edge caches redirect responses closer to users.
- Drawback: may bypass backend routing and reduce monitoring/logging visibility—mirrors the 301 caching/visibility trade-off.
Deep dive #3: scalability to 100M DAU and 1B URLs
- Compute redirect QPS estimate:
- 100M/day DAU assuming ~1 redirect per user → estimate leads to ~10K–100K requests/sec peak.
- Scaling approach:
- Horizontal scaling preferred (add more instances).
- Reads vastly outnumber writes, so the architecture evolves into microservices:
- Read service: handles redirects (high QPS)
- Write service: handles URL creation (lower QPS)
- API Gateway routes requests to the right service based on endpoint.
Scaling short-code counters safely
- Problem: multiple write service instances need a global shared counter.
- Solution:
- Move counter to a global counter store such as Redis, using atomic increment operations.
- Optimization idea:
- Pre-fetch a batch of counter values (e.g., next 1000) to reduce network overhead; unused values can be lost without harming correctness.
Deep dive #4: database sizing and sharding
- Rough sizing:
- The table row estimated up to ~500 bytes.
- For 1B rows → about 500 GB, described as manageable for modern storage.
- Primary concern becomes read throughput, mitigated via Redis cache.
- Sharding discussed as an option:
- If needed, shard by short code.
- Example idea: distribute by
hash(shortCode) mod N.
- Conclusion in the worked example: a single DB may suffice.
Deep dive #5: high availability (fault tolerance)
- Auto-scaling improves availability for read/write services.
Redis availability
- Down-time isn’t ideal because it provides global counter increments.
- Suggested mitigations:
- HA configuration
- snapshots of counter/state
Database HA
- Replicas and periodic snapshots (example: snapshot to S3-like object storage and restore).
- If the primary DB fails, restore from snapshot/replica.
Outcome / review conclusion
The design is considered complete once it satisfies:
- All functional requirements: create, redirect, optional alias, optional expiration
- All non-functional requirements: low latency, scale, uniqueness, availability/consistency choice
Main speaker(s) / sources
- Evan (co-founder of Hello Interview, former Meta staff engineer) — primary narrator and system design presenter.
- Mentions Stefan (co-founder of Hello Interview) in context of tuning guided practice feedback.