Est. 1983 Price · Free
Steven Grant

Digital Evangelist

Notebook

Building two apps for one football team

This past season I coached my son Levi's school football team, and shipped two products along the way. Neither would exist without AI.

This past season I coached my son Levi's school football team. I didn't build anything because I was drowning in admin. I built it because I wanted a better process for seeing where each player was at and where the team's results were heading. Across a season you accumulate a lot of small signals, who played where, who turned up to training, who's improving at what, and I had no good way of looking at any of it in one place. Over the course of that season I shipped two products to fix that. Pitchside is a privacy-first squad management app for coaches. Coach Steven is a learning platform for the players on my own team. They're adjacent products for the two halves of the same touchline, and neither would exist without AI. Let me walk you through it.

Two products, one touchline

Pitchside is the coach's tool, and it exists because I went shopping first. Every squad management app I tried wanted an account before I could do anything, a monthly subscription, or served me ads while I was trying to check a lineup before kickoff. For a volunteer running a school team, that's all noise. I wanted an app that opens and works, so I built one. Squad management, drag-and-drop lineup planning on a real pitch, match tracking, training sessions and attendance, a drill library with video, and a stats screen that finally answers the question I started with: who's actually progressing. It works entirely offline with no account required. Your data stays on your device.

Coach Steven is the player's tool. It sends homework to every player on the team: auto-graded theory questions and practical drills where a player uploads a video of themselves attempting a skill against my reference clip. It's grown to include termly player reports, a leaderboard, and end-of-season awards voting.

The important difference is audience size. Pitchside is a public app on the App Store and Google Play for any coach. Coach Steven is a product for exactly one team: me, a dozen or so players, and their parents. That constraint shaped almost every decision in it, and it's freeing. When your entire user base fits in one group chat, you can be as opinionated as you like.

The tech stacks

The two products couldn't be more different under the hood, and that's deliberate.

Pitchside is React Native with Expo SDK 55, expo-router for file-based routing, and NativeWind for styling. The entire data layer is a local SQLite database via expo-sqlite. There's no backend, no sync engine, no accounts, and no analytics SDK anywhere in the codebase. The only network calls are opt-in: converting a YouTube drill link to HLS for offline playback, and support tickets. The lineup editor uses Reanimated and gesture-handler, with player positions stored as normalized 0 to 1 coordinates so a lineup saved on a phone renders correctly on a tablet.

Coach Steven is Laravel 13 with Inertia and React 19, TypeScript end to end thanks to laravel-data and typescript-transformer generating types from PHP DTOs, and Wayfinder for type-safe routes. It runs on Laravel Cloud with scale-to-zero enabled, which suits a team app that sleeps most of the week. Video lives in a private Cloudflare R2 bucket, uploaded straight from the browser with presigned multipart uploads and played back through short-lived signed URLs.

There are no passwords anywhere in Coach Steven. Parents sign in with magic links, and players aren't accounts at all. A player record belongs to a parent, and the parent picks who's submitting. More on why below.

Building with AI

Let me be direct about this: there is no way I could have built these two products without AI. Not in the time I had. Each one on its own would have taken me far longer to get through, never mind having them finished and in players' and coaches' hands before the season needed them. A season doesn't wait for your side project. The fixtures were the deadline, and AI is the only reason I made it.

Both codebases were built with Claude Code and Codex, and both repos carry a CLAUDE.md and an AGENTS.md that are byte-identical, because the guidance is the same regardless of which agent reads it.

Coach Steven went further. A good chunk of it was built with a Ralph loop: a shell script that runs Claude in a loop against a PRD in JSON form and a progress log. Each iteration picks the highest-priority unfinished feature, implements it, writes Pest tests, runs the suite and Pint, marks the feature as passing, and appends a handoff note for the next iteration. Reading that progress log afterwards is odd. It's a conversation between instances of an agent, and the notes are genuinely useful: key decisions, test counts, open questions for whoever picks it up next.

The loop only works because the rules around it are strict. Controllers use REST verbs only, and anything else becomes a single-purpose invokable controller. Data classes own all transformation, so React receives ready-to-render props and does no reshaping. There are around fifty single-purpose action classes. None of that is novel architecture. What's novel is that the strictness exists primarily so an agent can't wander.

As for the tools themselves, my current verdict after a month away and back: Claude has gotten noticeably verbose in its output, while Codex feels more efficient and faster right now. I switch between them per task and I don't expect that ranking to hold for long. It never does.

There's also AI inside one of the products. Coach Steven uses Laravel's new AI SDK with Claude to generate age-appropriate multiple choice questions from a topic, which the coach then edits. It's a drafting tool, not an autopilot, and that's the right shape for it.

The technical challenges

The R2 upload pipeline in Coach Steven taught me the hard way that multipart uploads fail silently if your CORS policy doesn't expose the ETag header. The browser completes every part, then the assembly step has nothing to stitch together. That one cost an evening.

