Why Build This
I have always been curious about what happens under the hood when you hit "Submit" on LeetCode. It looks simple from the outside — you send code, you get a judge. But behind that there is a surprisingly hard problem: how do you execute untrusted code safely, with resource limits, and return results fast?
I wanted to understand that end-to-end, not just read about it. So I built my own.
The Hard Part: Sandboxed Execution
The judge core is the most interesting piece. Each submission needs to compile and run inside an isolated environment where it cannot touch the host filesystem, eat all the memory, or run forever. I used Docker containers as the isolation boundary — each submission spins up a short-lived container with strict CPU and memory limits, runs the code against test cases, and reports back the result (AC, WA, TLE, MLE, RE).
Getting this right involved a few tricky decisions:
- Container lifecycle. Spinning up a fresh container per submission is safe but slow. I experimented with container pooling — pre-warming a set of containers and reusing them — to bring the turnaround time down without sacrificing isolation.
- Resource limits. Docker's cgroup integration handles memory and CPU caps, but you still need to handle edge cases like fork bombs and excessive disk writes at the application level.
- Language support. Each supported language (C++, Java, Python, Go) needs its own compile-and-run pipeline. The judge core abstracts this behind a common interface so adding a new language is just a config change plus a base image.
The judge core itself is written in Go — good fit for this kind of concurrent, I/O-heavy workload where you want fine-grained control over process management.
The Rest of the Stack
- Frontend: React. Problem list, code editor (Monaco - my favourite theme), submission history, real-time judge updates.
- Backend API: Go service handling user auth, problem CRUD, submission queue, and result delivery.
- Database: MySQL for problems, users, and submission records.
- Judge Core: Go + Docker. Runs as a separate service that pulls jobs from the submission queue, executes them in sandboxed containers, and writes back results.
The whole system is built from scratch — no existing judge framework underneath. That was the point: I wanted to understand every layer, from how the editor sends code to how the sandbox returns a judge.
What I Learned
Building an online judge is a good exercise in systems thinking. You deal with untrusted input, concurrency, resource management, and the tension between isolation and performance — all in one project. It also gave me a much deeper appreciation for how platforms like LeetCode and Codeforces work at scale. What I built handles a single-digit number of concurrent submissions; scaling that to thousands is a whole different problem.