Skip to Content
Technical ReferenceArchitecture

Architecture

For a developer taking over maintenance of iRank.

Shape of the system

LayerTechnology
FrontendNext.js (App Router), React, Tailwind, shadcn/ui
BackendConvex — database, queries, mutations, actions, scheduling
HostingVercel (frontend), Convex Cloud (backend)
EmailSMTP via Nodemailer, in Convex’s Node runtime
PushWeb Push (VAPID)

The app is effectively a single-page application: almost every component is a client component, and data comes from Convex’s live queries rather than from server rendering.

Where the important logic lives

ConcernLocation
WSDC scoring ruleslib/scoring/wsdc.ts
Pairing algorithmlib/pairing/
Ranking modellib/ranking/
Offline store and outboxlib/offline/
Ballot validationconvex/lib/ballot_validation.ts
Panel reconciliationconvex/lib/ballot_results.ts
Release gatingconvex/lib/ranking_release.ts

lib/scoring/wsdc.ts is the single source of truth for scoring. Both the browser and the server import it, so a rule cannot be enforced differently in two places.

The pairing algorithm is isomorphic

One pure module runs in both the browser and the server. It has no Convex or React imports, takes explicit inputs, and is deterministic: a seeded PRNG, no Date.now(), and total sort orders that end on a stable identifier.

That determinism is load-bearing. A device can pair a round offline, and on sync the server re-runs the identical algorithm over its own data and compares the result. A mismatch is rejected.

Offline

Convex’s own mutation queue is in memory and does not survive a reload. The durable outbox in lib/offline/ is separate: Dexie over IndexedDB, with idempotency keys, retry with backoff, and replay on reconnect.

Anything computed on a device is re-verified on sync. The client is never the authority.

Background work

Convex components carry the asynchronous load:

ComponentUsed for
WorkpoolBulk email, push notifications, ranking rebuild fan-out
Action cacheGemini calls, keyed on content rather than session
Action retrierTransactional email — invitations, magic links

Two workpools exist: notificationPool (6 in flight) and rankingPool (2). They are separate so a nightly ranking rebuild cannot delay a motion release.

Scheduled jobs

Defined in convex/crons.ts. The important one is the nightly ranking snapshot rebuild, which fans out per tournament through rankingPool and merges when the last tally reports in.

Testing

Vitest across three projects — Convex functions in an edge-like runtime, pure modules in Node, components in jsdom — plus Playwright for mobile layout at 360px. Property-based tests cover pairing at field sizes from 2 to 400 teams.

Last updated on