<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Feroz</title>
    <description>The latest articles on DEV Community by Feroz (@phero20).</description>
    <link>https://dev.to/phero20</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3839629%2F323ed6dc-f097-4631-98e4-c669b2f0627f.jpeg</url>
      <title>DEV Community: Feroz</title>
      <link>https://dev.to/phero20</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phero20"/>
    <language>en</language>
    <item>
      <title>I Built the Ultimate All-in-One Platform for Software Engineers — Here's the Full Architecture</title>
      <dc:creator>Feroz</dc:creator>
      <pubDate>Wed, 08 Jul 2026 14:36:16 +0000</pubDate>
      <link>https://dev.to/phero20/i-built-the-ultimate-all-in-one-platform-for-software-engineers-heres-the-full-architecture-5ce3</link>
      <guid>https://dev.to/phero20/i-built-the-ultimate-all-in-one-platform-for-software-engineers-heres-the-full-architecture-5ce3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explore the Platform:&lt;/strong&gt; &lt;a href="https://slavecode.codes" rel="noopener noreferrer"&gt;slavecode.codes&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Support on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/phero20" rel="noopener noreferrer"&gt;github.com/phero20&lt;/a&gt; &lt;em&gt;(Give it a star)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Your code is your master. Serve it well."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the philosophy I built this around. Not just a tagline — a design constraint. Every system decision in SlaveCode comes back to that principle: the code should be judged fairly, the infrastructure should serve users well, and nothing should break under pressure.&lt;/p&gt;

&lt;p&gt;SlaveCode is a competitive programming platform I built from the ground up. It is not a weekend project. It is a full production system with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;11,000+ coding problems&lt;/strong&gt; across multiple difficulty levels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;82+ language learning tracks&lt;/strong&gt; in the Academy (from Go to Haskell to Gleam)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time multiplayer Arena&lt;/strong&gt; for up to 50 players simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;460+ company interview profiles&lt;/strong&gt; (Google, Meta, Amazon, Apple, and more)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;56-topic System Design curriculum&lt;/strong&gt; with an AI-powered diagram workspace&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;global contest aggregator&lt;/strong&gt; pulling from LeetCode, Codeforces, AtCoder, CodeChef&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;compiler playground&lt;/strong&gt; available to guests with zero login required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is a full architecture walkthrough. Not "here's what I used" — but &lt;em&gt;why&lt;/em&gt; every decision was made, what breaks if you get it wrong, and how it all fits together.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Big Picture: A Multi-Cloud Architecture
&lt;/h2&gt;

&lt;p&gt;Before diving into any single system, it's worth understanding the physical topology first. Where things run matters as much as how they run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────────┐
│                        VERCEL EDGE NETWORK                           │
│  Next.js 15 (App Router) — SSR + Static + CDN-distributed assets    │
└───────────────────┬───────────────────────────────┬─────────────────┘
                    │ HTTPS REST                     │ WSS WebSockets
                    ▼                                ▼
┌────────────────────────────────────────────────────────────────────┐
│                   GOOGLE CLOUD PLATFORM (Compute Engine VMs)        │
│                                                                      │
│   ┌──────────────────┐   ┌───────────────────┐   ┌─────────────┐   │
│   │  Hono API Server │   │  Golang Arena Hub │   │ BullMQ      │   │
│   │  (Bun runtime)   │   │  (WebSocket mgr)  │   │ Workers     │   │
│   └──────────────────┘   └───────────────────┘   └─────────────┘   │
└──────────┬─────────────────────────────────────────────┬────────────┘
           │ Redis Protocol                              │ HTTP POST
           ▼                                             ▼
