Gimbo's Universe / AI Builder · Tennis Buddy
Side project · 2026 · v1.0 收官,已上线生产

Tennis
Buddy

给业余网球玩家的社交 + 比赛记录 app —— 找到搭子、约起来、把比分记下来、攒出自己的赛季。 五个 phase、十一天收官,真实球友在真实球场用起来了。

Role
设计 + 前端 + 后端 + 部署
Status
v1.0 收官 · 已上线 tennis.gimbo.co.nz
Stack
React · Supabase · Vercel · Resend
Design
Roland-Garros × Material 3
Milestone
第一场真实比赛 · 2026-07-18
Build
5 个 phase · 11 天 · 2026-07-11 → 07-22

一个代码小白,把它 ship 了

我的本职是供应链计划员 —— 天天打交道的是表格和采购单,不是代码。 React 我写不来,也不装会。但我打网球,而且和所有球友撞上同一堵墙: 约球散落在各个群里,比分只活在脑子里。这个项目,就是 AI 时代一个代码小白 把这个痒处当真之后发生的事 —— 产品自己设计,施工交给 AI,最后 ship 给真实朋友用。 四步,不掺水地讲。

Step 01

从自己的痒处出发

每次约球都散在群聊里,每个比分都只在我脑子里。我想要一个地方,既能找到搭子,又能把历史记下来 —— 首先为自己做,不是为市场做。

Step 02

先设计,后写码

一行代码都还没有的时候,先把整个产品画完:34 个画板、每个页面每种状态,外加一份带验收标准的交付规格。真正的功夫,是决定不做什么。

Step 03

AI 写码,我拍板

React 和 SQL 是 Claude 写的;周边每个决定是我做的 —— 下一步 ship 什么、一个页面该是什么手感、哪个取舍赢。每次生产数据变更,依然由我亲手审、亲手执行。

Step 04

交给真人用

不是 demo:朋友们注册进来,反馈几天内改写了路线图,产品在一个周六早上的球场上证明了自己。Ship 出去,胜过完美。

两个老问题,放进同一个 app

业余网球玩家最常撞上的两堵墙:"今天没人陪我打" 和 "我上次跟谁打的什么比分?" Tennis Buddy 把"发现搭子"和"记录比赛"做进同一条闭环 —— 现在还加上了社交层,让每场比分都有地方聊。

Match

从邀请到最终比分

定向邀请,或谁都能加入的 open game。接受/婉拒、赛前聊天、一键加进手机日历 —— 打完按盘录入比分,胜负自动判定,比赛沉淀进历史记录。

Discover

真实俱乐部,真实球友

奥克兰东区五家真实俱乐部、真实球场数,按 NTRP 等级筛球友,看附近的 open game。访客不注册也能直接浏览真实首页。

Community

比分值得被聊起来

真实的社区 feed:发帖可附上战绩卡,评论、点赞、关注,球员主页有赛季数据和交手记录(H2H)。这是 Phase 3,已上线生产环境,不是效果图。

五个 phase,十一天

5 月上线的第一版是砍过的简化版。7 月我决定回到最初的 34 画板完整设计, 按 phase 一段段重建 —— 每一步都直接发上生产环境,真实朋友一边施工一边注册进来。 从拍板到 v1.0,十一天。

Jul 11

拍板重建

回到最初的设计:五个 phase 的施工计划,一条规则 —— 约球主循环跑通就上线,不等完整路线图。

Jul 13

约球主循环上线

Phase 1–2 进生产:数据层地基 + 完整比赛生命周期 —— 邀请、open game、赛前聊天、日历文件、按盘记分。

Jul 14

第一批真实球友

朋友们注册进来,mock 数据当晚退役。加上三步引导向导,首页对访客开放 —— 不注册也能逛。

Jul 16

自有域名 + 邮件基建

App 搬到 tennis.gimbo.co.nz,事务邮件走自己的子域名 —— 登录链接从每小时 2 封提到 30 封,直落收件箱不进垃圾箱。

Jul 17

朋友群首发

群里发了一条:当晚 +4 注册,登录链接零流失,还有人填了我从没列过的主场俱乐部。访客分析上线。

Jul 18

闭环在真实世界跑通

一天五连发:社交 feed(phase 3)、邀请邮件直接从数据库发出、过期 open game 惰性清理,以及发现层(phase 4)—— 俱乐部详情页、真实球场、真实球友。而真正重要的里程碑:朋友发来邀请 → 接受 → 赛前聊天 → 周六早上真的站上球场 → 比分录进 app。

5-2 · 2-5 · 3-5 —— 输了,如实记录
Jul 19–22

产品开始自己转起来

比验收清单更硬的证据:邀请邮件上线第二天,有球友用它反过来邀请了我 —— 接受之后,周六在俱乐部的那场球自己出现在了 Upcoming 里,全程没人推。数据库里躺着 6 条真实的关注关系。一个过期的 open game 被惰性清理悄悄收走,那是这套机制第一次真的开工。

Jul 22

v1.0 —— 最后一个 phase,和逐条验收

Phase 5 把计划收口:库内通知系统(邀请、接受、拒绝、关注、open game 广播五类触发器)、带未读角标的通知页、404 页,以及每个人主页上的赛季战绩。然后把交付规格里的十四条验收标准逐条走了一遍 —— 十三条通过,一条有意留给 v1.1。

