System Design

The System Design Interview Framework: Designing URL Shorteners and Chat Apps

System design interviews can feel overwhelming, but they follow a predictable pattern. Success hinges not on knowing every technology, but on structuring your thinking clearly. This post outlines a robust framework you can apply to classic problems like URL shorteners and real-time chat applications, helping you demonstrate architectural maturity and depth.

Step 1: Clarify Requirements and Scope

Never jump into architecture. Start by defining the problem boundaries. For a URL shortener, ask: Do we need analytics? What is the expected traffic? Is the link expiration time critical? For a chat app, clarify: Is it 1-on-1 or group-based? Do we need offline support or read receipts? Quantitative constraints are crucial. Assume the URL shortener needs to handle 100 million writes per day and 1 billion reads per day. For the chat app, assume 500,000 daily active users with an average of 50 messages per user.

Step 2: High-Level Architecture Design

Sketch the core components. Both systems typically include a Client, Load Balancer, Application Servers, Data Layer, and possibly a Cache.

URL Shortener Architecture

The core logic is simple: mapping a long URL to a unique short code. On write, generate a unique ID and store the mapping. On read, look up the ID and redirect. A critical decision is the ID generation strategy. Using a database auto-increment is simple but can become a bottleneck. A better approach is using a distributed ID generator or hashing. Consider base62 encoding to maximize the number of possible short URLs within a 7-character limit.

// Pseudocode for Short URL Generation
function generateShortCode(longUrl) {
    // Option 1: Database Sequence
    id = db.get_next_id();
    
    // Option 2: Hash-based (with collision check)
    hash = md5(longUrl).substring(0, 7);
    if (db.exists(hash)) {
        handle_collision(hash);
    }
    
    base62_code = to_base62(id);
    return base62_code;
}

Chat App Architecture

Chat is real-time, requiring WebSocket connections or long polling. The architecture must handle ephemeral state (online status) and persistent state (messages). A Message Queue (Kafka, RabbitMQ) is essential for decoupling message ingestion from delivery. The application server should not directly hold WebSocket connections to the database; instead, it should persist messages to a NoSQL store (like Cassandra or DynamoDB) for durability and use a separate service for real-time push.

Step 3: Data Model and Storage Selection

Data models must match access patterns.

URL Shortener Data Model

A relational database (MySQL) is often sufficient for the mapping table if read/write ratios are balanced, but the lookup is the hottest path. Use Redis for caching hot short codes. The table structure is simple: short_code (PK), long_url, created_at, creator_id. Partitioning by short_code hash ensures even distribution across nodes.

Chat App Data Model

Chat messages are append-only. A wide-column store like Cassandra is ideal for high-throughput writes. Design the table to allow efficient retrieval of recent messages for a conversation.

CREATE TABLE messages (
    conversation_id uuid,
    message_timestamp timeuuid,
    sender_id uuid,
    content text,
    PRIMARY KEY (conversation_id, message_timestamp)
) WITH CLUSTERING ORDER BY (message_timestamp DESC);

Step 4: Scalability and Bottlenecks

Identify where your design breaks.

Scaling URL Shorteners

Reads are typically 10-100x more frequent than writes. Caching is mandatory. Implement a multi-tier cache: L1 in-memory (Caffeine) in the app server, L2 distributed (Redis). For hot keys, use local caching to reduce network latency. If the ID generator becomes a bottleneck, move to a distributed sequence generator that pre-allocates ID ranges to application servers.

Scaling Chat Apps

The bottleneck is the WebSocket layer. Application servers holding socket connections are stateful. Use a connection layer that supports horizontal scaling, possibly with a sticky session load balancer or a stateful gateway service. For message fan-out in group chats, use a Pub/Sub system (like Kafka) where each member of a group subscribes to their own topic or partition. This ensures each user receives their messages without blocking others.

Step 5: Trade-offs and Edge Cases

Demonstrate depth by discussing trade-offs. For URL shorteners, discuss the trade-off between uniqueness guarantee and performance. A hash might collide; a sequence is unique but centralized. For chat, discuss the trade-off between consistency and availability. In a chat app, losing a message is bad, so prioritize durability. However, for presence updates (online/offline), strong consistency is not needed; eventual consistency via a TTL-based cache is acceptable and more performant.

Conclusion

System design interviews are about structured problem-solving, not memorization. By following this framework—clarifying requirements, designing high-level architecture, choosing the right data model, identifying scalability bottlenecks, and discussing trade-offs—you can confidently navigate complex design questions. Practice applying this methodology to both URL shorteners and chat apps, and you’ll develop the intuition to handle any system design challenge.

Share: