snips_interview_guide.md

πŸš€ Snips (URL Shortener & Analytics Engine) β€” Interview Mastery Guide

Tailored for High-Bar Technical & Managerial Interviews (Juspay, Tier-1 Product Companies).
Covers word-for-word spoken pitches, deep architectural walkthroughs, line-by-line defense of your resume bullet points, Redis caching mechanics, token rotation security, high-volume click ingestion, and exact answers to 15+ tough cross-questions.


πŸ“Œ Table of Contents

  1. What Is Snips? (The Plain-English Concept)
  2. What to Say in an Interview: "Explain Your Project"
  3. Resume Bullet Points β€” Word-for-Word Speaking Scripts
  4. End-to-End System & User Journeys
  5. High-Level Architecture & Component Flow
  6. Tech Stack & Justifications ("Why X over Y?")
  7. The 3 Core Technical Stories (STAR Method)
  8. Deep Technical & Concurrency Mechanics (Juspay / High-Bar Focus)
  9. Scaling, Bottlenecks & 10x/100x Evolution
  10. Engineering Trade-offs & What You Would Do Differently
  11. Cross-Questioning Masterclass (15+ Exact Q&A Scripts)
  12. Provable Numbers & Quick Reference Sheet

1. What Is Snips? (The Plain-English Concept)

The Real-World Problem:

Standard URL shorteners (like Bitly or TinyURL) are third-party SaaS products with severe limitations:

  • Expensive tiered pricing for click analytics and custom branding.
  • Lack of data ownership and privacy concerns for sensitive enterprise URLs.
  • Vulnerability to credential hijacking and session replay attacks without multi-factor authentication.
  • High latency during redirects if analytics logging blocks the HTTP response.

The Solution:

Snips is a high-performance, self-hosted full-stack URL shortening and real-time analytics platform built with React 19, Node.js/Express, PostgreSQL, Prisma, and Redis:

  1. Link Engine: Converts long URLs into 6-character Base62 slugs or user-defined custom aliases with optional password protection, link expiration dates, and active/inactive toggling.
  2. Sub-Millisecond Redirect: Employs a Redis Cache-Aside architecture where redirect lookups bypass the primary database, and click tracking is executed asynchronously in a non-blocking fire-and-forget pattern.
  3. Deep Analytics Dashboard: Visualizes traffic with Recharts, tracking geographic distribution (country, city), device types, operating systems, browsers, referrers, and hourly/daily time-series graphs with SHA-256 hashed IP anonymization for unique visitor counts.
  4. Zero-Trust Security & Session Invalidation: Features two-factor authentication (email OTP via Nodemailer), bcrypt password hashing, JWT Access/Refresh Token Rotation stored in httpOnly secure cookies, CSRF double-submit protection, and Redis-backed instant single-session revocation.

2. What to Say in an Interview: "Explain Your Project"

Choose the version matching the interview format.


⚑ 30-Second Elevator Pitch (Quick Round)

"I built Snips, a high-throughput URL shortener and click analytics platform using React 19, Node.js/Express, PostgreSQL with Prisma, and Redis.
It supports custom branded slugs, expiration dates, and an interactive Recharts analytics dashboard tracking geo-location, device, and referrer metrics.
The key technical highlight is our sub-millisecond redirect engine: lookups hit a Redis cache-aside layer while click analytics are logged asynchronously in a non-blocking pipeline. On the security side, it features JWT access/refresh token rotation with Redis-backed instant session invalidation and email OTP 2FA."


πŸŽ™οΈ 90-Second Standard Response (The Gold Standard)

(When the interviewer says: "Tell me about your project", "Walk me through what you built", or "Pick a project from your resume and explain it")

"The goal behind Snips was to build a high-performance, self-hosted URL shortening and analytics platform that matches commercial SaaS capabilities like Bitly while maintaining full data ownership and strict security.

