← All work

Pickle Prep Pro

Pickle Prep Pro is a phone-first app for the minutes before and after a pickleball match. Strategy cards, a thirty-second prep ritual, guided warm-ups, a checklist, and a post-match reflection that sets one focus for next time. Around that loop: AI prep generated from how you actually play, a clip review that lets a real coach watch your video for a fee, and a way to ask a coach to take you on. The first version was prompted into existence in Lovable in five days. Everything that made it a product came after.

Role —
My own product. The first version was generated in Lovable over five days in March 2026; from March 30 I took the repository over and have designed, built and operated it since.
Timeline —
March 2026 — ongoing
Stack —
Vite + React (PWA), TypeScript, Supabase (Postgres, RLS, Auth, Storage, Vault), Vercel serverless + crons, Stripe (subscriptions, token packs, Connect), xAI Grok (text and vision), PostHog, Web Push (VAPID), i18next (English, Spanish), Tailwind CSS + shadcn/ui, vitest + Playwright + PGlite
Visit pickleprep.app

The problem

I built it for myself first. I was playing a lot and getting better in ways I could not account for — a lesson here, a habit noticed there, a tendency I would catch in a match and forget by the next one. There was nowhere to put any of it. Pickleball is a niche, and the tools for tracking your own progress in it were either generic fitness trackers or a notes app. I am also a busy person, so whatever I used had to take thirty seconds, not thirty minutes. What I wanted was a record of my own journey in the game: the learnings as they accumulated, and a quick daily check-in that would tell me how my game actually was, surface the patterns I kept repeating, and give me something specific to correct next time out. That was the whole brief. Everything the app became grew out of that one habit.

What I built

The first version was not written; it was prompted. Between March 25 and 29, 2026, Lovable generated 417 commits from a Vite and shadcn template: a deck of strategy cards, a warm-up timer with step sounds, custom cards with undo, a waitlist. It looked like an app. On March 30 I took the repository over, and the first weeks were the unglamorous list of what a prototype lacks: a PWA that actually installs, a database I owned rather than one Lovable had provisioned, auth that survived a password reset, an Open Graph image that did not crop. By late April there were Stripe token credits; by mid-May PostHog analytics with first-touch attribution, an investor page and deck, and a Prep Pro subscription; by the end of May a personalized home, coach check-ins over web push, and coach reels pulled from Instagram into the community feed.

June was the month the product found its shape. Clip Review shipped in a single pull request on June 8 — upload a clip, get an AI breakdown, optionally pay a real coach to watch it — with the marketplace, Stripe Connect payouts, messaging, notifications and a delivery deadline all in one. Two days later the app spoke Spanish. Then, after reading the research on deliberate practice, I consolidated three competing “focus” concepts into a single Current Focus that a post-match reflection sets and the next prep session leads with. From July the work turned toward coaches as ongoing relationships rather than transactions, and toward operations: migrations tested against a real Postgres, production promoted from CI, alerting on cron and worker failures, a health endpoint.

What a player has today: a home screen that leads with their one focus, a deck of cards by category, Today’s Prep, three timed warm-ups, a checklist, saved routines, a reflection flow, a learnings view, and a community of shared cards. Signed in, a free account gets ten custom cards and two AI runs a day; Prep Pro lifts that to fifty and ten, and token packs buy more AI. Clip Review, the coach marketplace and coaching relationships are all live in production. So far the users are free users, and the paid paths are built and waiting — the May 2026 investor deck recorded about 150 visitors in the first five weeks, 4.5 pages a session, and nearly half the traffic arriving from Instagram at no acquisition spend. It is the sibling of MyPickleball: the same players, the other side of their week. It is also the sponsor on the tournament pages of Wakamole PBC, the club whose nights those players are preparing for. The public side is at pickleprep.app.

Vertical timeline of Pickle Prep Pro in three eras: the Lovable prototype in late March 2026, the product build from March 30 through June, and the coaching and operations work from July to September 2026.
Fifteen milestones in three eras: five days of Lovable, three months of turning it into a product, and the coaching and operations layer since July. The red era is the one where I took over.

Decisions worth explaining

The engineering, for anyone who wants it.

