Katkat

Find the Bugs in an AI-Generated URL Shortener

MicrosoftSoftware Engineer · Technical Screen

Last updated

Tests code review, debugging, and refactoring skills by identifying flaws in an AI-generated in-memory URL shortener and implementing a robust, thread-safe replacement.

Problem Statement

You are reviewing a teammate's pull request containing an AI-generated prototype for an in-memory URL shortener service. The prototype has multiple functional and non-functional flaws:

import hashlib

class BadURLShortener:
    def __init__(self):
        self.url_map = {}  # short -> long
        self.reverse_map = {}  # long -> short

    def shorten(self, original_url):
        if original_url in self.reverse_map:
            return self.reverse_map[original_url]
        
        # Generate 6-char hash token
        token = hashlib.md5(original_url.encode()).hexdigest()[:6]
        
        # Save mappings
        self.url_map[token] = original_url
        self.reverse_map[original_url] = token
        return "http://short.ly/" + token

    def resolve(self, short_url):
        token = short_url.split("/")[-1]
        return self.url_map[token]

Task

  1. Identify the bugs and design flaws in BadURLShortener (e.g., hash collisions, unhandled misses, lack of thread safety, and URL validation).
  2. Implement a production-grade class URLShortener that addresses these issues.

Requirements & Interface

class URLShortener:
    def __init__(self, base_url: str = "http://short.ly/"):
        pass

    def shorten(self, original_url: str) -> str:
        """Shortens a valid URL. Returns existing short URL if already registered.
        Raises ValueError if original_url is empty or not a valid HTTP/HTTPS URL.
        """
        pass

    def resolve(self, short_url: str) -> str | None:
        """Resolves a short URL to the original URL. Returns None if not found."""
        pass

Constraints

  • Generated short tokens must be alphanumeric strings between 6 and 8 characters.
  • Hash collisions must be handled cleanly without overwriting existing mappings.
  • The class must be thread-safe for concurrent calls to shorten and resolve.
  • Valid URLs must start with http:// or https:// and have a non-empty host.

Example

shortener = URLShortener()

# Shorten new URL
s1 = shortener.shorten("https://www.example.com/very/long/path")
# e.g., "http://short.ly/aB3x9z"

# Idempotency check
s2 = shortener.shorten("https://www.example.com/very/long/path")
assert s1 == s2

# Resolution
assert shortener.resolve(s1) == "https://www.example.com/very/long/path"
assert shortener.resolve("http://short.ly/nonexistent") is None

Hints

Hint 1

Look at what happens when two different original URLs produce the same 6-character prefix under MD5: BadURLShortener blindly overwrites the existing entry.

Hint 2

Notice the missing error handling in resolve: accessing self.url_map[token] directly raises an unhandled KeyError instead of returning a predictable result or None.

Hint 3

Consider multi-threaded environments: Python dictionaries are individually thread-safe for single atomic operations, but read-then-write check sequences create race conditions without explicit locking.

Follow-up questions

  • How would you support URL expiration (TTL) without degrading lookup performance?
  • If this service moves from single-process memory to a distributed system, how would you generate non-colliding IDs across multiple worker nodes?
  • What counter-strategies would you use to prevent denial-of-service via memory exhaustion if malicious clients submit billions of unique junk URLs?