Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,13 @@ The app is configured for [Railway](https://railway.app) via `railway.json`:
- **Build**: `npm run build` (Nixpacks builder)
- **Start**: `node server.js`
- **Restart policy**: on failure, up to 10 retries

## Design notes

**Why a proxy server for a frontend app** — Codeforces doesn't send CORS headers, so the browser can't call the API directly. The Express proxy handles that for proxied endpoints, and also scrapes problem statement HTML, which is impossible cross-origin from the browser. Note that the Timeline and Bounty pages fetch contest data directly from the browser (not via the proxy), so the rate limiter only covers the proxied daily-problems and statement endpoints — not all Codeforces traffic.

**Why the daily pick is deterministic** — For the same date, rating, and solved-problem history, the pick is always identical. Refreshing doesn't reroll (no slot-machine effect), and the result is easy to test: fix the date and inputs, snapshot the expected output. Two users with the same rating and solved set get the same problem; users with different ratings or solved sets get different candidates and may get different picks — the pick is stable per user across the day, not globally uniform.

**Why the rating band is asymmetric [−100, +300]** — Practice should stretch, not repeat. A small floor below your current rating keeps a warm-up option available; the larger ceiling pushes toward difficulty. The asymmetry reflects the deliberate-practice idea that slightly-too-hard is more valuable for growth than slightly-too-easy.

**Why `Promise.allSettled` instead of `Promise.all`** — The daily page needs three things: the problemset, your user info, and your solved submissions. If one fetch fails (handle not set, Codeforces API hiccup), `allSettled` delivers whatever succeeded and lets the page render with sensible defaults. `Promise.all` would reject everything on a single partial failure and leave the page blank.
Loading