Appearance
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
| Choice | Why this, not the alternative |
|---|---|
| React 19 + Vite | Vite'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 v7 | File-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 v4 | CSS 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-highlight | Question statements are authored as markdown with GFM tables, LaTeX math, and code blocks — this pipeline renders all three without a custom parser. |
| recharts | Used 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
| Choice | Why 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 Pages | Free 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 Tier | Commercial 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. |
| Resend | Transactional email for OA reminders — chosen over raw SMTP for deliverability without hand-managing DNS/SPF/DKIM. |
| Cloudinary | Avatar uploads — image transformation/CDN without building that pipeline in-house. |
Content & Tooling
| Choice | Why this, not the alternative |
|---|---|
| Markdown + YAML frontmatter as the question format | A 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. |
| Vitest | Fast, Vite-native test runner — no separate Jest config/transform pipeline to maintain. |
| Wrangler | Cloudflare's CLI for local Pages dev + Worker deploys, keeping the whole toolchain in one ecosystem. |