Apoquiz
Real-Time Multi-Role Live-Event Quiz Platform
Key Technical Highlights
Server-committed turn-order fairness — play order decided and persisted server-side before any client-facing animation runs, so a refreshed or late-joining client always lands on the identical result
Found and fixed an O(N²) broadcast bug at load — one event fanning out to ~1,000,000 messages/question at 1,000 simulated members — cut ~96% via a coalescing pattern, verified on a real 600-bot run
Built a from-scratch load-testing harness simulating 500+ concurrent audience members, an unattended moderator bot, and real Chromium team clients against the platform's actual Socket.IO/REST contract
Redesigned question allocation from randomly-sliced pools to author-curated, explicitly-numbered sets dealt whole per team, replacing a biased shuffle with correct Fisher–Yates
Ran an 'audit, then adversarially refute' QA pass against real tournament data — raised 96 candidate display defects, disproved 46, fixed the 50 that were real
The Case Study
Overview
Apoquiz is a real-time, multi-role live-event quiz platform — a NestJS + Socket.IO backend and Next.js frontend in a five-package pnpm/Turborepo monorepo — built for a client and deployed live at a real public tournament. Built by a small team of two to three people; the work described here is scoped to what version-control history actually attributes to this contribution (roughly half of all commits), not the project as a whole.
Fairness Under Real Network Conditions
Turn-based game modes needed to feel fair to a room full of people watching a shared projector. Play order is decided and persisted server-side before any client-facing "spinning wheel" animation runs, so a projector that refreshes mid-spin, or joins late, lands on the identical result rather than racing the animation — and a server restart mid-round restores the exact order the room already watched decided, rather than forcing a visibly different re-draw. Question allocation for the higher-stakes rounds moved from randomly slicing a shuffled pool per team (uncontrolled difficulty) to author-curated, explicitly-numbered sets dealt whole per team, paired with a correct Fisher–Yates shuffle in place of a biased sort-based one.
Load-Testing Against the Real Contract
Ahead of a live event, a from-scratch load-testing harness simulated 500+ concurrent audience members from one laptop, an unattended moderator bot driving all rounds, and real Chromium browser instances for team clients — against the platform's actual Socket.IO/REST contract, no mocks. It surfaced a genuine O(N²) broadcast bug: a single per-submission event was fanning out to roughly a million messages per question at 1,000 simulated members. Reusing an existing coalescing pattern already in the codebase cut broadcast volume by about 96%, measured on a real 600-bot run, with a full 18-question game verified end-to-end afterward and every scoring event reconciling exactly against the emitted total.
Honest About the Ceiling
The platform runs as a single Node process with Socket.IO's default in-memory adapter — no Redis adapter, no cluster mode — so it doesn't currently scale horizontally. That's documented plainly rather than glossed over, alongside a standing operational rule never to load-test against production near a real event, since the test bots write real database rows.
QA Discipline
A pre-tournament display-defect sweep against real data (long names the UI had only ever been tested against short placeholders) raised 96 candidate issues, then deliberately tried to disprove each one against the actual code path before fixing anything — closing 50 real defects and discarding 46 false positives, rather than fixing all 96 on faith.