The product
BenchApp is a sports team management platform used by 800K+ players across 12 sports — scheduling, rosters, payments, live scorekeeping, and team chat. It had years of real usage behind it and a backend to match: a large PHP application that had grown feature by feature since the company's early days.
The mandate was to move the whole thing onto a modern stack — an Expo (React Native) frontend backed by GraphQL — without breaking what 800K people already relied on every week. Same product, new foundation, shipped live in pieces.
My role
I'm one of three developers on the product. On a team that small, you don't get to specialize — I worked across the entire stack, from React Native screens to GraphQL resolvers to Prisma queries against the production database.
I led three of the largest subsystems end-to-end — the scorekeeper, the league manager, and Finder — owning both the frontend rebuild and the backend it ran on. Over the course of the migration I shipped 100+ tickets across three repositories.
I don't get attached to tools. I get attached to shipping.
What I built
The scorekeeper. The most complex part of the product — and the piece I rebuilt from the ground up. The old version was a tangle of PHP that only supported hockey. I converted the full system — game sheet, box score, rosters, goalies, penalties (30+ infractions), per-period shot tracking — into a modular GraphQL-backed architecture that now supports hockey, soccer, baseball, and more. I built the GraphQL mutations the rest of the team was blocked on, then refactored the scoring flow to URQL optimistic updates so taps register instantly instead of waiting on the network, and fixed the data model so per-period stats persist correctly.
The league manager. An entire admin product, rebuilt in Expo for web and mobile: settings, seasons, divisions, staff management, teams, schedule, player moves and transfers, and league-wide bulletins — each backed by new GraphQL CRUD operations.
Finder. The free-agent matching system. I rebuilt its REST endpoints in GraphQL, ported the frontend from React Native to Expo, brought it to web for the first time, and — at the team's request — designed a map view that clusters nearby players and never exposes individuals, for trust and safety.
The ports. Account, Schedule, Game Details, Roster, and Finances screens, each migrated from React Native to Expo, plus a calendar view built from scratch — all sharing reusable components so a change in one place propagates everywhere.
Backend depth
The migration wasn't just UI. A large share of the work was on the server:
- Payments. Built Stripe flows for guardians paying dependents' fees, a cancellation-survey mutation with email notification, and a bulk-fee mutation that took multi-fee creation from roughly 90 seconds down to near-instant by batching the writes and moving emails to a background queue.
- Reliability. Root-caused production crashes that turned out to be Prisma query-engine panics on null relation data — the waiver and box-score load failures — and shipped fixes plus the validation to prevent them recurring.
- Correctness over speed. When automated review flagged two "bugs" in my Finder port, I verified them line-by-line against the original REST behavior and called them as faithful ports, not regressions — avoiding silent behavior drift in a system real users depend on.
Impact
- The product runs on a modern, maintainable stack instead of legacy PHP — across web, iOS, and Android.
- The scorekeeper is now multi-sport and fast, replacing a hockey-only system that had served for years.
- League operators manage their entire organization through a rebuilt admin product.
- All of it shipped incrementally, in production, to 800K+ existing users — with no big-bang cutover.
Next: BSporty →