I designed and implemented the system across three key engineering pillars:

  1. High-Throughput Redirect & Non-Blocking Analytics:
    To minimize redirect latency, incoming slug requests hit a Redis cache-aside layer with a 24-hour TTL. The HTTP 302 redirect is sent immediately, and click trackingβ€”including geo-IP lookup, user-agent parsing, and SHA-256 IP hashing for unique visitorsβ€”runs asynchronously without blocking the client. The click event and atomic counter increment are committed in a single Prisma transaction.

  2. Analytics Dashboard with Recharts:
    I built an interactive dashboard visualizing 6 analytics dimensions: country and city distribution, device breakdown (mobile/desktop/tablet), browsers, referrers, and time-series click velocity over 24-hour, 7-day, and 30-day windows.

  3. Zero-Trust Authentication & Session Invalidation:
    I implemented two-factor authentication using Nodemailer email OTPs, bcrypt hashing, and JWT access/refresh token rotation (1-hour access token, 7-day refresh token) stored in httpOnly cookies with CSRF double-submit verification. To solve the stateless JWT revocation problem, I engineered Redis-backed session tracking, allowing immediate invalidation of old sessions upon new device logins or password changes.

The tech stack is React 19 and Tailwind on the frontend, Node.js/Express on the backend, PostgreSQL on Neon via Prisma ORM, and Redis for caching, rate limiting, and session state."


πŸ—οΈ 3-Minute Deep Technical Walkthrough (System Design / Deep Dive)

(When an interviewer asks: "Explain the architecture, how data flows, and your core technical decisions")

"Let me break down Snips across its 4 architectural tiers: the Link Engine, the Redirect & Ingestion Pipeline, the Security & Token Architecture, and the Analytics Layer.

1. Link Creation & Concurrency Handling:
When a user creates a link, they can provide a custom slug or request a generated 6-character Base62 slug ($62^6 \approx 56.8\text{ billion}$ combinations). We enforce a PostgreSQL unique constraint on slug. If a collision occurs or another user grabs the custom slug simultaneously, the database raises a unique constraint violation (P2002), which our controller maps to an HTTP 409 Conflict. Once created, the slug metadata is proactively cached in Redis.

2. Sub-Millisecond Redirect & Async Ingestion Pipeline:
When a user navigates to /:slug:

  • Step 1: Express checks Redis key slug:{slug}. Cache hits return in $<2\text{ms}$. On a cache miss, it reads from PostgreSQL and populates Redis with a 24h TTL.
  • Step 2: If the link is password-protected or inactive/expired, appropriate validation occurs. Otherwise, the server sends res.redirect(302, longUrl) immediately.
  • Step 3: Non-blocking Execution: The trackClick() pipeline executes post-response. It extracts the client IP, parses User-Agent headers for browser/device/OS, computes a SHA-256 hash of the IP for privacy-preserving unique click tracking, and writes a ClickEvent record while atomically incrementing Link.totalClicks in a database transaction.

3. JWT Token Rotation & Redis Session Revocation:
Pure stateless JWTs cannot be revoked before expiry. To enforce strict single-session security, on login we store an active-session:{userId} -> sessionId key in Redis. When a user requests an access token refresh, the server validates the refresh token, invalidates the old refresh token, generates a new pair (Token Rotation), and updates Redis. If a user logs in on a new device, the old sessionId in Redis is overwritten, instantly locking out the old device.

4. Multi-Layer Rate Limiting & Protection:
We apply multi-tier rate limiting: a global IP window (1,000 req / 15 min via express-rate-limit), Redis-backed cooldowns on OTP resends (60s), and a 5-attempt limit on OTP verification before key invalidation.

5. Scale & Evolution:
At 100x scale, I would evolve the fire-and-forget ingestion into a BullMQ/Kafka message queue to buffer click writes, replace the external Geo-IP API with a local MaxMind GeoLite2 binary database, and route high-volume link lookups to Cloudflare CDN Edge Workers."


3. Resume Bullet Points β€” Word-for-Word Speaking Scripts

Here is the exact response to use when an interviewer points to any of the 3 bullets on your resume:


πŸ“Œ Resume Bullet 1:

"Built a full-stack URL shortener with custom slugs, link expiration, and a Recharts analytics dashboard tracking clicks by geo-location, device, and referrer."

What to say when asked: "Tell me about the URL shortener features and the analytics dashboard."

