如果让我用一句话说清楚这种项目的感受:用 Claude Code 从零搭一个宠物生命周期管理 App,本质上是把“产品需求→技术方案→代码实现→测试验收”这条链路,全部交给一个能听懂自然语言的结对程序员去跑,而开发者负责做决策和兜底。我这段时间正好完整走了一遍这个流程,从初始化仓库到里程碑功能上线,拿真实项目把 Claude Code 的能力边界摸了一遍,这篇就把整个过程和方法论掰开揉碎分享出来。
这个宠物生命周期管理 App 并不是一个简单的“养宠记录本”。它的核心逻辑是根据宠物的出生日期、物种和体型,动态计算当前所处的生命阶段(幼年期、成长期、成年期、老年期),再基于阶段推送差异化的疫苗、驱虫、体检和喂养建议。也就是说,用户打开 App 看到的不是一个静态表单,而是一套能感知“宠物正在变老”的提醒系统。这类项目非常适合第一次尝试 AI 全流程开发:业务边界清晰、模块划分自然、又有一些需要仔细处理的算法细节,既能检验 AI 的上限,也逼着开发者把需求拆得足够细。
技术栈我选了 React 18 + Vite + Tailwind CSS 做前端,Express 做后端 API,Prisma ORM 管数据库,开发期用 SQLite,生产期切 PostgreSQL 只需改一行连接串。期间所有功能点都通过 Claude Code 的会话式交互完成,累计对话超过 40 轮,真正跑通的代码大概 6000 多行。下面我按项目推进的顺序,把关键决策、实测踩坑和可复用的方法论一条条理清楚。
1. 为什么第一个 AI 全流程项目,我选了宠物生命周期管理 App
1.1 这个 App 解决的真实问题
养宠人群普遍有两个痛点:一是“不知道该在什么时间做什么事”,二是“同一只宠物的需求会随年龄剧烈变化”。市面上多数工具只解决了“记录”问题,却缺乏“阶段感知”。
举个例子,一只大型犬在七八岁就已经算进入老年期,需要调整钙磷比、缩短疫苗间隔观察窗口、增加关节检查频率;而一只猫在七八岁还属于中年,护理重点完全不一样。如果 App 只是让用户手动填“上次驱虫日期”,那它并没有真正理解宠物生命周期管理这件事。
所以我把这个项目定义为:一个能根据宠物档案自动推导生命阶段、生成护理计划、并按时触达提醒的系统。核心模块包括宠物档案、阶段计算引擎、疫苗与驱虫计划、体检与体重趋势、提醒中心。每个模块之间耦合度低,非常适合让 AI 分步实现。
1.2 技术选型:为什么是 React + Express + Prisma
选这套组合不是随手的决定,主要从“AI 生成代码的正确率”和“我能兜底的能力”两个维度考虑。
| 层 | 选型 | 理由 |
|---|---|---|
| 前端框架 | React 18 + Vite | 生态成熟,Claude Code 对组件写法和 Hooks 规则非常熟悉 |
| 样式方案 | Tailwind CSS | 类名即样式,AI 生成 JSX 时不容易出现样式表丢失 |
| 状态管理 | Zustand | API 极简,Store 代码量小,AI 生成后人工 review 成本低 |
| 后端框架 | Express | 心智负担最小的 Node 服务端方案,路由与中间件模型简单 |
| ORM | Prisma + SQLite | Schema 文件本身就是一份可读的数据模型文档,AI 理解成本极低 |
| 定时任务 | node-cron | 表达式直观,适合生成每日阶段检查任务 |
| 测试 | Vitest | 与 Vite 同源,跑单测快,能快速验证 AI 写的边界逻辑 |
React 和 Express 都是 AI 训练语料里出现频率最高的技术,模型对它们的“幻觉率”明显低于冷门框架。Prisma 的 schema 是声明式的,AI 不容易写出“能跑但不懂”的代码。这里有个经验:选 AI 辅助开发的技术栈,不过度追求新奇,而要追求“AI 见过足够多正确写法”。
1.3 适合 AI 搭建的核心原因
这个项目让我最满意的地方在于,它的复杂点集中在生命周期状态机,而不是在花哨的交互上。只要把计算逻辑拆清楚,剩下的 CRUD、表单、列表页几乎全是重复范式,恰好是 AI 生成质量最高的区域。
我实测的感受是:在 CRUD 和页面脚手架部分,Claude Code 的产出接近“可以直接合入”的水平;而在生命周期阶段计算这种需要精确边界条件的函数,AI 第一版通常会有 2 到 3 个边界 bug。换句话说,选一个“大部分是体力活、小部分是算法活”的项目作为 AI 全流程首秀,正合适。既能体验效率飞跃,又不至于因为核心算法全错而对 AI 失去信心。
2. CLAUDE.md:AI 团队的项目说明书,值得反复打磨
2.1 CLAUDE.md 里到底该写什么
Claude Code 启动后会自动读取项目根目录下的 CLAUDE.md 文件,把它作为整个会话的基础上下文。这个文件就是给 AI 看的“项目说明书+开发规范”,决定了 AI 是像个刚入职的实习生一样到处问,还是像个跟了你两个月的结对程序员一样直接干活。
很多人第一次用 Claude Code 时恨不得一句话把整个 App 的需求丢给它,结果得到一堆结构混乱、互相矛盾的代码。根因是 AI 对项目的业务背景、目录约定、技术栈版本完全不了解,只能靠猜测补全。
我在 CLAUDE.md 里固定写入的内容包括:
- 项目简介与技术栈,明确版本号;
- 目录结构说明,标注每个目录该放什么;
- 常用命令,包括 dev、build、test、prisma migrate 等;
- 编码规范,包括命名风格、错误处理方式和日期处理约定;
- 关键约束,比如“禁止修改已存在的数据库字段类型”“新依赖需要先说明用途”。
这个文件不是写给我自己看的,是写给 AI 看的,所以表述必须非常精确。比如不能只说“日期用 ISO 格式”,要说“所有日期字段统一使用 ISO 8601 字符串存 UTC,展示时用 dayjs 转本地时区”。
2.2 我的第一版 CLAUDE.md 实测写法
我第一版写得很简单,就十几行,但实际跑下来已经能明显减少 AI 反问“这个用什么库”的频率。这里放一个精简版供参考:
# 项目:宠物生命周期管理 App ## 技术栈 - 前端:React 18 + Vite + Tailwind CSS + Zustand - 后端:Node.js + Express - ORM:Prisma(开发库 SQLite,迁移到 PostgreSQL) - 定时任务:node-cron - 测试:Vitest ## 目录结构 - /src/main:React 入口与页面 - /src/store:Zustand store - /server:Express 路由与业务逻辑 - /prisma:schema.prisma 与迁移文件 ## 常用命令 - npm run dev:启动前后端开发服务 - npm run test:执行 Vitest - npx prisma migrate dev:数据库迁移 ## 编码规范 - 日期统一使用 ISO 8601 UTC 字符串,展示时用 dayjs 转换 - 所有错误响应统一返回 { code, message } - 逻辑函数放在 /server/services,路由不写业务写完这份文件后,我再让 Claude Code 干活,明显感觉它生成的代码在命名和目录归宿上更规矩了,不再随机往根目录丢文件。
2.3 指令文件如何随项目演进
CLAUDE.md 不是一次写死的东西。随着项目推进,我会把踩过的坑不断追加进去,让它成为一份“AI 团队的活文档”。
比如在项目第二天,我发现 AI 经常会给 Prisma 模型添加不存在的字段,于是加了约束:“修改 schema 前必须先列出完整字段清单,并与现有迁移文件核对”。又比如 AI 默认会用自己记忆中的 Tailwind 3 写法,而项目用的是 Tailwind 4,我在技术栈里直接把“Tailwind CSS(v4,通过 @tailwindcss/vite 插件使用)”写清楚,后续生成样式就没再跑偏。
这个文件的价值是复利式的:每修正一次,后续的生成质量就上一个台阶。到项目后期,我甚至可以一句话说“给提醒列表加个筛选”,它就知道要改哪个路由、哪个 store、哪个页面组件,不再需要我把文件路径和依赖关系全讲一遍。
3. 数据模型与生命周期状态机:AI 最需要盯紧的部分
3.1 从需求到 Prisma 模型的 AI 会话实录
我在一个会话里把需求描述发给 Claude Code:需要有用户表、宠物档案表、疫苗记录表、驱虫记录表、体检记录表、喂养记录表和提醒表。宠物档案要记录物种、品种、生日、性别、体重、绝育状态等字段。
第一版 schema 生成得很快,结构大体合理,但有两个问题需要人工干预。一是 AI 默认在宠物档案表里放了 lifeStage 和 ageDays 两个字段,这是明显的冗余设计。生命周期阶段本身可以由生日、物种和体型推导出来,一旦宠物生日修改或体型变化,这些落库字段很容易变成脏数据。我让 AI 删掉这两个字段,改为通过服务层函数动态计算。
第二个问题是提醒表的状态设计。AI 最初只设计了 PENDING 和 DONE 两个状态,但实际业务里需要有“跳过本次提醒”和“提醒已过期未处理”的场景。我补充后,最终状态集合是 PENDING、COMPLETED、SKIPPED、EXPIRED 四种。
修正后的 Prisma 模型大概是这样的:
model Pet { id String @id @default(cuid()) name String species String // CAT 或 DOG breed String gender String // MALE 或 FEMALE birthDate DateTime weightKg Float sterilized Boolean @default(false) bodySize String? // 小型/中型/大型,犬类判断老年期会用到 createdAt DateTime @default(now()) vaccines VaccineRecord[] dewormings DewormingRecord[] checkups HealthCheckup[] reminders Reminder[] }这段会话最有价值的经验是:AI 可以快速给出结构草案,但“该不该把某个字段落库”这种设计决策必须由人来定。如果你不干预,数据库里会堆满可推导字段,后期每次数据变更都要连带更新,完全是给自己埋雷。
3.2 生命周期阶段计算的精度问题
生命周期计算是整个项目最核心的算法。我一开始给 AI 的描述是“根据生日、物种、体型判断宠物处于什么阶段”,它给出的函数用了简单的时间差换算成年数,但边界条件有明显缺陷。
我后来自己整理了一套规则:
| 物种/体型 | 幼年期 | 成长期 | 成年期 | 老年期起点 |
|---|---|---|---|---|
| 猫 | 0-12 月 | 1-6 岁 | 7-10 岁 | 11 岁 |
| 小型犬 | 0-12 月 | 1-6 岁 | 7-9 岁 | 10 岁 |
| 中型犬 | 0-12 月 | 1-5 岁 | 6-8 岁 | 9 岁 |
| 大型犬 | 0-12 月 | 1-4 岁 | 5-7 岁 | 8 岁 |
这里最关键的一个决策是:幼年期必须以“月”为粒度计算,满 12 个月才算进入成长期,而不是简单用“出生天数/365”换算。因为 12 个月和 365 天之间本来就存在闰年误差,如果直接用天数除,某些在 3 月出生的大型犬会在 11 个月时就被错误标记为成长期。
为了让 AI 理解这个约束,我把 getLifeStage 函数的需求拆成了三条明确指令:第一,先算出精确月龄;第二,猫和狗走不同的阶段表;第三,狗的体型只影响成长期到老年期的阈值,不影响幼年期时长。修正后的算法通过单元测试后,我才允许它进入后续模块开发。
3.3 提醒任务的状态流转设计
提醒模块是整个 App 的“神经末梢”。每天定时任务会扫描未来 7 天内到期的 PENDING 提醒,生成通知记录。用户处理提醒后状态改为 COMPLETED,如果用户手动忽略,则改为 SKIPPED;如果到期超过 3 天仍未处理,任务会自动改为 EXPIRED。
这个逻辑如果交给 AI 一次性写完,它通常会遗漏“自动过期”这个分支,因为对话里没有显式指令。我采取的写法是把状态流转画成文字描述喂给 Claude Code:PENDING -> 用户点击完成 -> COMPLETED;PENDING -> 用户点击忽略 -> SKIPPED;PENDING -> 超过 dueAt 三天 -> EXPIRED。
状态机的代码生成质量很高,因为分支判定用 switch 就够了。这里的教训是:不要指望 AI 帮你补全你没提过的业务分支,但只要你把分支定义清楚,它就能非常可靠地实现。
4. 逐模块实测:AI 生成代码的验收、修正与补丁过程
4.1 第 1 天:骨架搭建与两个必须手动修的坑
第一天我让 Claude Code 初始化了整个项目的骨架,从创建 Vite 工程、安装依赖、搭建 Express 服务到跑通一个“健康检查”接口。这一步整体顺利,但中间插了两个只有人工 review 才能发现的坑。
第一个坑是 Tailwind 版本问题。Claude Code 按记忆默认配置了 Tailwind 3 的 postcss 方案,在 Vite 4 下能正常跑,但项目实际装的是 Tailwind 4,需要改用@tailwindcss/vite插件。AI 不会主动去看 package.json 里装了什么版本,它倾向于输出训练数据里最常见的配置。解决方式很简单:在 CLAUDE.md 里写明版本和配置方式,然后让它重跑一次。
第二个坑是 Prisma 的数据库连接串。AI 第一次写的是DATABASE_URL="sqlite://localhost/dev.db",这种写法在 Prisma 6 下根本不会被识别。正确写法是:
DATABASE_URL="file:./dev.db"我让 AI 自查报错信息,它也很快意识到了问题。这两个坑都很小,但如果开发者没有经验,光排查环境问题就会耗掉大半天。AI 能写出代码,但环境与版本匹配的知识仍然需要人来把关。
4.2 生命阶段计算的单元测试纠错
这一步是我在整个项目里最受益的环节。我让 AI 基于阈值表写一个 Vitest 测试文件,覆盖了这些边界:刚出生第 1 天、11 个月 29 天、满 12 个月当天、大型犬满 8 岁当天、中型犬满 9 岁当天。
跑完测试,有三个失败用例。逐一分析原因后,我发现 AI 的错误模式非常有代表性:
- 第一处是把“满 12 个月”写成了
ageMonths > 12,导致满 12 个月当天仍被判定为幼年期; - 第二处是计算月龄时用固定 30 天代表一个月,碰到 1 月出生且当前日期在 12 月 31 日的宠物,会多算近一天;
- 第三处是大型犬的老年期起点被硬编码为 8,而不是根据 bodySize 查表。
这三个错误的共性都是“边界条件粗糙”。算法函数是 AI 最容易产生幻觉的地方,因为训练数据里有大量语义相近但实现不同的人月龄换算代码。我给出的对策是:凡是涉及日期换算和阈值判断的函数,必须配套完整的边界测试,AI 写实现,测试用例也让它写,但边界值由人提供。
4.3 前端逻辑:AI 补代码与人工绑定的边界
前端页面是 Claude Code 生成效率最高的部分。宠物列表页、档案编辑页、提醒中心这些页面,它可以在每个页面 10 分钟内生成可用版本,布局直接采用 Tailwind 类完成,视觉上完全不粗糙。
但有一个地方让我意识到“人工绑定”的必要性:Zustand Store 的异步接口调用。AI 生成的 store 代码里,fetchPets这样的 action 经常返回了 Promise 但不处理 loading 状态,或者忘记在组件卸载时清理定时器。这些代码单看不报错,跑起来也没问题,只是状态管理细节经不起推敲。
我的处理方式是:让 AI 负责生成页面组件和 API 封装,人工把数据流的“获取、加载、提交、更新”四个环节在 store 里统一写一遍。不要指望 AI 自动做好跨组件状态同步,它在局部代码上很强,但全局数据流设计仍然是开发者的责任。
4.4 定时任务与 API 集成实测
最后一块是 node-cron 定时任务。每日凌晨 2 点扫描未来 7 天到期的提醒,并生成站内通知。这里 AI 踩了一个非常经典的坑:它生成的 cron 表达式是0 0 2 * * *,一共 7 段,这是标准 Unix cron 的写法,但 node-cron 的 cron 表达式是 6 段,从秒开始,7 段解析会直接把任务注册成非法表达式。
正确写法是:
0 0 2 * * *也就是“秒 分 时 日 月 周”。我让 AI 自己解释这个表达式,它马上发现多了一位。除此之外,定时任务的执行结果需要在测试环境里把调度间隔改成每分钟,方便验证。这个改动 AI 也能快速完成。
API 集成方面,Vite 开发服务器需要把/api代理到 Express 端口。我让 AI 配置server.proxy,它生成的代码少了changeOrigin: true,导致后端读取到的请求头 Host 不对,CORS 之外的代理场景会出问题。这种配置细节靠肉眼 review 很难发现,但跑一次真实请求立刻暴露。
5. 把 AI 当结对程序员:沉淀下来的新项目开发方法论
5.1 会话式开发的节奏控制
用 Claude Code 做全流程开发,最大的错误就是“一次性把需求说完”。需求颗粒度越粗,AI 生成的代码越容易散架。我跑完整个项目后总结的节奏是:一次会话只做一件事,做完验收再开下一个话题。
比如我会这样拆:
- 初始化项目与依赖;
- 定义 Prisma schema 并完成首次迁移;
- 编写生命周期计算函数并配套测试;
- 实现宠物档案 CRUD API;
- 实现前端宠物列表页与表单页;
- 实现提醒任务和定时调度;
- 联调前后端并修正代理配置。
每个阶段之间有一个共同点:完成标准必须明确。比如“生命周期计算函数必须通过全部 Vitest 用例”,这才是真正意义上可验收的节点。项目从头到尾,我保持了大约 40 轮会话,每轮不超过 20 分钟,总体上节奏是可控的。
5.2 AI 生成代码的验收清单
经过这个项目,我整理出一张 AI 代码验收清单,每轮会话结束前我都会对照检查一遍:
- 编译与测试是否通过,警告信息是否清零;
- 是否存在文件被 AI 意外修改,特别是与本次任务无关的文件;
- 数据库字段命名是否与 schema 一致,有没有出现未定义字段;
- 日期时间是否按约定格式统一处理;
- 接口错误响应是否符合项目规范;
- 关键业务分支是否有测试覆盖;
- 是否新增了未说明的依赖。
前两项最容易被忽视。AI 有时会在修复一个问题的过程中,顺手改了另一个文件的格式或换了一个新的 API 写法,而这种“全局改造”往往是你发现得最晚、修复成本最高的。
5.3 什么时候该回到人工编码
如果说实话,这个项目里大约有三成代码是我自己写的,集中在数据流设计、认证逻辑和生命周期阈值规则上。AI 在 CRUD、页面展示、表单校验、接口胶水代码这些场景表现优秀,但在以下场景必须人工介入:涉及多个实体一致性的写操作、需要严格安全校验的业务规则、以及所有“你无法清晰描述给 AI 听”的需求。
有个很直观的例子:宠物删除功能,AI 建议直接执行 DELETE 并把关联的疫苗记录一并删掉。但从数据合规的角度,删除宠物档案应该保留审计日志,疫苗记录要做软删除。这个设计决策是我提出需求后 AI 才完成的。你给它的指令越接近“正确决策”,它产出的代码才越安全。
我把这个过程理解为“结对编程”:AI 是执行者,我是架构师。它会写代码,但你得告诉它世界的规则。
写在最后的实际体会
项目跑通那天,整个提醒流程从“新建宠物档案”到“自动生成第一针疫苗提醒”完整走通,体感上确实有点兴奋。但回头看,最让我有收获的不是那六千行能跑的代码,而是摸清楚了 Claude Code 的脾气——它对已经存在于训练数据中的常规代码几乎零失误,对设计和边界问题则必须由人兜底。
如果让我给后来者一个建议,那就是不要一上来就追求“全自动”。先用小模块做一轮“AI 写代码、你写测试、再让 AI 修测试”的闭环,把 CLAUDE.md 打磨到足够清楚,再铺开做全流程。把 AI 当作一个极其聪明但业务常识为零的协作者,它就能帮你把效率翻倍;你要是把它当许愿机,最后收拾烂摊子的还是自己。
最后再分享一个小技巧:每次会话结束时,我会把“本次修正了什么问题、下次要避免什么”追加到 CLAUDE.md 里。这比任何项目管理工具都管用。因为 AI 的记忆不跨会话,但 CLAUDE.md 可以帮你把一次次踩坑的教训永久沉淀在那个项目里。