CurioSeed V4:技术选型与团队协作实践
把做 CurioSeed V4 的几个关键决定摊开来讲:Supabase 还是 Neon,React SPA 还是 Next.js,前后端分离还是 SSR,什么时候拆 monorepo,以及一套人加 AI 加规则护栏的协作方式。每一项都附踩过的坑。
English version: CurioSeed V4: stack choices and how a small team works with AI
这不是炫技。我想把过去几个月做 CurioSeed V4 的几个关键决定摊开来讲:为什么这么选、代价是什么、规模化怎么走、人和 AI 怎么协作。每一项都会附上踩过的坑,不藏。
读完你会带走三样东西:
- 我们当前的技术架构是怎样的,哪些地方是有意为之的边界;
- 三个关键选型(数据库、框架、取数)的真实取舍;
- 一套可复用的“人 + AI 协作 + 规则护栏”工作方式。
1. 当前项目的技术架构
产品定位:面向 5 到 12 岁儿童的“教 AI 宠物变聪明”育成游戏。教学、测验、知识星系、商店养成连成一个闭环。
请求链路(自上而下)
浏览器(Next.js App Router,"use client" 页面 + React Query)
│ fetch /api/*
▼
Route Handlers(src/app/api/**)
· getSessionUser 鉴权 → 401
· zod 校验入参 → 400
· safeRoute 统一错误 → 500
│
▼
server-only 数据层(src/lib/neon.ts) ← 注入会话 JWT
│
▼
Neon Data API(PostgREST)+ RLS / 原子 RPC
─────────────────────────────────────────────
认证:Neon Auth(Better Auth,cookie session) ← src/proxy.ts 路由保护
部署:Vercel(iad1,紧贴 Neon us-east-1)
技术栈一览
| 维度 | 选型 |
|---|---|
| 框架 | Next.js 16(App Router)· React 19 · TypeScript 5 |
| 样式 | Tailwind v4 · Framer Motion |
| 数据 | Neon Postgres + Data API · Neon Auth |
| 客户端 | React Query · zod |
| 测试 | Vitest + Playwright |
| 部署 | Vercel(iad1,与 Neon 同区域) |
三条架构边界
- 客户端只碰
/api/*,不直接访问数据库。 - 数据库访问只能在 server-only 数据层,组件里禁止 import neon.ts。
- 认证在 proxy.ts 兜底,未登录请求在路由层就被拦下。
2. 关键技术选型
2.1 数据库:Supabase 还是 Neon
| 维度 | Supabase | Neon(我们的选择) |
|---|---|---|
| Postgres | 是 | 是 |
| 自动数据 API | PostgREST | Data API(PostgREST 兼容) |
| 认证 | Supabase Auth | Neon Auth(Better Auth,身份存自己库) |
| 杀手锏 | 生态成熟 | 数据库分支:写时复制克隆,秒开 |
为什么选 Neon
- Serverless 加数据库分支:每个 PR 能克隆一个临时库跑迁移和测试,用完即弃。这条对我们价值最大。
- Neon Auth 加 Data API 补齐了 Supabase 的同款能力:
.from().select()、RLS、auth.user_id(),迁移心智几乎一比一。 - 与 Vercel 同区域、Serverless 亲和,延迟和扩缩容都顺。
劣势,诚实说
@neondatabase/auth和postgrest-js还是 beta(0.x),文档少。好几处只能靠读类型定义才搞清 API。- Data API 的 schema 缓存不会自动刷新:建完表必须手动点 “Refresh schema cache”,否则报 PGRST205。我们踩过。
- 打包进来的 better-auth 有一个 high 级别漏洞,等上游修。
怎么控风险
- pin 精确版本,Dependabot 盯更新;
- 迁移在 CI 的 Neon 临时分支上跑回归;
- 把“建表后刷 schema”写进 rules,让 AI 帮你记。
2.2 框架:React SPA 还是 Next.js
选择:Next.js 16(App Router)。
优势
- 一体化:路由、SSR、Route Handlers(后端)、proxy(中间件)、打包,一套搞定。
- 前后端同仓、同语言、同类型(
src/lib/types.ts共用),少一层胶水。 - Vercel 零配置部署;Server 和 Client 组件按需选。
劣势
- Next 16 破坏性变更多,且新于训练数据:middleware 改成 proxy、缓存语义都变了。人和 AI 都容易套旧 API。 对冲办法:AGENTS.md 明确警示“先读
node_modules/next/dist/docs/再写”。 - App Router 的心智负担:Server 和 Client 的边界、server-only 标记、“use client” 的传染。
对比纯 React SPA:省了自己搭路由、SSR、API、构建,代价是绑定 Next 的约定。对“要快速出全栈产品的小团队”,这笔账划算。
2.3 取数:前后端分离还是 SSR
这是我们真实纠结并切换过的决策。
最终选择:前后端分离。客户端 React Query hooks → /api/* Route Handlers → 数据层。
为什么不直接用 Server Components 取数
- 项目强交互,页面大多是 “use client”。
- Neon Auth 是 cookie 加服务端 session,客户端自管 token 跟它混用会踩坑。
- 想要清晰边界(server-only 的 neon.ts 绝不进客户端),并为将来**多客户端(App)**留 REST 契约。
优势:边界清晰好测(mock /api 即可);客户端缓存、重试、失效统一交给 React Query;数据库访问只在服务端,更安全。
劣势:比纯 SSR 多一层样板(每个资源都要 endpoint 加 hook);首屏非直出,有 loading 态;需要纪律,组件别裸 fetch、别 import neon.ts。
一个真实踩坑:并发查询里 auth.token() 出现竞争,部分请求丢 token、被 RLS 挡空。修法是用 React cache() 做请求级去重,token 拿一次复用。
折中:用聚合端点(/api/me 一次返回 profile、pet、quests、test)减少往返。纯展示或 SEO 页未来仍可用 Server Components。
3. 规模化演进:pnpm monorepo
现状:单 Next 应用,前后端同仓,Route Handlers 当后端。MVP 阶段最快。
用户量上来后,用 pnpm workspace 拆成 monorepo:
apps/
web/ Next.js 前端
api/ 独立后端服务(把重逻辑从 Route Handlers 拆出)
worker/ 后台任务:AI 对话、统计聚合、定时任务
packages/
ui/ 共享组件
types/ 共享领域类型(现在的 src/lib/types.ts)
config/ eslint / tsconfig / tailwind 预设
core/ 业务纯逻辑(quizBank / shopCatalog / rng …)
为什么是 pnpm:硬链接省磁盘,严格依赖防 phantom deps,workspace 协议,比 npm 和 yarn 快。配 Turborepo 做任务编排和缓存。
什么时候拆:后端变重(LLM 对话、实时、定时任务)需要独立伸缩和部署时;团队变大、按领域拆包各自有 owner 时。现在不过度设计。 单仓够用就单仓,拆分是用户量和团队规模触发的演进,不是一开始就上。
4. 团队协作:Git 工作流加 Issue 管理
Issue 管理需求和 Bug:需求、Bug、技术债都走 GitHub Issue,分级 high / medium / low,标来源,附修复建议。AI review 的发现也 triage 进 issue。
分支加 PR,不走 fork:内部团队直接在主仓切分支。铁律是永远基于最新的 origin/main:
git fetch
git checkout -b <type>/<topic> origin/main
命名 feat/*、fix/*、chore/*,commit 用 conventional 风格。人人都有 PR 权限,CI 绿加测试通过就可以合。PR 合并等于分支生命周期结束,后续改动走新分支新 PR。
质量闸门(CI)
| 关卡 | 内容 |
|---|---|
| Lint | 硬卡,不通过禁止合并 |
| next build | 类型检查 |
| npm test | Vitest 全量 |
| DB 分支回归 | 每个 PR 在 Neon 临时分支上跑迁移加断言 |
Code Review:人加 AI 自动 review(Gemini)。真问题进 issue,误报标注。
5. AI 协作:Codex / Claude 加 CLAUDE.md 和 rules
协作模式
人(定大纲、决策)
→ AI(Claude / Codex) 实现、查文档、写测试、开 PR
→ AI(Gemini) review
→ 人 拍板合并
三层 markdown 治理,工具无关、纯文本:
| 文件 | 作用 |
|---|---|
| CLAUDE.md(仓库根) | 项目总纲:产品、栈、命令、结构、现状、全局约定、Git 铁律。AI 进来先读。 |
| AGENTS.md | 对 AI 的硬警示,比如“Next 16 有破坏性变更,先读官方 docs,别套旧 API”。 |
| .claude/rules/* | 按目录自动加载的细则(nextjs、styling、neon、testing)。编辑哪块就自动载入哪份规则。 |
规则即护栏,把踩过的坑固化成约定。举几个例子:
- 客户端绝不 import server-only 的 neon.ts;
- 新 RPC 一律 SECURITY DEFINER;
- 装饰性随机用
seeded(),别用Math.random; - 改完 DDL 必须刷 Data API 缓存。
每踩一个坑,写回 rules,人和 AI 都不再重复踩。 核心价值是“经验沉淀为可执行的上下文”。Codex 和 Claude 都能读同一套约定,换工具不丢知识。
6. 测试体系
三层测试加 CI 闸门
| 层 | 工具 | 测什么 |
|---|---|---|
| 单元 | Vitest | 纯函数(quizBank、shopCatalog) |
| 组件 | Vitest + RTL | props 到渲染(jsdom) |
| 集成 | Vitest(node) | Route Handler:mock neon 和 guard,断言 401 / 400 / 500 和响应结构,不连真实数据库 |
| E2E | Playwright | smoke:未登录跳转、sign-in 渲染、/api 返回 401(不依赖真实 Neon) |
| DB 回归 | CI + Neon 分支 | 每个 PR 克隆临时库,跑迁移加结构化断言(表、RLS、函数、种子) |
关键约定:server-only 在 Vitest 里 alias 成空,服务端模块可被测;真实 RLS 和认证的端到端留作“需要 creds 的门控 e2e”,不进默认 CI;lint、build、test 必过才能合 main。
7. 总结与下一步
三条哲学
- 选型:用“能补齐能力的新基建(Neon)“换开发体验与演进空间;用”pin 加迁移 CI 加 Dependabot 加规则护栏“控 beta 风险。
- 架构:前后端分离,server-only 边界,错误不吞,入参校验。先把地基(稳定、可观测、测试)打牢,再堆功能。
- 协作:轻量 Git(分支加 PR,人人可合,测试把关)乘以 AI 护栏(CLAUDE.md 和 rules 把经验固化)。
下一步:功能上接 LLM 做 AI 宠物对话、家长页真实统计;加固上做原子结算、限流、接 Sentry;规模化上按触发点拆 pnpm monorepo。