Back to index
04Work

Bolão de Futebol

Predictions, scoring and ranking

My role
Full stack developer
Client
Catsuc Labs
Sector
Consumer product · web
Status
In production

Proprietary code. Private client system — the repository isn’t public.

Numbers

apito

prediction lock

Deadline checked on the server — never on the client clock.

100%

recomputable scoring

Scoring is a pure function of the result, so a correction is a reprocess.

A

Context

Pools among friends have always lived in spreadsheets and group chats. The problem was never the maths — it was trust: who predicted before kickoff, who changed it afterwards, and why the leaderboard shifted overnight.

B

The problem

Three things that look like details and aren’t: the prediction has to lock at kickoff, non-negotiably; the scoring rule varies per tournament and can’t be scattered through the code; and official results can be corrected — so scoring has to be re-runnable without turning into a mess.

C

The approach

The deadline belongs to the server. The client hides the form as a courtesy, but the route is what rejects a late prediction — a client clock is a suggestion, not an authority. Scoring became a pure function of (prediction, result, tournament rule), so re-scoring is safe by construction: if a result is corrected, reprocess and the leaderboard rebuilds with no dirty state left behind.

D

Modules

01Rounds and matches

Campeonatos

Tournament structure, with the scoring rule attached to the tournament instead of hardcoded.

02Deadline-bound

Palpites

Entry and editing until kickoff. After that the route refuses — and the entrant sees exactly why.

03Deterministic

Apuração

Runs over the official result and can run again. A corrected result is a reprocess, not a patch.

04With tie-breaks

Ranking

Standings with explicit tie-break criteria, so two tied entrants get a stable order.

E

The hard parts

The client clock lies

Hiding the form at kickoff is UX. Refusing a late prediction is integrity — and only the server can do that. In a pool, one prediction accepted after the whistle isn’t an edge-case bug: it’s the reason nobody trusts the leaderboard again.

Scoring rule as data

Every tournament scores differently. Scattering that through conditionals guarantees drift at the first change. Treating the rule as tournament configuration and scoring as a pure function made recomputation trivial — which is what saved us when an official result was corrected.

F

What’s mine

  • Modeling tournaments, rounds, matches and predictions
  • Server-side deadline enforcement and the messages explaining a refusal
  • A deterministic, re-runnable scoring engine
  • Standings with tie-breaks, and the participation screens
G

Outcome

The pool moved off the spreadsheet and the screenshot in the group chat. The deadline is the same for everyone, the leaderboard explains itself, and a corrected result stopped being an argument and became a reprocess.

What I took from it

  1. 01A deadline is a server rule. Anything validated only on the client is not validated.
  2. 02Deterministic, re-runnable computation beats fast computation.
  3. 03In a competitive product the leaderboard must be explainable — otherwise the argument becomes support.

Stack