┌──────────────────────────┐          ┌─────────────────────────────┐
│  MANAGED DBaaS           │          │  MICROSOFT AZURE (VMs)      │
│  ┌──────────────────────┐│          │  ┌─────────────────────────┐│
│  │ Neon (Postgres)      ││          │  │ Judge0 Code Sandbox     ││
│  │ MongoDB Atlas        ││          │  │ (Dockerized, isolated)  ││
│  │ Aiven Valkey (Redis) ││          │  └─────────────────────────┘│
│  └──────────────────────┘│          └─────────────────────────────┘
└──────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three intentional multi-cloud decisions here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why GCP for the API and Arena?&lt;/strong&gt; Raw Compute Engine VMs give persistent connections, no cold starts, and no 10-second serverless timeout that would kill a long-running BullMQ worker mid-job. Vercel serverless functions cannot hold a BullMQ consumer — period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Azure for Judge0?&lt;/strong&gt; This is the most important security decision in the entire platform. The code execution sandbox runs on an entirely separate cloud provider (Azure), isolated from the primary databases (GCP). If a sophisticated attacker crafts a zero-day Docker escape exploit and compromises the Judge0 VM, they land on Azure — not inside the same VPC as the API, Postgres, or MongoDB. The "blast radius" is physically separated across two clouds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Vercel for the frontend?&lt;/strong&gt; Edge CDN distribution, automatic CI/CD on push, SSR support, and zero infrastructure management for static assets. It offloads a dimension of complexity I do not need to operate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Next.js 15 (App Router), React, TailwindCSS, Zustand&lt;/td&gt;
&lt;td&gt;App Router enables co-located server components for SEO + client islands for interactivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Editor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monaco Editor&lt;/td&gt;
&lt;td&gt;Same engine as VS Code — mature, syntax-aware, extensible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Whiteboard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tldraw (infinite canvas)&lt;/td&gt;
&lt;td&gt;Best-in-class React-native infinite canvas for System Design diagrams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;REST API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hono on Bun&lt;/td&gt;
&lt;td&gt;Hono is 2-3× faster than Express on Bun. Bun's native speed matters for hot paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Awilix&lt;/td&gt;
&lt;td&gt;Enables clean service-layer architecture at scale without tight coupling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiplayer Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Golang (Fiber, WebSockets)&lt;/td&gt;
&lt;td&gt;Go goroutines handle tens of thousands of persistent WebSocket connections with far less overhead than Node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background Jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BullMQ (Bun Workers)&lt;/td&gt;
&lt;td&gt;Reliable Redis-backed queue with retries, delays, and priority scheduling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Relational DB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL (Neon serverless)&lt;/td&gt;
&lt;td&gt;Serverless Postgres — scales to zero, no idle VM cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Document DB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MongoDB Atlas (Mongoose)&lt;/td&gt;
&lt;td&gt;Flexible schema for problems, test cases, submissions — highly nested JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-Memory Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aiven Valkey (Redis fork)&lt;/td&gt;
&lt;td&gt;Open-source Redis drop-in, handles cache + queue + pub/sub + arena state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clerk&lt;/td&gt;
&lt;td&gt;Cryptographic JWT edge verification, OAuth, webhook user sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Judge0 (primary) + Wandbox (fallback)&lt;/td&gt;
&lt;td&gt;Dockerized sandbox that compiles and runs untrusted code safely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unified AI proxy (multiple providers)&lt;/td&gt;
&lt;td&gt;Key rotation, load balancing, and automatic provider failover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ORM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Drizzle ORM (Postgres) + Mongoose (MongoDB)&lt;/td&gt;
&lt;td&gt;Type-safe SQL generation for Postgres; Mongoose for Mongo document modeling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloudinary&lt;/td&gt;
&lt;td&gt;Bug report screenshots, user avatars&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Database Strategy: Why Three Databases?
&lt;/h2&gt;

&lt;p&gt;This is the question I get most often. The answer is that the three databases own fundamentally different shapes of data, and forcing one data model onto all three use cases would be an architectural mistake.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL (Neon) — The Social Graph &amp;amp; Analytics Engine
&lt;/h3&gt;

&lt;p&gt;Postgres owns everything that requires &lt;strong&gt;joins, aggregations, and referential integrity&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- A simplified view of what Postgres owns&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;           &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;user_stats&lt;/span&gt;         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;           &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;user_activity&lt;/span&gt;      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;daily&lt;/span&gt; &lt;span class="n"&gt;heatmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;           &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;user_solved_problems&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Mongo&lt;/span&gt; &lt;span class="n"&gt;IDs&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="n"&gt;FK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;           &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;follows&lt;/span&gt;            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;referential&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;           &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;solutions&lt;/span&gt;          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;community&lt;/span&gt; &lt;span class="n"&gt;editorial&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;categories&lt;/span&gt;      &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;category_problems&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hierarchical&lt;/span&gt; &lt;span class="n"&gt;DSA&lt;/span&gt; &lt;span class="n"&gt;roadmap&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;contests&lt;/span&gt;        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregated&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="n"&gt;APIs&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;user_solved_problems.problemId&lt;/code&gt; is a &lt;code&gt;text&lt;/code&gt; column that stores a MongoDB ObjectId string. This is a &lt;strong&gt;cross-database foreign key&lt;/strong&gt; — a deliberate trade-off. I get relational semantics for user stats without forcing the problem definitions into Postgres where they don't fit.&lt;/p&gt;

