Worked designs¶
Each one is short enough to rehearse in 10 minutes and ties to something you've done.
1. Model portfolio document generation (fact sheets with white-labeling)¶
Ties to: white-labeling, Model Portfolios.
- Requirements: advisors download a fact sheet or trade commentary PDF for a model, branded with their firm. Models change daily at most; many advisors request the same model. p95 under 2 s for a cached document; new documents within a minute.
- Estimate: 10,000 advisors × 20 downloads/day = 200k/day ≈ 2–3/s average, maybe 20/s at peak. Small.
- API:
GET /models/{id}/factsheet?format=pdf→ 200 with a pre-signed S3 URL, or 202 + poll if being generated. - Design:
flowchart LR
UI[React UI] --> API[Document API]
API -->|cache key: model version + branding version| S3[(S3: generated PDFs)]
API -->|miss| Q[Queue / Kafka: render requests]
Q --> W[Render workers: template + data + branding to PDF]
W --> S3
MS[Model service] -->|model.updated event| Q
BR[(Branding config: name, logo per firm)] --> W
- Key decisions: cache key = model version + firm branding version, so a model update or logo change produces a
new document and old links stay valid; pre-generate for popular models on
model.updatedevents; render workers scale horizontally; validate/re-encode logos on upload. - Failure modes: renderer down → serve the last generated version with its date; a corrupted logo → fall back to default branding and alert.
2. A trading exchange / order matching system¶
Ties to: Prayog. You can go deeper than any interviewer expects here: use the scale page.
- Single-writer matching per symbol shard, journal before answering, deterministic replay for recovery and hot standby, market data fan-out with conflation, Kafka off the order path into post-trade.
3. Real-time leaderboard¶
Ties to: Prayog leaderboard (Redis sorted set).
- Requirements: 1M players, scores update on every trade/game, show top 100 and "my rank" in real time.
- Design: score updates → Kafka (keyed by player) → consumer updates DB (source of truth) and Redis
ZADD; reads:ZREVRANGE 0 99 WITHSCORES(cache 1 s),ZREVRANK player. - Scale: one sorted set holds 1M members easily (O(log n) ops). For much larger: shard by score range or keep exact top-K plus approximate rank buckets.
- Failure: Redis lost → rebuild from DB and swap in atomically (
RENAMEa freshly built key), exactly what Prayog does.
4. Rate limiter¶
Ties to: Prayog's tiered token buckets.
- Algorithms: fixed window (simple, bursts at edges), sliding window log/counter, token bucket (allows bursts up to capacity, refills at a steady rate), leaky bucket (smooth output).
- Where: API gateway or a filter in each service; per user/API key/IP; return
429withRetry-After. - Distributed: shared counters in Redis (Lua script for atomic check-and-decrement), or route each account to one gateway via consistent hashing so its bucket stays local (Prayog's scaling plan).
final class TokenBucket {
private final long capacity; private final double refillPerNano;
private double tokens; private long last;
TokenBucket(long capacity, double perSecond) {
this.capacity = capacity; this.refillPerNano = perSecond / 1e9;
this.tokens = capacity; this.last = System.nanoTime();
}
synchronized boolean tryAcquire() {
long now = System.nanoTime();
tokens = Math.min(capacity, tokens + (now - last) * refillPerNano);
last = now;
if (tokens >= 1) { tokens -= 1; return true; }
return false;
}
}
5. Notification service (common generic question)¶
- Requirements: send email/SMS/push for events (trade executed, report ready); user preferences; no duplicates; retries; 10M notifications/day.
- Design: producers publish events to Kafka → notification service checks preferences and templates → per-channel queues → channel workers call providers (SES, SNS, FCM) with retries and backoff → status store; idempotency key per (event id, user, channel) so retries don't double-send; DLQ for permanent failures; rate limit per user.
- Trade-offs: at-least-once delivery + dedupe; priority queues for urgent vs. marketing.
6. URL shortener (the classic warm-up)¶
- Write: generate a 7-character base62 id (from a counter or random + collision check), store
id → urlin a key-value store. Read:GET /{id}→ 301/302 redirect; heavy read traffic, so cache hot ids in Redis/CDN. 62⁷ ≈ 3.5 trillion ids. Analytics asynchronously via Kafka.
7. Report delivery to a large client base (monthly)¶
Ties to: Index Research client reporting.
- Requirements: at month end, generate and deliver reports for thousands of clients reliably by a deadline.
- Design: a scheduler creates one job per client report in a jobs table (status, attempts); workers pick jobs
(
SELECT ... FOR UPDATE SKIP LOCKED), generate, store, deliver (email/SFTP/portal); retries with backoff; a dashboard for operations showing pending/failed; rerun one client without rerunning all. - Points: idempotent delivery (don't email twice), config-driven onboarding (a new client is a row, not code), and reference data cached for the run.