"For the link management engine, I gave users full control: they can generate standard Base62 slugs or claim unique custom aliases for branded campaigns. Each link supports optional password gating, activation toggles, and TTL-based expiration dates.
On the frontend, I built an interactive analytics dashboard using React 19 and Recharts. When a user inspects a link, we aggregate click data across 6 dimensions: geographic maps and bar charts for top countries and cities, pie charts for device categories (mobile, desktop, tablet) and browsers, referrer distribution (direct, Twitter, LinkedIn), and responsive time-series area charts showing hourly and daily click velocity."


πŸ“Œ Resume Bullet 2:

"Implemented JWT access/refresh token rotation with Redis-backed session invalidation, bcrypt password hashing, and OTP email verification via Nodemailer."

What to say when asked: "How did you implement your authentication, token rotation, and session invalidation?"

"I designed a zero-trust authentication pipeline. Passwords are encrypted using bcrypt with 10 salt rounds. On signup and login, we send a 6-digit one-time password (OTP) via Nodemailer, cached in Redis with a 5-minute TTL and a maximum 5-attempt brute-force threshold.
For session security, I used a dual-token strategy: a short-lived 1-hour access token and a 7-day refresh token stored in httpOnly, SameSite: Strict secure cookies.
To prevent token replay attacks, every refresh request rotates the refresh tokenβ€”issuing a fresh pair and invalidating the old token in Redis. Furthermore, we maintain active-session:{userId} in Redis. If a user logs in on a new device or resets their password, the Redis session key is overwritten, which immediately invalidates the previous device's session on its next API call."


πŸ“Œ Resume Bullet 3:

"Designed the PostgreSQL schema via Prisma for high-volume click logging, integrating Redis caching, and rate limiting."

What to say when asked: "Walk me through your database schema, Redis caching strategy, and rate limiting."

"I modeled the schema in PostgreSQL using Prisma with 3 core entities: User, Link, and ClickEvent. The Link model has indexed fields for slug (unique) and userId, while ClickEvent stores foreign keys to linkId alongside timestamp, hashed IP, country, city, browser, OS, and referrer.
To handle high-volume redirects, I implemented the Cache-Aside pattern using Redis. Slug queries check Redis first (slug:{slug} with a 24-hour TTL). This absorbs ~95% of redirect reads without touching PostgreSQL.
For rate limiting, I implemented multi-tier throttling: a global IP window (1,000 req / 15 min), a 60-second cooldown per IP+email on OTP requests, and brute-force lockouts on password and OTP endpoints using atomic Redis operations."


4. End-to-End System & User Journeys

[User Creates Short Link] β”‚ β”œβ”€β”€ Provides Custom Slug (e.g. "juspay-careers") OR generates Base62 slug β”œβ”€β”€ Sets Expiry Date & Optional Password └── Backend writes to PostgreSQL (Unique Slug Index) ──► Caches in Redis (24h TTL) [End-User Clicks Short Link: /:slug] β”‚ β”œβ”€β”€ Step 1: Backend checks Redis Cache (slug:{slug}) β”‚ β”œβ”€β”€ Hit ──► Reads metadata in <2ms β”‚ └── Miss ──► Reads PostgreSQL ──► Populates Redis β”‚ β”œβ”€β”€ Step 2: Validates status (active?, expired?, password required?) β”‚ β”œβ”€β”€ Step 3: Returns HTTP 302 Redirect to Long URL (INSTANT UX) β”‚ └── Step 4: [Asynchronous Fire-and-Forget Analytics Pipeline] β”œβ”€β”€ Extracts IP & User-Agent β”œβ”€β”€ Performs Geo-IP lookup (Country, City) β”œβ”€β”€ Hashes IP with SHA-256 (Privacy-safe Unique Clicks) └── Writes ClickEvent & increments totalClicks in Prisma Transaction