13 ✓ + 1 条有意延后 —— 五个 phase、十一天、migrations 001–012
Jul 30

一次故障,教会我什么才叫基建

数据库连续七天零活动,免费档自动进了闲置暂停 —— DNS 被摘,app 打不开。不是封号,数据也没丢:点一下 Restore,两分钟复活。根因是那个每日体检任务只在我的电脑醒着时才跑。修复是把它挪到云上,每天早上定时打一发。

怎么搭起来的

前端 React 19 + Vite + MUI v6,设计语言取自法网(Roland-Garros)的色调和字体, 用 Material 3 的形状语言落地。Supabase 负责数据、登录、安全 —— 连邮件都从 Postgres 里直接发。 Vercel 上 push 即部署,没有需要照看的服务器。

Frontend

UI Layer

  • React 19 SPA
  • Vite build tool
  • MUI v6 components
  • Tailwind v3 layout utils
  • React Router 7 client routing
Backend

Data + Auth

  • Postgres via Supabase
  • 一张 matches 表 四种状态通吃
  • Row Level Security 规则住在数据库里
  • 列级权限 访客永远看不到邮箱
  • Magic-link 登录 无密码
Email

Outbound

  • Resend 自有子域名发信
  • 自定义 SMTP 登录链接直落收件箱
  • 邀请邮件 Postgres 触发器 + pg_net
  • Vault 密钥 代码里零 key
Deploy

Infra

  • Vercel edge CDN
  • tennis.gimbo.co.nz 自有域名
  • GitHub source of truth
  • CI/CD git push = deploy
  • Web Analytics 真实访客数据
User
Browser
React app + 本地 session
Hosting
Vercel CDN
静态资源全球分发
Source
GitHub
Source of truth · 自动部署
Backend
Supabase
Postgres · Auth · RLS
Email
Resend
登录链接 + 邀请邮件 · 自有子域名
↓ Browser ← CDN · CDN built from GitHub · Browser ↔ Supabase · Postgres → Resend → Inbox ↓

真实上线了什么

一个人端到端 —— 设计、前端、后端、部署,加上真实用户的运营。 下面每一项都在生产环境上跑着,不在路线图里。

5/5
Phase 已上线

整个计划都发出去了:数据层地基、完整约球主循环、社交层、发现层、通知层。路线图上没有留下停车的东西。

12
SQL migrations

建表、行级安全、引导流程、公开首页、社交图谱、库内邀请邮件、通知触发器 —— 每一次生产数据变更都人工审过、亲手执行。

13/14
验收标准

一行代码都还没有时就写好的交付规格,最后逐条走完。十三条通过;第十四条赛前提醒是有意延后到 v1.1,而不是悄悄抹掉。

8+
真实球友

是朋友,不是测试账号 —— 通过 magic link 注册,填的是真实资料和真实主场,包括我从没列进去的俱乐部。

1
真实世界完整闭环

一场比赛从邀请 → 接受 → 聊天 → 球场 → 录分,全程跑在产品里。这是最重要的那个数字。

Product Design React Postgres RLS / Auth SQL Migrations Email Infrastructure CI/CD Design System UX Writing Product Ops Full-stack

这次重建学到的

过程比结果有意思 —— 把真产品交到真人手里,再替他们运维下去,这几个想法我会一直带着。

Shipping

先跑通一环,别等路线图

Phase 1–2 上线时,社交层和发现层都还不存在。真实用户在施工中途就进来了 —— 他们的反馈几天内就改写了下一个 phase 该做什么。

Ops

跑在自己电脑上的任务不算基建

每日体检任务跑在我自己的机器上,机器一睡它就停 —— 一周的静默把数据库送进了闲置暂停。凡是系统要依赖的东西,都得跑在一个永远醒着的地方。

Data

一张表,四种状态

定向邀请、open game、待确认、已完赛,全住在同一张 matches 表里。更少的 join、更少的同步 bug,整条闭环只有一个 source of truth。

Security

RLS 比前端校验扎实

规则住在 Postgres 里:访客能浏览真实 open game,但 email 列永远被 revoke。前端就算被绕过,数据库这堵墙也不放行。

Design

借力用户行为,省掉 cron

过期没约满的 open game 只有发起人自己能看见 —— 那就让发起人下次打开 app 时顺手清掉。全覆盖、零定时任务、零新基建。

Users

真实用户帮你找到你看不见的

注册流程从没问过用户叫什么 —— 真人进来第一天就暴露。比分存反了,提交者自己看是对的,只有对手那侧才能看出来。

Growth

邮件就是产品基建

共享 SMTP 每小时只发 2 封登录邮件 —— 同一小时注册的第三个朋友永远进不来。自有发信域名解决了任何代码都解决不了的增长问题。

来打一场 →

Tennis Buddy 已经上线。任意邮箱登录,magic link 几秒到 —— 或者不注册,先逛逛现在开着的 open game。

打开 Tennis Buddy →

永不存储密码,登录只用邮箱 magic link(Supabase Auth)。 你的邮箱只用于发送登录链接和你主动请求的比赛邀请。