&lt;p&gt;Drizzle ORM handles this with full type safety. The column is declared as &lt;code&gt;text()&lt;/code&gt; but semantically treated as a foreign reference in the service layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB Atlas — The Content Engine
&lt;/h3&gt;

&lt;p&gt;MongoDB owns everything that is &lt;strong&gt;large, nested, or schema-flexible&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem definitions&lt;/strong&gt; — title, description, constraints, hints, editorial, companies tagged, difficulty, language support flags. A single problem document can be 20KB+ of nested JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden test cases&lt;/strong&gt; — hundreds of &lt;code&gt;{input, expectedOutput}&lt;/code&gt; pairs per problem that the judge evaluates against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submissions&lt;/strong&gt; — the full execution log: source code, stdout per test, stderr, time and memory per test case, final verdict. This can easily reach 100KB per submission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Academy tracks&lt;/strong&gt; — deeply nested exercise trees with descriptions, hints, and starter code per language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System Design workspaces&lt;/strong&gt; — Tldraw serialized JSON documents. These are opaque blobs that Postgres has no business indexing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Forcing this into Postgres would mean either (a) JSONB columns everywhere and losing schema enforcement, or (b) a schema migration nightmare every time a problem field changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aiven Valkey (Redis) — Four Jobs, One Store
&lt;/h3&gt;