5. High-Level Architecture & Component Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLIENT LAYER (Browser / SPA) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ React 19 SPA (Vite + TailwindCSS + Recharts + Lucide Icons) β”‚ β”‚ β”‚ β”‚ - Dashboard, Link Manager, QR Generator, Recharts Analytics Visualizer β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ HTTPS REST / Cookies (httpOnly JWT + CSRF) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ API GATEWAY & APPLICATION SERVER (Express 5) β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Middleware Pipeline: Helmet Security Headers β†’ CORS β†’ Express Rate Limit (Global) β”‚ β”‚ β”‚ β”‚ β†’ Cookie Parser β†’ JWT Auth & Redis Active Session Validator β†’ CSRF Token Guard β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Link & Redirect Controller β”‚ β”‚ Auth & 2FA Controller β”‚ β”‚ β”‚ β”‚ - Base62 Slug Generator / Custom Alias β”‚ β”‚ - Bcrypt Password Hasher β”‚ β”‚ β”‚ β”‚ - Cache-Aside Lookup Engine β”‚ β”‚ - Nodemailer OTP Dispatcher β”‚ β”‚ β”‚ β”‚ - Non-Blocking Async Ingestion Trigger β”‚ β”‚ - JWT Access/Refresh Token Rotator β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ Fast Redirect Lookups β”‚ Session & OTP Keys β”‚ / Rate Limit Keys β”‚ Rate Limiting β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ REDIS IN-MEMORY STORE β”‚ β”‚ EXTERNAL INTEGRATIONS β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ - slug:{slug} (24h TTL link metadata) β”‚ β”‚ β”‚ β”‚ Nodemailer (SMTP Emails) β”‚ β”‚ β”‚ β”‚ - active-session:{userId} (Session Invalidation) β”‚ β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ - otp:{email} (5-min TTL, max 5 attempts) β”‚ β”‚ β”‚ β”‚ ip-api.com (Geo-IP Lookup) β”‚ β”‚ β”‚ β”‚ - rate-limit:{ip}:{endpoint} (Sliding Windows) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ Persistent Relational Storage β”‚ β–Ό β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PRIMARY DATABASE (PostgreSQL on Neon via Prisma) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ User Model β”‚ β”‚ Link Model β”‚ β”‚ ClickEvent Model β”‚ β”‚ β”‚ β”‚ - id, email, password β”‚1─Nβ”‚ - id, slug (UNIQUE Index) β”‚1─Nβ”‚ - id, linkId, timestamp β”‚ β”‚ β”‚ β”‚ - isVerified, createdAt β”‚ β”‚ - originalUrl, totalClicksβ”‚ β”‚ - hashedIp, country, city β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - password, expiresAt β”‚ β”‚ - browser, device, referrer β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. Tech Stack & Justifications ("Why X over Y?")

LayerTechnologyWhy Chosen?Why Not Alternatives?
FrontendReact 19 + RechartsComponent-driven UI, instantaneous HMR via Vite, declarative SVG charting with Recharts.Chart.js: Canvas-based, harder to make responsive and style with Tailwind. D3: Too verbose for dashboard widgets.
BackendNode.js & Express 5Non-blocking event loop ideal for asynchronous fire-and-forget I/O pipelines and fast Redis lookups.Python/Django: Heavier memory footprint per worker; slower raw asynchronous throughput.
Primary DBPostgreSQL (Neon)Strict schema consistency, relational integrity between Users, Links, and ClickEvents, indexed B-Tree lookups.MongoDB: High write throughput, but lacks relational integrity and foreign key constraints for enterprise dashboards.
ORMPrismaType-safe migrations, auto-generated TypeScript types, and atomic multi-table transactions (prisma.$transaction).TypeORM / Sequelize: Less reliable type inference and higher maintenance overhead.
Cache & StateRedisSub-millisecond key-value retrieval, built-in key expiry (TTL), atomic increments (INCR), and session storage.Memcached: Lacks data structures (hashes, sets), persistence options, and atomic rate-limiting primitives.
Auth & TokenJWT + Redis + CookiesStateless signature verification combined with stateful Redis session revocation for instant logout.Pure Stateless JWT: Cannot revoke compromised tokens. Pure Server Sessions: Extra DB load on every single request.

7. The 3 Core Technical Stories (STAR Method)