Architecture diagram: the PWA calls Vercel serverless functions and Supabase directly; the functions call Stripe, xAI Grok, PostHog and Instagram; Vercel crons drive coach check-ins, the daily coach card and the clip-review refund sweep; Supabase holds Postgres with RLS, Auth, the private clips bucket, Vault and the email queue.
The app is a PWA talking to Vercel functions and Supabase. Row-level security and guard triggers in Postgres are the security boundary; Stripe, Grok, PostHog and Instagram sit behind the functions. Video never leaves the phone — only extracted frames reach the AI.

The video never leaves the phone

Clip Review does not upload a video to an AI. The browser samples a handful of evenly spaced, downscaled stills from the clip and sends only those to the analysis endpoint, which hands them to Grok’s vision model. That bounds upload size and provider tokens, and it means the AI pass works even when the clip itself cannot be stored — real phone videos of a rally run to hundreds of megabytes, well over the storage cap, and as of September 2026 the clips bucket has never held one. The page says so plainly in a toast: analysed, not saved, so it cannot go to a coach. Decoding runs against deadlines too, because iPhone Safari will sometimes fire neither a loaded event nor an error and simply hang.

The coach is paid when they deliver, not when the player pays

A coach review costs whatever the coach sets, from five dollars to five hundred, and the app keeps seven percent. The platform collects the whole payment at checkout and holds it. The coach is paid by a Stripe Connect transfer only when they submit the review; if they have not delivered within seventy-two hours, an hourly cron refunds the player and marks the clip expired, and both sides are notified. Coaches onboard as Connect recipient accounts — payout-only, which is what a coach is — and every money column is written only by the service role, with database triggers reverting any client write. A player cannot forge a paid status and a coach cannot enable their own payouts.

Row-level security is not the security boundary; the trigger is

When a player asks a coach to take them on, both of them are allowed to update the link row — the player to withdraw, the coach to accept or decline. Row-level security cannot express “only the coach may set this to active”, so a guard trigger does: it inspects who is making which transition and rejects the illegal ones. The trigger is deliberately not SECURITY DEFINER, because that would make its check of the current user always match the function owner and silently disable the guard. This is the property that keeps a player from granting themselves a coach’s read access to their own data, and it is proven rather than argued: the migrations run against a real Postgres compiled to WebAssembly in the test suite.

One focus at a time, because the research says so

By June the app had three parallel ideas of “focus”: a picker that let you choose two areas, an ephemeral focus line in the AI prep, and a focus inferred from reflections that only ever fed weekly-recap copy. The deliberate-practice and implementation-intention literature is clear that a single, specific if-then cue per session outperforms several, and that reflection only pays off when it closes the loop into the next plan. So the app now has one Current Focus, stored on the profile and following the player across devices. A post-match reflection sets it; the home screen leads with it; one tap turns it into a practice routine, rule-based and instant, with an AI-enhanced version behind it that falls back to the rule-based one rather than dead-ending.

A single-page app that search engines and chat apps can still read

The whole product is one client-rendered shell, which is right for an installable app and wrong for link previews: a crawler that does not run JavaScript sees the same title on every URL. Rather than adopt a server-rendering framework for a marketing page, the deployment rewrites requests whose user agent is a link-preview bot — iMessage, WhatsApp, Slack, Facebook, Google — to a small function that returns per-route Open Graph tags from the same table the app uses for its own titles. Humans never hit it. A shared card link unfurls with that card’s image; a coach invite looks like an invite.

No app store, on purpose, with the conditions for changing that written down

A decision memo weighed a Play Store shell, a Capacitor wrapper for both stores, and a native rewrite against the PWA as it stands. The PWA won for now: distribution runs through QR codes at the court, share links and the community feed rather than store search; web push already covers reminders; and Stripe on the web avoids duplicating every product as an in-app purchase at a thirty-percent fee. The memo also names the triggers that would reopen the question — a club pilot measurably losing players who refuse to add to home screen, or a store mandating in-app purchase — so the decision can be revisited on evidence instead of relitigated on instinct.

Instrumented for an investor conversation before there was one

PostHog runs on both the browser and the serverless routes, identifying signed-in users by their Supabase id so the two halves join without an email address ever reaching the client. Every event carries acquisition context — channel, QR versus share versus Instagram, UTM parameters — and first-touch attribution is persisted on the profile so cohorts can be cut by where a player came from. A hidden dashboard turns that into weekly numbers, and a nineteen-slide deck and a partners wiki were written against it. None of this is what a player sees. It is what lets the product answer “is this working?” with a number rather than a feeling.

← All work