&lt;p&gt;Redis is the most overloaded layer in the system. It does four distinct things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Key Pattern&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bull:submission-evaluation:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Until consumed&lt;/td&gt;
&lt;td&gt;BullMQ Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bull:arena-cleanup:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Until consumed&lt;/td&gt;
&lt;td&gt;BullMQ Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bull:contest-sync:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Until consumed&lt;/td&gt;
&lt;td&gt;BullMQ Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Arena State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arena:room:{roomId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24 hours&lt;/td&gt;
&lt;td&gt;Go Arena Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Arena State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arena:user_room:{userId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24 hours&lt;/td&gt;
&lt;td&gt;Go Arena Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;company:*&lt;/code&gt;, &lt;code&gt;llm:*&lt;/code&gt;, &lt;code&gt;diagram:*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;24h–7 days&lt;/td&gt;
&lt;td&gt;Hono API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arena:events:{roomId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N/A (channel)&lt;/td&gt;
&lt;td&gt;API → Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arena:match:started:{roomId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N/A (channel)&lt;/td&gt;
&lt;td&gt;API → Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arena:submission:{roomId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N/A (channel)&lt;/td&gt;
&lt;td&gt;Worker → Go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The most interesting part is that Redis decouples services that would otherwise need to know each other's IP addresses. The Bun API and the Golang Arena Hub &lt;strong&gt;never talk directly&lt;/strong&gt;. They shout into Redis, and the other side listens.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Submission Pipeline: Async Code Evaluation
&lt;/h2&gt;

&lt;p&gt;This is the most complex workflow in the platform. When a user clicks "Submit", this is what actually happens:&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Synchronous Fast Path (the API's job)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → POST /submissions/submit { sourceCode, languageId, problemId }
       → SubmissionService: Insert { status: "PENDING" } into MongoDB
       → BullMQ: Enqueue job { submissionId }
       → Response: 202 Accepted { submissionId }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API responds in ~50ms. It does not wait for compilation. It cannot — compilation can take 1–30 seconds depending on the language and problem complexity.&lt;/p&gt;

&lt;p&gt;The client immediately starts polling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Client-side polling — every 2 seconds&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;poll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;submissionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/submissions/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;submissionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/status`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PENDING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RUNNING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;submissionId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;displayVerdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 2: Asynchronous Execution (the worker's job)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BullMQ dequeues job
  → MongoDB: Fetch public + hidden test cases for problemId
  → ExecutionService.runFullSubmission(sourceCode, tests)
      → For each test case batch:
          → Judge0: POST { source_code, language_id, stdin }
          → Wait for Judge0 response: { stdout, stderr, time, memory, status }
  → Calculate overall verdict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the &lt;strong&gt;Driver layer&lt;/strong&gt; matters. Before sending code to Judge0, a language-specific wrapper is applied. For a Python problem, the driver injects the boilerplate that calls the user's function with the test input and captures stdout. This means users only write the algorithm — not the I/O scaffolding. The &lt;code&gt;/driver&lt;/code&gt; directory contains these wrappers for Java, C++, C, Go, Rust, Python, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Judge0 is the primary and definitive judge.&lt;/strong&gt; It compares the program's stdout against the expected output for each hidden test case. For 95%+ of problems, this is all that runs.&lt;/p&gt;

&lt;p&gt;For a small subset of problems where multiple valid outputs exist — such as questions asking for "any valid topological order" or "all valid parentheses combinations" — a pure string comparison would incorrectly reject correct answers. In those cases, an AI-based semantic check runs as a fallback, evaluating whether the user's output is logically equivalent even if the string differs. This is the exception, not the rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Finalization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Worker → MongoDB: Update submission { status, executionResults, finalVerdict }
Worker → Postgres (parallel): Update user_stats { totalSolved++, streak, points }
Worker → Redis Pub/Sub: Publish arena:submission:{roomId} (if arena match)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stats update runs in parallel with the MongoDB save using &lt;code&gt;Promise.all&lt;/code&gt;. It handles streak calculation (checking &lt;code&gt;lastSolveDate&lt;/code&gt; against today's date), difficulty-specific counters (&lt;code&gt;easySolved&lt;/code&gt;, &lt;code&gt;mediumSolved&lt;/code&gt;, &lt;code&gt;hardSolved&lt;/code&gt;), language distribution tracking via a &lt;code&gt;jsonb languageCounts&lt;/code&gt; column, and ELO-style arena point deltas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Arena: Real-Time Multiplayer at Scale
&lt;/h2&gt;

&lt;p&gt;The Arena is the feature that required the most architectural thought. Coordinating 50 simultaneous players — each submitting code, getting verdicts, and watching a live leaderboard update — requires a completely different stack than the REST API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Golang?
&lt;/h3&gt;

&lt;p&gt;Node.js uses an event loop — it is single-threaded I/O multiplexed. For WebSockets, this means each message goes through the same event queue. Under high concurrency, this creates latency jitter. A single slow operation (like a heavy JSON parse) can cause perceptible delays for unrelated clients.&lt;/p&gt;

&lt;p&gt;Go's concurrency model is fundamentally different: each WebSocket client gets its own &lt;strong&gt;goroutine&lt;/strong&gt; (a lightweight green thread, ~2KB stack vs ~8MB for OS threads). The Go runtime scheduler multiplexes thousands of goroutines across available CPU cores. Communication between goroutines uses &lt;strong&gt;channels&lt;/strong&gt; — typed, blocking data pipes that are inherently safe without mutex locks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Arena Hub (simplified) — each room is a Go goroutine cluster&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Hub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rooms&lt;/span&gt;  &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Room&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;     &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RWMutex&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Room&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;  &lt;span class="c"&gt;// userId → websocket connection&lt;/span&gt;
    &lt;span class="n"&gt;pub&lt;/span&gt;     &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;          &lt;span class="c"&gt;// outbound broadcast channel&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Each client has a dedicated read goroutine&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;readPump&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Redis as the State Store (Lua Atomic Scripts)
&lt;/h3&gt;

&lt;p&gt;The arena room state lives in Redis, not in Go memory. This is a critical design decision: if the Go server restarts (deploy, crash, OOM), the match state is not lost. Redis is the source of truth.&lt;/p&gt;

&lt;p&gt;But Redis is also shared between the Bun API (which handles join requests) and the Go Hub (which manages live connections). Two processes writing to the same key creates race conditions. The solution: &lt;strong&gt;embedded Lua scripts&lt;/strong&gt; that execute atomically on the Redis server itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- arena_join.lua (Lua runs atomically in Redis — no concurrent modification)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;roomKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;userRoomKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;maxPlayers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;roomJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roomKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;roomJson&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ROOM_NOT_FOUND"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;roomJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Atomic check: is room full?&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;maxPlayers&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ROOM_FULL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- Atomic check: is user already in a room?&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;existingRoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userRoomKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;existingRoom&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ALREADY_IN_ROOM"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- Both checks passed — atomically update&lt;/span&gt;
&lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roomKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"EX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userRoomKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"EX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this Lua script, two simultaneous join requests could both pass the "room full" check and push the room over capacity. The Lua script ensures this is &lt;strong&gt;compare-and-swap&lt;/strong&gt; in a single atomic operation — guaranteed by Redis's single-threaded command processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pub/Sub Bridge
&lt;/h3&gt;

&lt;p&gt;The Bun workers (which handle code evaluation) and the Go Hub (which manages WebSocket clients) need to communicate. But they run on different processes. The bridge is &lt;strong&gt;Redis Pub/Sub&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BullMQ Worker evaluates submission
    → Publishes to Redis channel: "arena:submission:{roomId}"
    → Payload: { userId, verdict, testsPassed, totalTests, score }

Go Hub subscribes to "arena:submission:*"
    → Receives message
    → Finds the correct Room by roomId
    → Broadcasts scoreboard update to all connected clients in that room
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design means the Bun API server and the Go Arena Hub are &lt;strong&gt;completely decoupled&lt;/strong&gt;. Neither knows the other's hostname or IP. They only share knowledge of the Redis channel name format. This makes independent scaling and deployment of each service trivial.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Arena State Machine
&lt;/h3&gt;

&lt;p&gt;An Arena match transitions through these states, managed via Redis + Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[*] → WAITING  (Host creates room, others join via PIN)
WAITING → LOBBY  (Host toggles lobby mode for readiness checks)
LOBBY → WAITING  (Host toggles back)
WAITING/LOBBY → PLAYING  (Host starts match — requires ≥ 2 players)
PLAYING → FINISHED  (20-minute timer expires OR host aborts)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The match timer is managed via a &lt;strong&gt;BullMQ delayed job&lt;/strong&gt;: when the host starts a match, a job is enqueued with a &lt;code&gt;delay&lt;/code&gt; of &lt;code&gt;matchDurationMs&lt;/code&gt;. When that job fires, the Arena Worker publishes a "match:ended" event to Redis Pub/Sub, which the Go Hub receives and broadcasts to all clients.&lt;/p&gt;




&lt;h2&gt;
  
  
  System Design: AI-Powered Learning Canvas
&lt;/h2&gt;

&lt;p&gt;The System Design section is more than static documentation. It is a &lt;strong&gt;56-topic curriculum&lt;/strong&gt; backed by an interactive canvas and AI assistant.&lt;/p&gt;

&lt;p&gt;Each topic (e.g., "Load Balancer", "Message Queue", "CDN") has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Structured content&lt;/strong&gt; — a deep-dive explanation with diagrams and real-world examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Tldraw workspace&lt;/strong&gt; — an infinite canvas where users sketch their architecture diagrams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An AI chat assistant&lt;/strong&gt; — context-aware of the current topic being studied&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI diagram generation&lt;/strong&gt; — describe a system in prose, get a Tldraw diagram back&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI tutor prompt is aware of the current system design topic. If the user is on the "Caching" topic and asks "where does Redis fit?", the AI knows the context and responds with topic-specific advice, not generic information.&lt;/p&gt;

&lt;p&gt;The diagram generation pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: "Draw a system design for a URL shortener"
   → API: POST /systemdesign/workspaces/{id}/generate
   → AI: Generate Tldraw JSON schema from description
   → Parse + validate the returned JSON
   → Merge into the existing workspace document in MongoDB
   → Return updated Tldraw state to the client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI responses and diagram outputs are cached in Redis (7-day TTL) so identical prompts never cost a second API call — useful when many users study the same topics concurrently.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Codebase: 70+ Interconnected Classes
&lt;/h2&gt;

&lt;p&gt;The backend (&lt;code&gt;/api&lt;/code&gt;) uses &lt;strong&gt;Awilix&lt;/strong&gt; for dependency injection — a container-based DI system similar to what you'd see in NestJS, but lighter and built for Hono. Every service is registered in the container and injected into controllers via the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// container.ts&lt;/span&gt;
&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Repositories&lt;/span&gt;
  &lt;span class="na"&gt;problemRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;asClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ProblemRepository&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;submissionRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;asClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SubmissionRepository&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="c1"&gt;// Services&lt;/span&gt;
  &lt;span class="na"&gt;executionService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;asClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ExecutionService&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;submissionService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;asClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SubmissionService&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="c1"&gt;// Controllers&lt;/span&gt;
  &lt;span class="na"&gt;submissionController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;asClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SubmissionController&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service layer boundary is strict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controllers&lt;/strong&gt; — handle HTTP routing, input validation, auth middleware&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services&lt;/strong&gt; — contain all business logic, orchestrate between repositories and external APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repositories&lt;/strong&gt; — contain all database queries, return typed model objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The codebase has 4,765 indexed nodes and 9,732 dependency edges across all packages. The submission domain alone chains: &lt;code&gt;SubmissionController → SubmissionService → ExecutionService → DriverJudgeExecutionService → LLMService&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Scope at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Problems&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;11,000+ coding problems with hidden test cases, Judge0 execution, solution voting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Academy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;82+ language tracks (Go, Rust, Haskell, Gleam...) with progressive exercise-based learning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Roadmap&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interactive DSA roadmap — from Arrays to Tries — with problem links at each node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Arena&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time multiplayer coding battles, up to 50 players, live leaderboard via Go + Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System Design&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;56-topic curriculum, Tldraw canvas, AI chat tutor, AI diagram generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Companies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;460+ company profiles with tagged interview questions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Contests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unified calendar: LeetCode, Codeforces, AtCoder, CodeChef — auto-synced via cron&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compilers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guest-accessible Monaco playground, full Judge0 execution, built-in Tldraw scratchpad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Leaderboards&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ELO-style arena rating, problem solve streaks, language-specific stats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Social&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Follow users, post solutions with voting, GitHub/LinkedIn/LeetCode profile links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Admin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal dashboard for problem creation, category management, and content moderation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What I Would Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. WebSocket for submission status instead of polling.&lt;/strong&gt; The client polling every 2 seconds works, but it creates unnecessary API load at scale. A Redis Pub/Sub → Server-Sent Events (SSE) channel would push the verdict to the client the moment it's ready, with zero polling overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Read replicas for MongoDB.&lt;/strong&gt; All submission reads go to the primary. Under high read load, a read replica for the &lt;code&gt;GET /submissions/{id}/status&lt;/code&gt; endpoint would reduce primary pressure significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Separate Redis instances by concern.&lt;/strong&gt; All four Redis roles (cache, queue, state, pub/sub) share one Valkey instance. Under heavy arena load, the pub/sub throughput could starve the BullMQ queue processing. Separating pub/sub into a dedicated instance is the right move at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. gRPC instead of Redis Pub/Sub for Go↔Node communication.&lt;/strong&gt; Redis Pub/Sub is "fire and forget" — there's no acknowledgment. If the Go Hub restarts and misses a message, that leaderboard update is lost. A gRPC streaming connection with delivery guarantees would be more robust.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;slavecode/
├── /api        → Hono API (Bun, Awilix, Drizzle, Mongoose, BullMQ)
├── /arena      → Go WebSocket Hub (Fiber, goroutines, Redis Pub/Sub)
├── /web        → Next.js 15 frontend (Monaco, Tldraw, Zustand)
├── /admin      → Internal admin portal (Next.js)
├── /driver     → Language-specific code wrappers (Java, C++, Go, Rust...)
├── /cloud      → Azure VM provisioner + health checks
├── /infra      → Docker Compose + Dockerfiles
├── /scripts    → DB seeding, Drizzle migrations, Docker utilities
├── /testings   → Integration tests (network, DB pool, endpoints)
└── /docs       → Full UML diagrams, ERDs, architecture docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building SlaveCode taught me that distributed systems are not about choosing the right technology — they're about understanding the data flow and failure modes first, and then choosing the technology that maps cleanly to those constraints.&lt;/p&gt;

&lt;p&gt;The Judge0 sandbox is on Azure because blast radius matters. The Arena is in Go because goroutines genuinely outperform an event loop for persistent WebSocket connections at scale. Redis does four jobs because it is the right tool for all four of those jobs at this scale.&lt;/p&gt;

&lt;p&gt;Every piece is there because the alternative broke something.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;SlaveCode is live at &lt;a href="https://slavecode.codes" rel="noopener noreferrer"&gt;slavecode.codes&lt;/a&gt;. Find me on GitHub at &lt;a href="https://github.com/phero20" rel="noopener noreferrer"&gt;@phero20&lt;/a&gt;. Your code is your master. Serve it well.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>programming</category>
      <category>career</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