Story 1: Sub-Millisecond Redirect Engine & Non-Blocking Click Ingestion

  • Situation (S): When users click a shortened URL, redirect latency must be near-instant ($<20\text{ms}$). However, tracking analytics involves a geo-IP lookup, user-agent parsing, database insertion, and click counter incrementβ€”which would add 200–400ms of lag if executed synchronously.
  • Task (T): Architect a pipeline that sends the HTTP 302 redirect instantly while reliably recording comprehensive click analytics in the background.
  • Action (A):
    1. Implemented Redis Cache-Aside: GET slug:{slug} retrieves destination URL in $<2\text{ms}$.
    2. Sent res.redirect(302, originalUrl) immediately upon cache hit.
    3. Decoupled analytics into a non-blocking background promise (trackClick().catch(...)).
    4. Extracted user headers, queried geo-IP, computed a SHA-256 hash of the IP for GDPR-safe unique identification, and wrapped ClickEvent.create() and Link.update(increment) inside an atomic prisma.$transaction.
  • Result (R): Achieved sub-5ms redirect response times on cached routes while maintaining 100% analytics data capture consistency.

Story 2: JWT Token Rotation, Single-Session Enforcement & CSRF Defense

  • Situation (S): Traditional JWT authentication is vulnerable to token theft because tokens remain valid until expiration. Storing tokens in localStorage invites XSS attacks, while storing them in cookies exposes endpoints to CSRF attacks.
  • Task (T): Build an enterprise-grade auth system featuring token rotation, single-device session enforcement, XSS immunity, and CSRF protection.
  • Action (A):
    1. Stored short-lived (1-hour) Access Tokens and (7-day) Refresh Tokens in secure httpOnly, SameSite: Strict cookies.
    2. Implemented Token Rotation: Every refresh endpoint call invalidates the existing refresh token and issues a new pair, preventing replay attacks.
    3. Built Single-Session Enforcement: On login, stored active-session:{userId} -> sessionId in Redis. Auth middleware verifies that the incoming token's sessionId matches the active Redis key. Logging in on Device B instantly kicks out Device A.
    4. Added CSRF Double-Submit Verification: Generated a cryptographically random CSRF token stored in Redis and validated via custom request headers for mutating operations.
  • Result (R): Completely neutralized XSS token theft vectors, enforced instant remote session revocation, and eliminated CSRF vulnerabilities.

Story 3: PostgreSQL Schema, High-Volume Click Logging & Multi-Layer Rate Limiting

  • Situation (S): An unrestricted public URL shortener is vulnerable to brute-force slug enumeration, OTP email flooding, spam link generation, and database exhaustion from bot traffic.
  • Task (T): Protect infrastructure with multi-tier rate limiting and design an optimized database schema for high-volume click logging.
  • Action (A):
    1. Designed a normalized PostgreSQL schema with indexed B-Trees on Link.slug, Link.userId, and ClickEvent.linkId.
    2. Applied global IP throttling using express-rate-limit (1,000 req / 15 min).
    3. Enforced an atomic Redis sliding cooldown (60s) on OTP generation to prevent email exhaustion attacks.
    4. Capped OTP verification attempts to 5 max before deleting the OTP key from Redis.
  • Result (R): Protected server infrastructure against DoS and brute-force attacks while maintaining fast aggregation query performance on analytics dashboards.

8. Deep Technical & Concurrency Mechanics (Juspay / High-Bar Focus)

1. The Cache-Aside Pattern & Consistency:

[Incoming Slug Request] β”‚ β–Ό [Check Redis: GET slug:{slug}] β”œβ”€β”€ Hit ──► Return cached long URL (Latency: ~1-2ms) └── Miss ──► Query PostgreSQL β”œβ”€β”€ Found ──► Write to Redis (SETEX 86400s) ──► Return URL └── Not Found ──► Return HTTP 404
  • Write Path Invalidation: When a user updates, deletes, toggles, or changes a link's password, we explicitly execute redisClient.del("slug:" + slug). This prevents stale redirect loops.

