Mari praktikkan system design dengan merancang URL Shortener seperti bit.ly — service yang mengubah URL panjang menjadi URL pendek.
Step 1: Requirements
Functional:
- User memasukkan URL panjang → mendapat URL pendek (misalnya
short.ly/abc123) - User mengakses URL pendek → redirect ke URL asli
- URL pendek bisa di-custom (opsional)
- Statistik klik (opsional)
Non-functional:
- Redirect harus sangat cepat (< 50ms)
- High availability (99.99%)
- URL pendek tidak boleh bisa ditebak
Step 2: Estimasi Kapasitas
Asumsi:
- 100 juta URL dibuat per bulan
- Rasio read:write = 100:1 (100x lebih banyak redirect dari create)
- Data disimpan 5 tahun
Perhitungan:
- Write: 100M / 30 / 24 / 3600 ≈ 40 URL/detik
- Read: 40 × 100 = 4.000 redirect/detik
Storage:
- 100M × 12 bulan × 5 tahun = 6 miliar URL
- Rata-rata 500 bytes per record → 6B × 500 = 3 TB
Step 3: Desain Short Code
6 karakter dari [a-zA-Z0-9] = 62^6 = 56,8 miliar kombinasi (cukup untuk 6 miliar URL).
// Approach 1: Base62 encoding dari auto-increment ID
function encode(id) {
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let result = "";
while (id > 0) {
result = chars[id % 62] + result;
id = Math.floor(id / 62);
}
return result.padStart(6, "0");
}
// encode(1000000) → "4c92" → predictable! Bisa ditebak.
// Approach 2: Random + check collision
function generateCode() {
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let code = "";
for (let i = 0; i < 6; i++) {
code += chars[Math.floor(Math.random() * 62)];
}
return code;
}
// Perlu cek database apakah sudah ada (collision)
Step 4: Arsitektur High-Level
Client → Load Balancer → API Servers → Database (write)
→ Cache/Redis (read)
↓
Cache (Redis)
Key: short_code
Value: original_url
TTL: 24 jam
Step 5: Database Schema
CREATE TABLE urls (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
short_code VARCHAR(10) UNIQUE NOT NULL,
original_url TEXT NOT NULL,
user_id BIGINT,
created_at TIMESTAMP,
expires_at TIMESTAMP,
click_count BIGINT DEFAULT 0,
INDEX idx_short_code (short_code)
);
Step 6: API Design
POST /api/shorten
Body: { "url": "https://very-long-url.com/..." }
Response: { "short_url": "https://short.ly/abc123" }
GET /:code → 301 Redirect ke URL asli
GET /api/stats/:code
Response: { "clicks": 1234, "created_at": "..." }
Step 7: Redirect Flow (performa kritis)
app.get("/:code", async (req, res) => {
const code = req.params.code;
// 1. Cek cache dulu (< 1ms)
let url = await redis.get("url:" + code);
if (!url) {
// 2. Cache miss → query database
const record = await db.query("SELECT original_url FROM urls WHERE short_code = ?", [code]);
if (!record) return res.status(404).send("Not found");
url = record.original_url;
// 3. Simpan di cache
await redis.set("url:" + code, url, "EX", 86400);
}
// 4. Increment click count secara asinkron (jangan blok redirect)
clickQueue.add({ code });
// 5. Redirect (301 = permanent, bisa di-cache browser)
res.redirect(301, url);
});
Optimasi tambahan:
- Rate limiting untuk prevent abuse
- URL validation (jangan izinkan URL berbahaya)
- Analytics: simpan click data di separate table/service
- Geographically distributed dengan CDN edge redirect
🎭 Analogi sehari-hari: URL shortener = penerjemah kode pos. URL panjang = alamat lengkap "Jl. Mawar No. 17 RT 03 RW 05 Kelurahan X Kecamatan Y Jakarta Selatan 12345". Short URL = "JKT-MWR-17". Lebih cepat ditulis, tetep nyampe ke tempat yang sama. Tukang pos punya tabel mapping kode → alamat lengkap.
💡 Klasik interview SD: URL shortener = "hello world" untuk system design interview. Bagus karena cover banyak konsep: capacity estimation, ID generation, caching, hot key, redirect performance, analytics. Simple problem space tapi tiap keputusan ada trade-off.
⚠️ Jebakan klasik:
- MD5/SHA hash full → 32 char, kepanjangan. Truncate hash, cek collision
- Auto-increment ID → predictable + bocor jumlah URL ke kompetitor
- Cache TTL pendek → hot URL terus hit DB (Justin Bieber tweet phenomena)
- 301 vs 302 — 301 cached browser permanently, susah ganti destination. Pakai 302 kalau butuh fleksibel
- Click count direct DB write = bottleneck. Pakai queue → batch update
🎯 Trade-off keputusan:
- Short code length — 6 char = 56B URL (cukup 5 thn), 8 char = lebih bisa scale
- Encoding — random+collision check (aman) vs base62 ID (predictable tapi sequential)
- Cache strategy — Redis untuk hot URL, geo-distributed CDN untuk redirect speed
- Analytics — sync vs async (queue). Sync lambatin redirect, async eventual consistent
TL;DR: URL Shortener = klasik interview SD. Cover: capacity estimation + ID gen (random+collision atau base62) + cache hot URL + 301 redirect + async analytics. Hot URL = critical optimization. Performance redirect (<50ms) = priority utama.