← Projects
Case Study

CodeSprint

A competitive-programming platform that coaches you, instead of just grading you.

Status
Live
Updated
July 10, 2026
NestJSNext.jsPostgreSQL + pgvectorMongoDBRedis · BullJudge0LangChain · GeminiSocket.io
Live ↗GitHub ↗

Problem

Grinding problems on a judge tells you whether you got one right. It doesn't tell you what to learn next, whether you're actually improving, or where your blind spots are. Most platforms are graders. What a learner needs is a coach: something that diagnoses your level, tells you what to work on, and proves you got better.

CodeSprint is built around that difference. It diagnoses skill, generates a personalized roadmap, tracks growth through retesting, and surfaces global contests for one-click application. The engineering challenge: make code judging fast and reliable at scale, and turn every submission into a signal that updates the learner's roadmap.

Architecture

The system is a modular NestJS API behind a Next.js client, with the heavy lifting — judging — pushed off the request path onto a Redis-backed queue.

Next.js Clientdashboard · editorNestJS APIauth · problem · submission · roadmap · ragGemini · LangChainhints · RAG · post-submitBull workersubmissions queue → Judge0Judge0sandboxed executionPostgreSQL+ pgvectorMongoDBRedisBull · cacheSocket.iolive verdicts
CodeSprint high-level architecture: a Next.js client over a modular NestJS API, backed by Postgres/pgvector, Mongo, and a Redis-backed Bull queue, with Judge0 for execution and Gemini + RAG for coaching.

The most important flow is what happens when you hit Submit. The controller doesn't run your code; it enqueues a job and returns immediately. A Bull worker drains the submissions queue, executes against Judge0, persists the result, and pushes the verdict back to the browser over Socket.io — so the UI updates live without polling.

ControllerPOST /submitSubmissionServiceenqueue jobBull queueRedisProcessorRunnerFactoryJudge0bundled testsDB + Socket.iopersist + push
CodeSprint submission lifecycle — the controller returns immediately; a Bull worker drains the queue and streams results back over Socket.io.

Around that spine sit the coaching pieces:

  • Roadmap & mastery — a mastery aggregator consumes every submission result and updates per-topic stats, which drives the personalized roadmap and retesting.
  • RAG + Gemini — embeddings stored in Postgres via pgvector power retrieval for contextual hints and post-submission analysis, generated with Gemini through LangChain.
  • Auth — JWT with Google/GitHub OAuth2 via Passport.

Key decisions & trade-offs

One bundled Judge0 submission per problem — not one per test case. All test cases are sent as a single JSON payload over stdin, and a language-specific runner template does the comparison internally and returns a JSON array of per-test results. This cuts judge API calls from N (one per test) to one per submission, which dominates latency at scale. The trade-off: per-test timing is coarser, and the runner template carries more responsibility — but for a coaching platform, throughput and cost mattered more than microsecond-accurate per-case timing.

Judging on a queue, not inline. Compilation and execution are slow, bursty, and fail in transient ways (timeouts, rate limits). A Bull queue with retries and backoff turns a flaky external dependency into a non-event, and lets the API stay responsive under a burst of submissions. The cost is eventual-consistency in the UI — solved by streaming verdicts over Socket.io.

pgvector in the same Postgres, not a separate vector database. RAG retrieval lives in the database I already run. One datastore, one backup story, one connection pool. A dedicated vector DB would scale further, but at this stage the operational simplicity wins.

Gemini Flash for coaching. Hints and post-submission analysis are latency-sensitive and high-volume, so a fast, low-cost model tier is the right call over a frontier model.

Outcome

CodeSprint is live at code-sprint.com. The full loop works end to end: submit → queued judging → live verdict → mastery update → roadmap adjustment. The queue-based judging pipeline is the piece I'd reach for again — it's the same shape that holds up under production load in the banking systems I work on by day.