Case Study · Team Scout
Youth sports programs run on software written in 2014. I spent enough time with coaches and volunteer administrators to understand why they tolerate it — then built the alternative myself.
1 person
Product, architecture, build
Multi-tenant
Every org isolated at the database
Double-entry
Ledger accounting on every dollar
Web + native
Next.js and React Native
The obvious read on youth sports software is that the incumbents are bad and someone should build a better one. That read is wrong, or at least incomplete — the incumbents are bad and they are entrenched, because the person choosing the software is a volunteer who inherited it and has eleven other jobs.
So the real question wasn't whether a better product could exist. It was: what would make a volunteer administrator, mid-season, decide to move? The answer turned out to be narrower than a feature list. It was the handful of moments where the old system actively costs them — a registration that takes forty minutes to configure, a treasurer who can't tell you who has paid, a website only one person can update.
That framing decided the product. Not “a platform for youth sports,” but a specific sequence of relief, with the first one cheap enough to try without a committee.
Each of these is a business decision that happens to be expressed in schema.
The usual approach
Multi-tenant platforms usually keep organizations apart in application code — every query carries a filter, and one forgotten WHERE clause leaks one club's roster to another.
What I did instead
Isolation is enforced in Postgres itself. Row-level security policies mean the database refuses to return another organization's rows regardless of what the application asks for. A bug becomes an error instead of a breach.
Why it mattered
The data is minors' names, addresses and guardians. That's not a place to rely on remembering a filter.
The usual approach
The fast way to ship a registration form is to store submissions as JSON. It works immediately and it's unusable within a year — you can't report on it, can't constrain it, and the same field ends up spelled three ways.
What I did instead
Every entity gets real tables, real foreign keys, real constraints. Registrations, participants, guardians, forms, payments and ledger entries are separate and related. One fact lives in exactly one place.
Why it mattered
The reporting a club actually needs — who has paid, who is cleared, what a season cost — is trivial against a normalized schema and nearly impossible against a blob.
The usual approach
Most youth-sports platforms track a payment as a row with a status. When a refund is partial, or a scholarship covers half a fee, or a fundraiser splits across two teams, that model quietly stops reconciling.
What I did instead
Every movement of money writes a balanced pair of ledger entries. The books have to balance because the schema won't let them do anything else, and any figure can be traced to the transactions that produced it.
Why it mattered
Volunteer treasurers hand off every year. A system that reconciles to the bank is the difference between a clean handoff and a forensic exercise.
The usual approach
Routing club funds through a platform account is simpler to build and creates a custodial relationship nobody wants — regulatory exposure for the platform, and a trust problem for the club.
What I did instead
Each organization has its own connected account. Funds settle directly to the club. The platform facilitates and never holds.
Why it mattered
It removes an entire category of risk and it's the honest answer when a treasurer asks where their money sits.
Why this is the portfolio piece
Row-level security because the data is children's. Double-entry because volunteer treasurers hand off every year. Connected accounts because a club shouldn't have to trust a platform with its float. Someone who only writes code would need to be told all of that. Someone who only writes strategy couldn't act on it. That gap is the thing I sell.
Built in Kansas City for programs everywhere. If you have a system that needs the same treatment, that's the work I do.