LLD Hub
lldhashinganalyticsoop

How to Design a URL Shortener (bit.ly) | LLD Interview Guide

Design a URL shortening service with Base62 encoding, custom aliases, TTL, and click analytics. Common in Google, Microsoft, and startup LLD rounds.

5 April 2025·7 min read

Practice this problem

URL Shortener (bit.ly) — get AI-scored feedback on your solution

Solve it →

The URL Shortener (like bit.ly or TinyURL) is a classic LLD problem that tests your understanding of encoding schemes, collision handling, caching, and analytics. It's commonly asked at Google, Microsoft, Amazon, and startups. Here's a complete design walkthrough.

Core Requirements

  • Shorten a long URL to a 6-8 character alias
  • Redirect short URL to original URL
  • Custom alias support
  • URL expiration (TTL)
  • Click analytics: total clicks, unique visitors, geographic data
  • User accounts to manage URLs

Core Entities

  • URL — original URL, short code, userId, createdAt, expiresAt
  • ShortCodeGenerator — produces unique 6-char codes
  • ClickEvent — timestamp, IP, userAgent, referrer
  • Analytics — aggregated click stats per URL
  • User — owns URLs, has quota limits

Short Code Generation

class Base62Generator implements CodeGenerationStrategy {
  private chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  generate(url: string): string {
    // MD5 hash → take first 8 chars → Base62 encode
    const hash = md5(url).substring(0, 8);
    return this.toBase62(parseInt(hash, 16));
  }

  private toBase62(num: number): string {
    let result = "";
    while (num > 0) {
      result = this.chars[num % 62] + result;
      num = Math.floor(num / 62);
    }
    return result.padStart(6, "0");
  }
}

Collision Handling

Two different URLs can hash to the same code. Always check and retry with a salt:

class URLShortenerService {
  shorten(originalUrl: string, userId: string): string {
    let code = this.generator.generate(originalUrl);
    let attempt = 0;
    while (this.urlRepo.existsByCode(code)) {
      code = this.generator.generate(originalUrl + attempt++);
    }
    this.urlRepo.save({ code, originalUrl, userId });
    return code;
  }
}

Analytics with Observer

class RedirectService {
  redirect(shortCode: string, context: RequestContext): string {
    const url = this.urlRepo.findByCode(shortCode);
    if (!url || url.isExpired()) throw new NotFoundException();
    // Fire analytics event asynchronously
    this.eventBus.publish(new ClickEvent(shortCode, context));
    return url.originalUrl;
  }
}

class AnalyticsService implements EventHandler<ClickEvent> {
  handle(event: ClickEvent) {
    this.analyticsRepo.incrementClicks(event.shortCode);
    this.analyticsRepo.recordVisitor(event.shortCode, event.ip);
  }
}

Common Questions

  • "How do you handle expired URLs?" → Check expiry on redirect, return 410 Gone
  • "How do you make redirects fast?" → Cache hot URLs in Redis with TTL
  • "Same URL for same user?" → Hash (userId + originalUrl) to generate code
  • "Custom aliases?" → Let user provide code, check availability first

Ready to practice?

Submit your solution and get AI-scored feedback on OOP, SOLID principles, design patterns, and code quality.

Solve URL Shortener (bit.ly)