2. Base62 Slug Generation vs Custom Slug Collisions:

  • Standard slugs use Base62 characters [a-zA-Z0-9] ($26 + 26 + 10 = 62$ characters).
  • A 6-character slug yields $62^6 = 56,800,235,584$ (56.8 billion) unique URLs.
  • Concurrency Collision Handling:
    • If two users attempt to claim the custom slug "juspay" at the exact same millisecond, both requests reach PostgreSQL.
    • PostgreSQL's unique constraint on slug (CREATE UNIQUE INDEX ON "Link"("slug")) serializes the transactions.
    • Exactly one insert commits; the second triggers Prisma error P2002, caught by our controller and returned as 409 Conflict: "Slug already in use".

3. Non-Blocking I/O vs Synchronous Awaiting:

// Inside Link Controller: res.redirect(link.originalUrl); // Sends HTTP 302 immediately to browser // Asynchronously logged - event loop continues without holding HTTP socket: trackClick({ linkId: link.id, ip: req.ip, userAgent: req.headers["user-agent"], referrer: req.headers["referer"] }).catch(err => console.error("Click tracking error:", err));

4. Rate Limiting Algorithms Compared:

AlgorithmMechanismUsed in Snips?
Fixed Window CounterResets counter at fixed clock intervals (e.g. top of minute). Vulnerable to burst at window boundaries.Global Express rate limiter
Sliding Window Log / KeyRedis key with atomic TTL (e.g., SET resend-otp:{ip}:{email} 1 EX 60 NX). Guarantees strict cooldown.OTP Resend Rate Limiter
Token BucketTokens refill at steady rate; allows controlled bursts.Recommended for 100x Scale API Gateways

9. Scaling, Bottlenecks & 10x/100x Evolution

Current System Capacity (1x):

  • Throughput: ~1,000–1,500 redirect RPS (bottlenecked by single Redis/Express instance).
  • Database Reads: ~95% cached in Redis.
  • Database Writes: Direct async Prisma inserts.

Bottlenecks at 10x Scale & Solutions:

  1. Third-Party Geo-IP API Latency (ip-api.com):
    • Problem: Free tier has a 45 req/min rate limit and adds network latency.
    • Fix: Switch to an in-memory, self-hosted MaxMind GeoLite2 MMDB binary database ($<0.1\text{ms}$ lookup, zero external network calls).
  2. Database Connection Saturation:
    • Problem: High concurrency exhausts PostgreSQL connection limits.
    • Fix: Place PgBouncer in transaction pooling mode in front of PostgreSQL.

Bottlenecks at 100x Scale & Distributed Architecture:

  1. Click Logging Queue (BullMQ / Apache Kafka):
    • Instead of fire-and-forget in Node.js memory (which risks data loss if a server pod restarts), publish click events to a Kafka topic or BullMQ Redis Queue. Dedicated worker pods consume the stream and batch-insert 500 rows at a time into the database.
  2. Time-Series Analytics Engine (ClickHouse / TimescaleDB):
    • PostgreSQL B-Trees slow down when ClickEvent reaches 100M+ rows. Migrate click logs to ClickHouse, a columnar database purpose-built for real-time aggregations (group by country, date, device in milliseconds).
  3. Global Edge Redirects (Cloudflare Workers):
    • Deploy slug redirects to Cloudflare CDN edge nodes with KV storage. Redirects resolve in $<10\text{ms}$ globally without ever hitting origin servers.

10. Engineering Trade-offs & What You Would Do Differently

1. Non-Blocking Async Ingestion vs Message Queue

  • Chosen: Non-blocking in-process promise (trackClick().catch(...)).
  • Trade-off: Zero additional message broker infrastructure to manage, but unhandled process crashes before DB commit could drop in-flight click logs.

2. Hybrid JWT + Redis Session vs Pure Stateful Sessions

  • Chosen: JWT in httpOnly cookies verified locally + session ID checked against Redis.
  • Trade-off: Fast token verification with the capability for instant remote revocation, but requires a Redis read on protected dashboard routes.

3. What I Would Do Differently If Rebuilding from Scratch:

  1. Local Geo-IP Database: Integrate MaxMind GeoLite2 locally from Day 1 rather than relying on an external REST API.
  2. Dedicated Worker for Analytics: Use BullMQ to buffer click writes with retry semantics.
  3. Short Slug Auto-Generation at Scale: Pre-generate Base62 slugs in batches in Redis Sets to eliminate slug generation logic at request time.

