The lab · Golf
Card Caddie
Live, handicap-aware scoring for a golf trip.
Everyone in the group scores from their phone and the leaderboard moves within ten seconds. Stableford, Skins, Nassau and Net Stroke settle themselves, so nobody opens a calculator at the bar.
- Status
- Live
- Domain
- Golf
- Since
- 2026
- Role
- Design, engineering
- Site
- cardcaddie.golf
The problem
A golf trip runs on a spreadsheet by the grill and a group-chat running tally. Nobody agrees on the net scores, and the Nassau gets settled by whoever shouts loudest. GHIN, GolfShot and 18Birdies are built for one golfer, not four friends playing three rounds in a weekend.
What was built
- Trips with rosters, scheduled rounds and per-round group assignments.
- An 18-hole entry grid sized for one thumb on a tee box.
- Handicap-adjusted net scoring from the WHS Course Handicap, with per-player tees adjusted relative to the group.
- Four formats computed on the server: Stableford, Skins with carries, Nassau, and Net Stroke. Scramble rounds score by team.
- Leaderboards that refresh silently across every phone in the group.
- Phone sign-in by SMS code. A player added by phone number claims their slot later from a link.
- Solo rounds, a feed of finished rounds with kudos and comments, and public or private profiles.
How it was built
- 01
Write the contract first
Every endpoint lives in one OpenAPI file. The client hooks and the validators are generated from it, so the phone and the server cannot disagree about a score.
- OpenAPI
- Orval
- 02
Keep the math on the server
All four formats live in one scoring module with unit tests. Every phone renders the same leaderboard because only one place computes it.
- TypeScript
- Vitest
- 03
Design for the cart path
Built for one hand, outdoor sunlight and a player who will not read instructions. The look borrows from printed scorecards: tabular numerals, dashed rules, cream on forest green.
- 04
Test it on a trip
It ships before a golf weekend and gets fixed after one. 349 commits so far.
- Claude Code
- Replit
Stack
- Frontend
- React 19
- Vite 7
- Wouter
- TanStack Query
- Tailwind CSS 4
- shadcn/ui
- PWA
- Backend
- Express 5 with Pino logging
- Drizzle ORM on Postgres, 14 tables
- API
- An OpenAPI spec as the source of truth
- Orval generates the React Query hooks and Zod validators from it
- Services
- Twilio Verify for SMS codes
- GolfCourseAPI for tees, pars and stroke indexes
- Replit Autoscale
How it works: one player taps a score into the grid. The server stores it, recomputes net scores from each player's Course Handicap and tee, and rebuilds the leaderboard for every format on the round. Every other phone in the group picks up the new standings on its next refresh.