Designing LeetCode - A System Design Tutorial for Beginners
Have you ever wondered how platforms like LeetCode work behind the scenes? How do they handle millions of code submissions, execute them safely, and manage live programming contests? In this comprehensive tutorial, we'll walk through designing a complete LeetCode-like system from scratch.
What you'll learn:
Don't worry if you're new to system design β we'll explain everything step by step!
LeetCode is an online platform where programmers can:
Now, let's design our own version!
Before writing any code or drawing diagrams, we need to understand exactly what our system should do. This is called requirements gathering, and it's the foundation of good system design.
π‘ Pro Tip: Always start with requirements! They guide every decision you'll make in your design.
The API defines how clients communicate with our system. Think of it as the "menu" of actions users can perform.
# Get a single problem
GET /problems/{problemId}
# Get list of problems (with pagination)
GET /problems?offset=0&limit=50
# Submit code for evaluation
POST /submit/{problemId}
{
"code": "def solution(nums): ...",
"language": "python"
}
# Get contest leaderboard
GET /contest/{contestId}/leaderboard?offset=0&limit=50
Our system needs to store several types of data. Let's identify the main entities:
π― Quick Note: We don't need to design every table field right now. Focus on understanding the relationships between entities.
Now comes the exciting part β designing the actual system! Let's start simple and add complexity as needed.
[Client] β [Web Server] β [Database]
This handles basic operations like viewing problems. The web server processes requests and fetches data from the database.
We can't just run user code directly on our web servers β that would be a massive security risk! Imagine if someone submitted code that tried to:
[Client] β [Web Server] β [Message Queue] β [Code Execution Workers]
β
[Database]
How it works:
A message queue acts like a buffer between your web server and code execution workers. Benefits include:
Popular message queues: Apache Kafka, RabbitMQ, Amazon SQS
This is one of the most critical parts of our system. Let's explore different approaches to safely execute untrusted code.
Docker gives us the best mix of isolation, speed, and cost.
Container Configuration:
How do we handle thousands of users watching leaderboards?
SELECT user_id, SUM(points) as total_score
FROM submissions
WHERE contest_id = 'current_contest' AND status = 'accepted'
GROUP BY user_id
ORDER BY total_score DESC;
Problems:
Redis offers in-memory Sorted Sets that auto-maintain order.
Benefits:
// Add user score
redis.zadd('contest_123_leaderboard', user_score, user_id);
// Get top 10
redis.zrevrange('contest_123_leaderboard', 0, 9, 'WITHSCORES');
Client-side polling every 10 seconds works well:
Bonus: Use for true real-time but requires more infra
If Redis update fails but DB write succeeds:
Run user code against multiple test cases:
Approach:
Example Test Case File:
# Input
[1, 2, 3, 4, 5]
# Expected Output
15
# Input
[10, 20, 30]
# Expected Output
60
βββββββββββββββ ββββββββββββββββ βββββββββββββββ
β Clients β β β Load Balancerβ β β Web Servers β
βββββββββββββββ ββββββββββββββββ βββββββββββββββ
β
βββββββββββββββββββββΌββββββββββββββββββββ
β β β
βββββββββββββ βββββββββββββββββββ ββββββββββββ
β Database β β Message Queue β β Redis β
βββββββββββββ βββββββββββββββββββ ββββββββββββ
β
βββββββββββββββββββ
β Code Execution β
β Workers β
β (Docker) β
βββββββββββββββββββ
Weβve gone from basic requirements to a scalable, secure architecture ready for production. The key principles:
System design can feel overwhelming, but step-by-step planning makes it approachable.
Happy designing! π
Want to practice system design? Try designing other popular systems like Twitter, Netflix, or Uber.
The principles you learned here apply everywhere!