Pitchside's worst moment was shipping a real bug to production: away match scores were recorded swapped. The fix wasn't just a code change. Because all data lives on users' devices, I had to write a one-shot data-repair migration, guarded by a settings flag so it runs exactly once, that un-swaps the scores for affected matches. It shipped as an over-the-air update rather than a new binary. Local-first is great for privacy and terrible for take-backs. When there's no server, every user's database is production, and you can't run a script against it. You have to ship the script to them.

Getting onto the app stores

Pitchside was the first native app I've ever published, and getting it onto the stores was a challenge in its own right. After twenty-odd years of deploying to web servers, where shipping means pushing to a branch, the app stores are a different world. Nothing goes live until Apple and Google say it does.

There's a long list of things a web deploy never asks of you. Provisioning profiles and signing certificates. Store listings, privacy questionnaires, and content ratings. Screenshots at specific sizes for phones and two sizes of tablet. Google's testing requirements before you're allowed a production track. And then the review queue, where you wait to find out whether a human or a robot objects to something you can't predict. EAS and Fastlane took a lot of the mechanical pain away, and the AI tools were good at the surrounding chores, like scripting ffmpeg to crop and frame Play Store screenshots. But the process itself you just have to go through, and the first time is the worst time.

It's also where over-the-air updates earn their keep. Once the binary is approved, JavaScript-level fixes like that score repair can ship in minutes instead of joining another review queue.

Learning needs a reason

Pitchside was also my first React Native app, which meant learning a new stack while building it. That worked, and I've been around long enough to know why: I cared about the thing I was building. When you're learning something you have no interest in, motivation is uphill all the way. When the thing you're building matters to you at its core, the enthusiasm shows up on its own, and you're far more likely to actually finish something.

Looking back at my career, the pattern is consistent. I learned ExpressionEngine by building a church website. I was invested in making a great church website, so the learning came along for free. When I tried to learn Ruby on Rails, I was building a Twitter clone, and I didn't care about the Twitter clone. As a result I lost interest in Rails too. Learning Laravel stuck for the opposite reason: there was a particular product I wanted to build and put in front of the public, and the framework was simply what stood between me and it.

A season of my son's football turned out to be a better React Native curriculum than any tutorial.

Kids' data

Both products handle information about children, and that's the non-technical challenge I thought about most. Coaching apps that hold young players' details on someone else's servers never sat right with me, and both products are shaped by that.

Pitchside's answer is to not have the data. Everything sits in a SQLite file on the coach's device. There's no account, no server-side player database, and no analytics. Backups are a JSON export the coach owns and shares through the system share sheet. I don't run servers that collect your data, and I never will. I can't leak what I never collect.

Coach Steven can't avoid a server, since players upload videos of themselves. So the accounts belong to parents, not children. Players never have credentials. Media sits in a private bucket and is only reachable through signed URLs that expire in an hour. Even the awards voting was designed around this: each player gets a single-use signed link emailed to their parent, and the voting token table is deliberately separate from the login table so a ballot link can never sign anyone in.

None of this made either product harder to use. Mostly it made them simpler. Privacy pressure is a decent forcing function for cutting features you didn't need.

Time, focus, and actually finishing

The honest non-technical challenge is that I built these around a job, a family, and the actual coaching the apps exist to support. AI collapses the cost of starting, which is a blessing and a trap. On any given evening I could spin up a new feature in either product, and the backlog of plausible features for a football app is infinite.

What saved me was writing down what not to build. Pitchside has no player ratings by design, just Player of the Match, goals, assists and notes, because I didn't want an app that scores eight-year-olds out of ten. Coach Steven's PRD had a v1 line and the Ralph loop worked to it. Both products shipped because their scope was a document, not a feeling.

Four sales

Pitchside has made four sales so far. That's probably four more than I had any right to expect.

I didn't build either of these to turn a profit. They exist because I needed them, and scratching my own itch was the whole business case. Selling Pitchside wasn't even the plan until someone on another team mentioned they'd pay for access to it. That was the nudge to put a price on it and see what happens. Four coaches later, the experiment continues.

Coach Steven will probably never be a product, and I've made my peace with that. The difference isn't the code, it's the support. Coach Steven has a live server, and the moment you charge someone for access to a server, you're responsible for keeping it up. I don't want the pager. Pitchside is far friendlier in that respect: everything is stored locally on the device, so there's no uptime to defend and nothing of mine between a coach and their data. If I disappeared tomorrow, the app would keep working. That's the kind of product a side project should be.

In closing

Two adjacent products, two very different stacks, one team. Pitchside is on the App Store and Google Play, with no account, no ads, no subscription, and your squad's data never leaves your phone. Coach Steven serves an audience of about fifteen people at coachsteven.nz, and it's all the better for it.

The season is just coming to an end now. Levi's team never knew the difference, but their coach did.