11. Cross-Questioning Masterclass (15+ Exact Q&A Scripts)

Q1: "Why did you choose PostgreSQL over MongoDB?"

Answer: "Our domain requires relational integrity and structured transactional reliability. Users own links, and links own click events. We need strict foreign key cascading, unique constraints on slugs, and ACID guarantees during link creation. PostgreSQL provides high-performance B-Tree indexes on slug and linkId. For high-volume writes, we offload reads to Redis and would introduce ClickHouse for time-series analytics at scale."


Q2: "How does your cache invalidation work when a user updates or deletes a link?"

Answer: "We follow the Cache-Aside pattern. On the read path, we query Redis first (slug:{slug}). On any write or mutationβ€”such as updating the destination URL, toggling active status, changing password, or deleting the linkβ€”our controller immediately executes redisClient.del('slug:' + slug). This ensures that subsequent redirect requests fetch fresh data from PostgreSQL and re-prime the cache."


Q3: "What happens if Redis crashes? Does the entire redirect system fail?"

Answer: "No. We wrap our Redis calls in fallback try-catch blocks. If Redis is down or unreachable, the application logs the cache error and gracefully falls back to querying PostgreSQL directly. The redirect continues to function, though at database query latency (~10–15ms instead of 1ms). When Redis recovers, the cache begins repopulating automatically."


Q4: "How does JWT Token Rotation prevent replay attacks?"

Answer: "With Token Rotation, every time an access token expires and the client calls the refresh endpoint, the server invalidates the provided refresh token and issues a brand-new access/refresh token pair. If an attacker intercepts a stolen refresh token and tries to use it after the legitimate user has already refreshed, the server detects token reuse, rejects the request, and invalidates the entire session family in Redis."


Q5: "How did you implement single-session enforcement across devices?"

Answer: "We store an active-session:{userId} key in Redis containing the current valid sessionId. When a user logs in on Device B, we generate a new sessionId, overwrite the Redis key, and delete the old session data. When Device A makes an API call with its old JWT, the authentication middleware checks the JWT's sessionId against Redis. Finding a mismatch, it returns an HTTP 401 with 'Session expired: logged in from another device'."


Q6: "Why store JWTs in httpOnly cookies instead of localStorage?"

Answer: "localStorage is accessible by any JavaScript running on the page, making it completely vulnerable to Cross-Site Scripting (XSS) attacks where a malicious script steals the token. httpOnly cookies cannot be accessed or read by JavaScript, completely neutralizing token theft via XSS. We then pair httpOnly cookies with CSRF double-submit tokens to protect against Cross-Site Request Forgery."


Q7: "How do you calculate unique clicks without violating user privacy or GDPR?"

Answer: "We never store raw IP addresses in plain text. When a click occurs, we take the client IP address, concatenate it with a server-side secret salt, and compute a one-way SHA-256 hash. We store only the hashed string in the ClickEvent table. To count unique visitors, we run a COUNT(DISTINCT hashedIp) query. This gives exact unique visitor metrics without persisting Personally Identifiable Information (PII)."


Q8: "How does the Recharts analytics aggregation query work?"

Answer: "We execute indexed queries grouped by category. For categorical dimensions (device, browser, country, referrer), we run groupBy counts filtered on linkId. For time-series data (24-hour, 7-day, 30-day), we filter by timestamp >= startDate and bucket click counts into hourly or daily intervals, passing the resulting formatted JSON array directly into Recharts <AreaChart> and <BarChart> components."


Q9: "What is the time complexity of the redirect endpoint?"

Answer:

  • Cache Hit (95% of traffic): $O(1)$ constant time key-value lookup in Redis ($\approx 1\text{ms}$).
  • Cache Miss (5% of traffic): $O(\log N)$ B-Tree index lookup on Link.slug in PostgreSQL + $O(1)$ write to Redis.
  • Async Click Logging: $O(\log M)$ B-Tree insertion into ClickEvent + $O(1)$ atomic increment on Link.totalClicks."*

Q10: "How do you prevent slug enumeration or scraping attacks?"

