List the key functional requirements for the system (Ask the AI for hints if stuck)...
List the key non-functional requirements (performance, scalability, reliability, etc.)...
Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
The math: ~500 bytes/entry × 100M/day = 50GB/day ≈ 18TB/year. That number is what justifies sharding later.
Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
POST /api/v1/urls
Request: { "longUrl": "https://example.com/very/long/path" }
Response: 201 { "shortUrl": "https://tiny.url/aX7Kp2", "code": "aX7Kp2" }
GET /{code}
Response: 302, Location: https://example.com/very/long/path
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
Flow — Create POST /urls → LB → Create Service → ID Generator (base62 code, Uses a range allocator) → write to DB → return 201 {shortUrl, code}.
Flow — Redirect GET /{code} → LB → Redirect Service → Redis hit? return 302 : read DB → populate cache → return 302.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
Since our flow is structered has realations and needs to be transactions.
Writes: strong consistency — a new code must be durable and never duplicated. Reads: eventual consistency is fine — a replica lagging briefly only risks a transient 404, not corruption.
I would use PostgresSQL here for the USER, LINKS.
User {
id: UUID PRIMARY_KEY;
name: STRING;
created_at: Date;
}
Link {
code: VARCHAR(7) PRIMARY_KEY; a redirect is a single primary-key lookup, not a table scan
long_url: STRING;
user_id: UUID => User.id
created_at TIMESTAMP DEFAULT now()
expires_at TIMESTAMP NULL
}
hash of the short code → each redirect resolves on a single shard, no scatter/gather.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
1. Short-Code Generator
Counter-based IDs → base62. A range allocator hands each instance a block (e.g., 10K IDs) so instances generate independently with zero collisions.
DB has a unique index on code as a safety net — on the rare conflict, retry with a fresh code.
Hard to guess: shuffle the base62 alphabet / add a salt so codes aren't enumerable.
2. Redirect + Cache Path
GET /{code} → Redis → DB on miss → repopulate cache → 302.
Viral link: served from Redis/CDN so origin doesn't see the spike.
Thundering herd: single-flight lock on a hot-key miss — one request hits the DB, the rest wait.
3. Sharding & Hotspots
Shard by hash(code) → a redirect resolves on one shard, no scatter/gather.
Redis + read replicas absorb hot keys.