System Design Questions: Practice Problems and Solutions
System Design
Interview Prep

System Design Questions: Practice Problems and Solutions

S

Shivam Chauhan

28 days ago

System design interviews can feel like navigating a maze. I remember when I first started, I'd get hit with a broad question and freeze up. "Design a social media platform," they'd say, and my mind would go blank. I've been there.

But here's the thing: with the right approach and practice, you can tackle these challenges with confidence. I learned to break down complex problems into smaller, manageable parts. Today, I want to share some practice problems and solutions to help you prepare for your next system design interview. If you’re looking to sharpen your skills, this post is for you. Let’s break down these questions.


Why System Design Matters

System design is more than just knowing the latest tech. It’s about understanding how different components work together to build a scalable, reliable, and efficient system. In interviews, it’s a way for companies to gauge your ability to think critically and solve real-world problems.

I've seen candidates with impressive resumes stumble because they couldn't articulate their design decisions or explain the trade-offs involved. That's why practice is key.


Problem 1: Design a URL Shortener

Problem Statement: Design a system that takes long URLs and shortens them, like bit.ly. Consider scalability, performance, and data storage.

Solution Approach:

  1. Requirements:

    • Shorten URLs.
    • Redirect short URLs to original URLs.
    • Handle a large number of requests.
    • Ensure uniqueness of short URLs.
  2. High-Level Design:

    • Use a web server to receive requests.
    • Employ an application server to process requests.
    • Store URL mappings in a database.
  3. Detailed Design:

    • URL Encoding: Use a base-62 encoding (a-z, A-Z, 0-9) to generate short URLs.
    • Database: Store mappings in a key-value store like Cassandra or a relational database like MySQL.
    • Caching: Implement a caching layer (e.g., Redis) to quickly retrieve frequently accessed URLs.
  4. Scalability:

    • Load balancing to distribute traffic.
    • Database sharding to handle large datasets.
    • Content Delivery Network (CDN) to serve static content.
  5. Example Code (Java):

java
public class URLShortener {
    private Map<String, String> urlMap = new HashMap<>();
    private int counter = 0;

    public String shortenURL(String longURL) {
        String shortURL = base62Encode(counter++);
        urlMap.put(shortURL, longURL);
        return shortURL;
    }

    public String getLongURL(String shortURL) {
        return urlMap.get(shortURL);
    }

    private String base62Encode(int value) {
        StringBuilder sb = new StringBuilder();
        String base62Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        while (value > 0) {
            sb.append(base62Chars.charAt(value % 62));
            value /= 62;
        }
        return sb.reverse().toString();
    }
}

Why This Works: This approach covers the core requirements while addressing scalability and performance concerns. The Java example illustrates how to encode URLs, but real-world implementations would involve more robust error handling and security measures.


Problem 2: Design a Rate Limiter

Problem Statement: Design a rate limiter to control the number of requests a user can make to an API within a given time period.

Solution Approach:

  1. Requirements:

    • Limit requests per user.
    • Handle a large number of users.
    • Support different rate limits for different APIs.
  2. High-Level Design:

    • Use a middleware to intercept requests.
    • Maintain request counts for each user.
    • Block requests exceeding the limit.
  3. Detailed Design:

    • Token Bucket Algorithm: Each user has a bucket that holds tokens. Each request consumes a token. Tokens are replenished at a fixed rate.
    • Leaky Bucket Algorithm: Requests are added to a queue (bucket). Requests are processed at a fixed rate.
    • Data Storage: Use Redis to store user-specific request counts and timestamps.
  4. Scalability:

    • Distributed rate limiting using a shared cache.
    • Consistent hashing to distribute users across multiple rate limiter instances.
  5. Example Code (Java):

java
public class RateLimiter {
    private Map<String, Integer> requestCounts = new HashMap<>();
    private int maxRequestsPerSecond;

    public RateLimiter(int maxRequestsPerSecond) {
        this.maxRequestsPerSecond = maxRequestsPerSecond;
    }

    public boolean allowRequest(String userID) {
        int currentCount = requestCounts.getOrDefault(userID, 0);
        if (currentCount < maxRequestsPerSecond) {
            requestCounts.put(userID, currentCount + 1);
            return true;
        } else {
            return false;
        }
    }
}

Why This Works: The rate limiter ensures that users don't abuse the system by making too many requests. The Java example shows a basic implementation, but a production system would need to handle concurrency and distributed environments.


Problem 3: Design a Message Queue

Problem Statement: Design a message queue system that allows different services to communicate asynchronously.

Solution Approach:

  1. Requirements:

    • Asynchronous message delivery.
    • Support multiple producers and consumers.
    • Ensure message durability.
    • Handle message ordering.
  2. High-Level Design:

    • Use a message broker to manage queues and messages.
    • Producers send messages to queues.
    • Consumers subscribe to queues and receive messages.
  3. Detailed Design:

    • Message Broker: Use RabbitMQ or Apache Kafka.
    • Queues: Store messages in queues until consumers are ready to process them.
    • Message Durability: Persist messages to disk to prevent data loss.
    • Message Ordering: Use ordered queues or sequence numbers to maintain message order.
  4. Scalability:

    • Horizontal scaling of message brokers.
    • Partitioning queues across multiple brokers.
  5. Example Code (Conceptual):

java
// Producer
public interface MessageProducer {
    void sendMessage(String queueName, String message);
}

// Consumer
public interface MessageConsumer {
    String receiveMessage(String queueName);
}

Why This Works: A message queue decouples services, allowing them to operate independently. RabbitMQ and Kafka are popular choices for implementing message queues in real-world systems. For deeper insights into messaging systems, check out resources on amazon mq rabbitmq or similar technologies.


FAQs

Q1: How do I approach a system design question? Start by clarifying requirements, then outline a high-level design, followed by detailed design and scalability considerations. Communicate your thought process clearly.

Q2: What are the key areas to focus on? Scalability, reliability, performance, and security are crucial. Understand the trade-offs involved in different design decisions.

Q3: How does Coudo AI help with system design preparation? Coudo AI offers a range of system design interview preparation problems and machine coding challenges to help you practice and refine your skills.


Wrapping Up

System design interviews require a blend of theoretical knowledge and practical experience. By practicing with these problems and solutions, you'll be better prepared to tackle complex design challenges. For more practice, check out Coudo AI for real-world scenarios and AI-driven feedback. Remember, continuous practice and learning are the keys to mastering system design interviews. Good luck, and keep pushing forward!

About the Author

S

Shivam Chauhan

Sharing insights about system design and coding practices.