Answer: "First, generated slugs are randomly distributed across a 62-character alphabet ($62^6 \approx 56.8\text{ billion}$ possibilities), making sequential enumeration mathematically impractical. Second, we apply IP-based rate limiting on redirect and API endpoints. Third, for sensitive internal links, users can enable password protection, requiring a valid bcrypt-verified password before redirecting."


Q11: "How do you handle link expiration?"

Answer: "Each link has an optional expiresAt timestamp in PostgreSQL. When resolving a slug, if expiresAt is present and new Date() > expiresAt, the server returns an HTTP 410 Gone / 'Link Expired' status and deletes the key from Redis. This lazy validation approach avoids needing periodic background cleanup cron jobs for redirects."


Q12: "Why use bcrypt for password hashing instead of SHA-256 or MD5?"

Answer: "SHA-256 and MD5 are fast cryptographic hash functions designed for data integrityβ€”making them vulnerable to rapid brute-force attacks via GPUs (billions of hashes/sec). Bcrypt is a slow, computationally intensive password-hashing algorithm with configurable cost factors (salt rounds). We use 10 salt rounds, which introduces deliberate CPU work and incorporates a random salt to completely defend against rainbow table attacks."


Q13: "How does your rate limiter handle distributed instances?"

Answer: "While express-rate-limit works for single-node instances in memory, our OTP and sensitive auth endpoints utilize Redis atomic operations (SET key value EX seconds NX and INCR). Because Redis is an external centralized in-memory store, rate-limit counters are shared consistently across all backend application instances behind a load balancer."


Q14: "What happens if two users try to create the same custom slug simultaneously?"

Answer: "Both requests reach PostgreSQL. Because the slug column has a unique database index, PostgreSQL enforces row serialization at the storage engine level. The first transaction commits and creates the link. The second transaction triggers a P2002 unique constraint violation. Our backend catches this specific Prisma error code and returns HTTP 409 Conflict with 'Custom slug is already taken'."


Q15: "If a celebrity posts a short link and it gets 50,000 clicks/sec, how will your architecture behave?"

Answer: "The initial click primes the Redis cache. All subsequent 49,999+ requests hit Redis in memory, returning the HTTP 302 redirect in $\approx 1\text{ms}$ without touching PostgreSQL.
To protect the database from the incoming 50,000 click analytics writes, in a production setting we would push click payloads to an in-memory Redis stream / Kafka topic, allowing background worker pods to batch-insert them into the database smoothly without overloading connection pools."


12. Provable Numbers & Quick Reference Sheet

Keep these exact architectural numbers ready:

ParameterExact ValueContext / Source
Access Token TTL1 Hour (expiresIn: '1h')generateToken.js / httpOnly cookie
Refresh Token TTL7 Days (expiresIn: '7d')generateToken.js / Token Rotation
Redis Slug Cache TTL24 Hours (86,400s)linkController.js Cache-Aside strategy
User Profile Cache TTL1 Hour (3,600s)Redis user auth cache
OTP Expiration5 Minutes (300s)Redis otp:{email} key TTL
Max OTP Attempts5 Attempts MaxBrute-force lockout before key deletion
OTP Resend Cooldown60 SecondsRedis atomic sliding key per IP+email
Global Rate Limit1,000 Req / 15 Min per IPexpress-rate-limit middleware
Slug Combinations$62^6 \approx 56.8\text{ Billion}$6-character Base62 alphabet
Database Schema3 Models (User, Link, ClickEvent)prisma/schema.prisma
Analytics Dimensions6 DimensionsCountry, City, Device, Browser, OS, Referrer
Redirect Response Time$<5\text{ms}$ on cache hitRedis memory lookup + HTTP 302

[!TIP] Pro-Tip for Interviews: When asked about Snips, emphasize the dual-phase optimization:

  1. Phase 1 (The User Experience): Sub-millisecond redirect using Redis Cache-Aside.
  2. Phase 2 (The Analytics Engine): Non-blocking asynchronous data capture with SHA-256 IP anonymization.
    This immediately demonstrates senior-level thinking about latency, user experience, and decoupled system design.