1The problem
A recreational sports league was running entirely on spreadsheets, paper score sheets and group texts. There was no live way to see a tournament bracket update, no shared source of truth for standings, and — the sharpest pain point — referees on the field had no reliable way to score a game and have it show up anywhere else. Anything that needed to be "live" wasn't.
2The approach
Rather than one monolithic app, I split the problem by who uses it and how they read/write data, and built four independently deployable apps sharing one data layer:
- Referee scoring PWA — the single-writer, real-time core. Installable, offline-capable, built to survive a patchy field connection and sync when it reconnects.
- Tournament site — bracket generation and display, PNG export, multiple divisions.
- Schedule / standings — a read-heavy app computing points-based standings with tiebreakers, consuming what the scoring app writes.
- Captain lineup tool — fully client-side and offline-first by design, no backend dependency at all.
All four sit in a pnpm monorepo sharing two internal packages — a typed Supabase client factory and a shared UI kit — consumed as TypeScript source with no build step between them. The Postgres schema (internally called GRIMMERIE) is documented with a DBML/Mermaid ERD and versioned through ten hand-written, idempotent migrations — deliberately no ORM, so every change is a reviewable, reversible SQL file rather than a generated diff.
CI is deploy-gating, not deploy-owning: GitHub Actions runs typecheck and lint on every push to main, and Cloudflare Pages — five separate projects off the same repo, one per app plus a tournament-config variant — owns the actual deploy. That split kept each app independently shippable without a shared release train.
3The outcome
apps live in production, sharing one governed Postgres schema.
All four apps are live and field-validated — the scoring PWA has been used by real referees to score real games under real field conditions, not demoed once and shelved. The tournament site's config-flag pattern let a second tournament format ship from the same codebase without a fork. And because the schema was designed up front for four different access patterns, adding the lineup tool later meant zero changes to the existing three apps.
4What I'd take into a team
This project is the clearest evidence I have of end-to-end ownership with no safety net — scoping, data modelling, building, deploying and supporting real users, alone. It's also where most of my current front-end fluency comes from: I lean on dbt/SQL for the pipeline side day to day, but this is where I keep the React/TypeScript muscle live.