Skip to content

Tech Stack & Why

A stack list is easy to skim past. What interviewers actually probe is why — so each entry here includes the alternative that was considered and rejected.

Frontend

ChoiceWhy this, not the alternative
React 19 + ViteVite's dev-server speed and native ESM meant no CRA/webpack config to fight. React 19 for the newer useDeferredValue/concurrent-render primitives used in list filtering (see Challenges).
React Router v7File-based routing frameworks (Next.js) assume a Node server; Cloudflare Pages + a pure SPA needed client-side routing that doesn't require SSR infrastructure.
Tailwind v4CSS variables (--ink, --surface, --line, etc.) drive a full light/dark theme system on top of Tailwind utility classes — utility-first without hand-rolling a design system from scratch.
CodeMirror 6 (@uiw/react-codemirror + per-language @codemirror/lang-*)Needed a real multi-language editor (C++, Python, Java, Go, Rust, SQL, JS) with syntax highlighting, not a <textarea>. Monaco was considered and rejected — CodeMirror 6's bundle size is dramatically smaller, which matters when the editor is one of several heavy chunks already competing for load time.
react-markdown + remark-gfm + remark-math + rehype-katex + rehype-highlightQuestion statements are authored as markdown with GFM tables, LaTeX math, and code blocks — this pipeline renders all three without a custom parser.
rechartsUsed for the Codeforces rating graph and weekly-progress charts on the dashboard.
sql.js (SQLite compiled to WASM)SQL Practice runs entirely client-side — no backend query execution, no server round-trip, no risk of arbitrary SQL hitting a real database.

Backend / Infrastructure

ChoiceWhy this, not the alternative
Firebase (Auth + Firestore)Solo dev, no dedicated backend team — Firestore's onSnapshot real-time listeners replace what would otherwise be a WebSocket server for live progress sync, submission queues, and OA exam state.
Cloudflare PagesFree static hosting with a generous build/deploy pipeline; paired with Cloudflare Workers for the two things that need actual server logic (see below).
Cloudflare Workers (ediky-reminders-worker)OA reminder emails were originally on a GitHub Actions cron — GitHub's schedule is best-effort and silently drops runs under load. A Workers cron trigger runs every minute reliably; it reads Firestore over the REST API (no SDK weight in a Worker) and sends via Resend.
Judge0 CE, self-hosted on Oracle Cloud Free TierCommercial code-execution APIs either rate-limit free tiers too aggressively for OA-style bursts (multiple problems x many students) or charge per execution. Self-hosting on an always-free Oracle VM removes that ceiling — see Code Execution for the actual setup and its rough edges.
ResendTransactional email for OA reminders — chosen over raw SMTP for deliverability without hand-managing DNS/SPF/DKIM.
CloudinaryAvatar uploads — image transformation/CDN without building that pipeline in-house.

Content & Tooling

ChoiceWhy this, not the alternative
Markdown + YAML frontmatter as the question formatA structured authoring format (<!--STATEMENT-->, <!--HINTS-->, <!--SOLUTION-->, <!--TESTS-->) that's both human-writable and machine-parseable, so a local pipeline (qgen.py, build_tests.sh, verify_solution.py) can auto-generate, fuzz-test, and cross-language-verify problems before they reach production.
Custom build-time content sharder (tools/build-content.mjs)Not a CMS, not import.meta.glob. Explained in depth in Content Pipeline — this is the single most deliberate architectural decision in the project.
VitestFast, Vite-native test runner — no separate Jest config/transform pipeline to maintain.
WranglerCloudflare's CLI for local Pages dev + Worker deploys, keeping the whole toolchain in one ecosystem.

Ediky Workflow — internal engineering documentation.