How it’s built
One app, three front doors.
For the curious. BPM is a single Next.js deployment. The iPhone and Android apps are native shells around it, so every release reaches every device at once.
The shape of it
From your phone to the database.
- Server-rendered pages, gated by sign-in and club
- API routes, members-only and rate-limited
- Every read scoped to one club
- English and 简体中文 throughout
Decisions worth knowing
Why it’s built this way.
One deployment
Every change ships to everyone at once. Unfinished work stays behind feature flags, so there’s no separate “stable” version to drift.
Native shells, live web
The phone apps load the live app, so web, iPhone and Android never fall out of step. The shells add widgets, the app badge, shortcuts and push.
Clubs kept apart
Many clubs share one deployment, but every read goes through a single club-scoped path, so one club never sees another’s roster or money.
Honest failures
If something can’t load, the app says so. It never shows a confident zero in place of data it couldn’t fetch, because people rely on the numbers.
AI with a human in the loop
Claude helps draft announcements and summarise your stats. An organiser always sees an announcement before players do.
Fast first paint
Fonts and icons are self-hosted and trimmed, so the first screen doesn’t wait on anyone else’s servers.