资讯动态

用Claude Code从零开发宠物管理App的实操记录与避坑指南

发布时间:2026/10/22 11:48:32 来源:尧图企业网站定制
最近我用 Claude Code 把一个宠物生命周期管理 App 从零到一完整地做了一遍——宠物档案、疫苗驱虫提醒、生命周期阶段判断、体重记录、健康备忘录这些模块全都没落下移动端跑在 Expo 上后端是 Node.js 加一个轻量数据库整个开发过程我主要负责拆需求和审代码写代码的重活大量交给了 Claude Code 来完成。跑完这个流程之后我的感受是AI 编码工具现在真的能扛起一个新项目的全流程但前提是你得学会正确使用它并知道哪些环节必须自己把关。这篇文章更像是一份实操记录我会按项目的推进顺序把需求拆解、上下文配置、数据建模、算法实现、移动端页面落地、踩坑排查这几个阶段逐一讲清楚。我不会刻意美化过程翻车的地方也會直接说。如果你正准备用 Claude Code 或其他类似的 AI 编程助手从一个空目录开始做项目这份记录应该能帮你省掉不少试错的时间。1. 项目拆解宠物生命周期管理App到底要做什么1.1 核心需求梳理先划出MVP边界再谈AI开发“宠物生命周期管理”听起来有不少扩展空间但落到一个可交付的 MVP 上核心就三件事记得住、算得清、提醒到。记得住宠物的基础档案包括名字、物种、品种、出生日期、性别、头像以及疫苗、驱虫、体检这些生命周期关键节点的历史记录。算得清根据物种和出生日期推算宠物当前处于哪个生命阶段幼年期、成年期、老年期并在档案页展示出来。提醒到对未来要发生的疫苗第二针、年度加强针、每月驱虫等时间点生成提醒通知推送到用户手机上。第一版我只做了这三块闭环。体重记录、喂养日志、体检报告这些功能虽然也进了需求池但我明确划到了二期。为什么一定要划这个边界因为 AI 工具最大的风险不是不会写代码而是你让它“尽量多做点”的时候它会控制不住地往项目里塞东西——多出三五个无意义的页面、加一堆没人用的依赖、写出远超实际需要的复杂表结构最后代码量爆炸上下文管理也跟着失控。所以我在项目一开始就给 Claude Code 下了死命令只按我确认过的需求清单开发不在清单内的功能哪怕明显缺失也别擅自补充。1.2 技术选型思路选组合更要考虑AI生成的容错率技术栈我最终定成了移动端 Expo React Native TypeScript后端 Express Prisma 本地文件型数据库。选这套组合我有几个具体考量。第一Expo 把 Android 和 iOS 的原生工程细节全部封装掉了。这一点对 AI 编码特别关键——如果用裸 React Native 或原生开发AI 需要生成 Gradle 配置、Xcode 工程文件这类内容这些恰恰是模型最容易编错、也最难从报错里定位的部分。Expo 项目只需要维护一份标准的 JavaScript/TypeScript 代码切到 Expo Go 或开发构建都能跑通AI 涉及的上下文就简单得多。第二TypeScript 对 AI 编码的容错极高。Claude Code 生成代码时偶尔会写出参数类型错误、字段名拼错这类低级问题TypeScript 在编译阶段就能把这些大部分拦下来。我实际开发中确实依赖了这一点——每次写完一个功能先跑一下类型检查很多问题不用等运行时就能发现。第三数据库选 Prisma 迁移方案而不是直接在代码里写 SQL是因为 Prisma 的数据模型声明非常清晰AI 理解起来也准确。它只需要生成 schema 里的模型定义迁移命令是固定的几乎不用动脑子。至于数据库文件本身一开始用本地文件型数据库足够后期要切成服务型数据库也只是改一个连接配置的问题。这套选型的核心逻辑就一句话尽量降低 AI 生成代码时的平台差异和工程复杂度把模型的注意力集中在业务逻辑上。2. 环境准备与工作流初始化让Claude Code进入状态2.1 安装配置与权限管理在项目目录里安装并认证好 Claude Code 命令行工具之后第一件事不是让它写代码而是先把工作目录的权限边界设清楚。我会把项目放在一个独立的目录里并在启动工具时只授权这个目录。这样做的原因很实际AI 工具的自主操作能力比较强目录权限如果不收窄它可能在执行命令时扫描到同目录下的其他项目或者在 git 操作时误伤无关文件。一次误操作恢复起来比省那几分钟成本高太多了。另外我强烈建议在正式开发前和一个会话里把模型选择、操作偏好这些配置调好。我在配置里开启了所有危险操作的确认提示包括文件删除、执行任意命令、修改 git 历史这类操作必须经过我确认才会继续。可能有读者觉得这样麻烦但这个项目跑下来我最大的体会就是AI 工具的破坏力随着代码量的增长而增长确认步骤看起来费时间实际上是在保护之前几个小时的成果。2.2 项目上下文文件比提示词更重要的长期记忆用 Claude Code 做新项目有一个东西比任何一次单独提示词都重要那就是项目根目录下的上下文文件。这个文件会被工具自动读取等于给每次对话注入一份项目说明书。没有这份文件之前我确实遇到过会话进行到后期 AI 突然找不到当前目录结构、把文件路径写错、甚至把不相关的文件改乱的情况。有了上下文文件之后这类问题少了很多。我的做法是写一份项目级的 CLAUDE.md内容大致如下# 项目说明宠物生命周期管理 App ## 技术栈 - 移动端Expo SDK React Native TypeScript - 后端Express Prisma SQLite - 通知expo-notifications ## 目录结构 - apps/mobile移动端代码 - screens页面组件 - components通用 UI 组件 - servicesAPI 请求层 - store全局状态 - apps/server后端代码 - prisma数据模型与迁移文件 - src/routes路由层 - src/services业务逻辑层 - src/domain纯业务规则不依赖框架 ## 常用命令 - 前端启动cd apps/mobile npx expo start - 后端启动cd apps/server npm run dev - 类型检查npx tsc --noEmit - 数据库迁移cd apps/server npx prisma migrate dev ## 编码约定 - 业务规则放在 domain 目录页面组件里不要写业务逻辑 - API 返回统一使用 { code, data, message } 结构 - 所有时间字段统一用 UTC ISO 字符串传递 - 禁止使用 any 类型 ## 硬性约束 - 不要生成 package.json 中不存在的 npm 包 - 不要修改已有依赖的版本号 - 新增第三方依赖前先向用户确认 - 未进入需求清单的功能不要擅自编写这份文件的价值在于它把项目的技术决策固化了Claude Code 每次生成代码都会受这段上下文约束。重点说两个细节一是“不要生成不存在的 npm 包”这条后面踩坑部分我会细讲这几乎是 AI 编码最容易犯的错误二是“业务规则放 domain 目录”这条能保证生成的生命周期判断逻辑是纯函数方便加单测不会和 UI 组件耦死。2.3 空目录初始化两种路线实测对比关于“从零搭建”实际有两种路径我都试过差别很大。路线 A直接在空目录里让 Claude Code 执行项目初始化命令。比如让它“初始化一个 Expo TypeScript 项目”再让它搭 Express Prisma 后端。这条路的优点是省事缺点是 AI 往往默认生成最新版本的依赖而最新版本之间经常互相不兼容。我曾遇到一个早上还能正常启动的项目因为一次全局依赖版本变动直接崩掉排查到后面发现是 Expo SDK 内部依赖的版本冲突。路线 B用官方脚手架命令手动生成基础项目再切到 AI 填充业务代码。我最终采用的是这条路。先手动跑脚手架锁定当前 SDK 对应的版本然后把项目交给 Claude Code 做后续功能开发。两条路对比下来我的结论是项目骨架这种最基础的东西人工做不会花太多时间却能避免 AI 后期因为依赖版本问题反复踩坑。别把精力花在省这几分钟初始化时间上前期的稳定性决定了后期 AI 生成的代码能不能跑起来。3. 核心功能实现数据模型、生命周期算法与提醒机制3.1 数据模型设计让表结构清晰到AI不会误解后端的第一步是数据建模。我让 Claude Code 基于需求清单生成 Prisma 模型生成之后我逐条检查过但在此之前我先自己把核心实体想清楚了这样才能校验它生成的对不对。宠物主表、事件记录表、体重记录表。主表存基础档案事件表存疫苗、驱虫、体检等所有“生命周期节点”体重表存历史体重变化。三个表足够支撑所有核心交互。我特别要求了字段级别的约束比如事件类型必须是枚举值不允许自由字符串这样后续统计和提醒逻辑才可靠。model Pet { id String id default(cuid()) name String species String // dog | cat breed String? birthDate DateTime gender String? // male | female avatarUrl String? createdAt DateTime default(now()) events Event[] weights WeightRecord[] } model Event { id String id default(cuid()) petId String pet Pet relation(fields: [petId], references: [id]) type String // vaccine | deworm | checkup name String scheduledDate DateTime doneAt DateTime? createdAt DateTime default(now()) } model WeightRecord { id String id default(cuid()) petId String pet Pet relation(fields: [petId], references: [id]) recordDate DateTime weightKg Float }这里有一个我做对了的选择在生成模型之前我先让 Claude Code 列出每个字段的用途和类型而不是直接让它生成代码。这一步看起来多余实际能防止 AI 自己设计出隐藏字段或重复表。比如它第一次生成的模型里给宠物表加了“年龄”字段我让它去掉了一一年龄应该是从出生日期实时计算的存冗余字段只会让后续更新时数据不一致。3.2 生命周期阶段判断一个确定性问题不必硬上机器学习生命周期阶段判断是这个 App 最有“智能感”的功能但它本质是一个确定性问题完全不需要机器学习。宠物阶段划分是有明确规则的以狗为例普遍意义上小型犬 1 岁前是幼年期1 到 8 岁左右是成年期之后进入老年期猫也是 1 岁前幼年7 岁之后进入老龄。不同物种差异只在年龄区间的阈值上。我让 Claude Code 把规则抽成了一个纯函数配置放在常量表里。这比让它“用 AI 判断宠物阶段”要靠谱得多——一个可解释、可测试的规则引擎比一个黑盒子在这场景下有价值得多。const STAGE_CONFIG { dog: [ { stage: puppy, maxMonths: 12 }, { stage: adult, maxMonths: 96 }, { stage: senior, maxMonths: Infinity } ], cat: [ { stage: kitten, maxMonths: 11 }, { stage: adult, maxMonths: 84 }, { stage: senior, maxMonths: Infinity } ] }; function getLifeStage(species: string, birthDate: Date, now: Date) { const ageMonths (now.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24 * 30.44); const stages STAGE_CONFIG[species as keyof typeof STAGE_CONFIG]; if (!stages) return { stage: unknown }; for (const config of stages) { if (ageMonths config.maxMonths) return { stage: config.stage, ageMonths: Math.floor(ageMonths) }; } return { stage: senior, ageMonths: Math.floor(ageMonths) }; }不要让 AI 直接写完函数就收工。我要求它补充一组边界测试刚出生 1 天的幼犬、11 个月零 29 天的幼猫、正好满 12 个月的幼犬、出生日期是未来日期异常情况、跨物种输入等。AI 编码时很多时候能写出正确的主路径但边界条件才是 bug 高发地。单测写出来之后Claude Code 在后续改动生命周期规则时就能自动回归少操很多心。3.3 疫苗提醒与时间计算统一UTC不要跟时区较劲疫苗提醒是整个 App 里最常被使用、也最容易出错的功能。核心逻辑是疫苗事件创建后还需要计算“下一次提醒时间”。第一针疫苗和第二针的间隔可能是一个月之后是年度加强针驱虫则按 30 天循环。为了让这个逻辑清晰可维护我在项目约定里明确了一条所有时间字段统一用 UTC ISO 字符串传递前端展示时才转本地时区。这个约定看起来小小一条实际避免了大量提醒时间偏移一天的 bug。Claude Code 在生成时间逻辑时可能会本能地用本地时间格式化如果你不提前约束它就可能在“下一次提醒”的计算里混进时区偏移导致用户凌晨收到一条应该在中午出现的提醒。function scheduleNextEvent(prevEvent: Event) { if (prevEvent.type vaccine) { const isFirstVaccine prevEvent.name.includes(首免); const days isFirstVaccine ? 30 : 365; const next new Date(prevEvent.scheduledDate.getTime() days * 24 * 60 * 60 * 1000); return next.toISOString(); } if (prevEvent.type deworm) { const next new Date(prevEvent.scheduledDate.getTime() 30 * 24 * 60 * 60 * 1000); return next.toISOString(); } return null; }生成提醒逻辑时我让 Claude Code 同时处理两个提前条件一是手动点击“已完成”时要能基于完成时间而不是原计划时间计算下一次二是已经过期的未完成事件不能自动无限顺延必须在界面上提示用户确认新的计划时间。这两个细节如果不提前写进需求AI 默认生成的就只是简单加天数远远不能满足实际使用。4. 前端的落实现场移动端页面、状态管理与本地通知4.1 分步生成页面组件骨架、数据、样式分开推进前端开发的第一个原则是不要试图让 AI 一次性生成一个完整页面。我实际试过让它“写一个功能完整的首页”生成结果虽然初看像模像样但里面经常混入多余的组件、不合理的布局层叠以及多余的依赖。问题在于一个完整页面的上下文跨度过大AI 很难在同一段代码里同时处理好结构、样式、交互、数据请求和异常处理。我的做法是把一个页面的生成拆成三步。第一步只生成页面骨架和组件结构第二步接数据请求第三步做样式和交互细节。第一步我给 Claude Code 的指令是这样的在 apps/mobile/screens/HomeScreen.tsx 中创建首页组件 1. 顶部区域显示宠物数量统计。 2. 下方使用 FlatList 展示宠物卡片列表卡片包含头像、名字、品种、生命周期阶段标签。 3. 宠物为空时显示空状态空状态包含一段文案和一个“添加宠物”按钮。 4. 先不写 API 调用导出 HomeScreen 组件。第二步再让它加数据请求在 services/api.ts 中导出 fetchPets(): PromisePet[] 函数调用后端 GET /api/pets 接口。 在 HomeScreen 中使用 useState useEffect 加载宠物列表 - 加载中显示居中 ActivityIndicator - 加载失败显示错误文案和重试按钮 - 不要使用 react-query - 数据请求放在 services 层不要在组件里直接写 fetch前两步跑完页面已经能显示出真实数据第三步才是视觉精修。拆开生成的好处是每一步都可以单独验证出了问题也能很快定位到是结构问题、数据问题还是样式问题。如果让 AI 一顿操作全写完报错时要排查的东西就多太多。4.2 状态管理轻量方案降低AI出错成本全局状态管理我选了 zustand 而不是 Redux理由是这种规模的 App 只需要共享几个简单的状态当前用户、当前选中的宠物、通知设置。Redux 的样板代码量很大AI 生成时的出错点也更多——action type 定义、reducer 结构、dispatch 绑定每一环都可能和实际代码不一致。zustand 的状态定义非常短AI 几乎不太可能在这一层出错。import { create } from zustand; interface AppState { activePetId: string | null; setActivePet: (id: string) void; } export const useAppStore createAppState((set) ({ activePetId: null, setActivePet: (id) set({ activePetId: id }), }));这里有一个实际教训我一开始让 Claude Code 把“当前宠物”直接存在组件本地状态结果发现从列表页切到详情页时数据要重新加载体验很割裂。后来改成全局状态并把它存在异步存储里做持久化切页面的体验就顺滑了。持久化这个需求一开始没提是我在测试时发现页面刷新后选中状态丢失才补上的。这也是一种协作方式AI 负责把代码写对产品细节要靠人来补。4.3 本地通知实现系统机制与交互细节本地通知我用的是 Expo 的通知 API。Claude Code 对这个 API 的掌握程度还可以但它第一次生成的代码里把通知权限请求放在了 App 启动时这是明显的问题——最佳实践是等到用户有明确意图比如创建第一个疫苗提醒时再请求通知权限否则用户很可能在第一次看到弹窗时就点了拒绝。我要求 Claude Code 按以下几个点调整权限请求时机后移到用户首次创建提醒事件时。Android 需要配置通知渠道名称否则通知可能不显示。提醒内容要包含宠物名字和事件类型比如“金豆的驱虫提醒到了”。通知触发时间必须用 UTC 时间戳传入 API避免时区偏移。import * as Notifications from expo-notifications; async function scheduleReminder(petName: string, message: string, triggerDate: Date) { await Notifications.scheduleNotificationAsync({ content: { title: ${petName} · 周期提醒, body: message }, trigger: { type: Notifications.SchedulableTriggerInputTypes.DATE, date: triggerDate }, }); }这个功能让我体会到为什么要在项目约定里写清“所有时间字段统一 UTC”。第一次生成的代码传的是本地时间对象在模拟器上测没问题但我已知这种代码在真实环境容易出问题。后来手动改成 UTC 字符串传入并把测试用例覆盖到“跨时区模拟”才敢放心。细节单一但逃不掉。5. 全流程踩坑实录与排查思路5.1 常见问题速查表这些坑可以提前绕开整个开发过程中碰到的典型问题我整理成了一张速查表。这些问题不是碰巧遇到的而是在 AI 编码的常见模式下几乎必然会遇到的。现象根因处理方式Expo 启动时报依赖版本冲突AI 生成新依赖时没有对齐 SDK 版本初始脚手架人工搭建新依赖必须带明确版本号并经确认会话后期 AI 找不到当前文件上下文太长模型对项目结构的记忆被冲淡CLAUDE.md 写清目录结构必要时开新会话AI 引用了不存在的 npm 包模型对第三方库记忆出现幻觉要求只能在 package.json 已有依赖中选择新增前必须确认提醒时间偏移一天时间字段用了本地时间没统一 UTC项目约定统一 UTC代码 review 时重点检查API 返回字段和前端类型对不上后端模型改动后前端类型定义没有同步让 AI 生成类型定义时同时检查 Prisma 模型数据库迁移失败修改了 schema 但未及时生成迁移文件每次改模型都执行迁移命令CI 里加迁移检查这张表里我想特别强调第二行——上下文过长的问题。Claude Code 在一个会话里干太多活之后明显会开始“犯糊涂”这不是算力问题而是上下文窗口的注意力有限。我后来形成了一个习惯一个功能点开一个会话比如“宠物列表页”单独一个会话“疫苗提醒逻辑”再开新会话。新会话会自动读 CLAUDE.md 和当前文件AI 对代码库的理解反而比长会话后半段更准确。5.2 一次时区偏移问题的完整排查过程这里举一个我印象很深的具体排查案例。某次功能测试时我在模拟器上创建了一个明天上午 10 点的疫苗提醒手机上显示的通知时间却是次日凌晨 3 点。表面上看差得不远但如果是年度加强针这种重要提醒时间错了就容易漏掉。排查路径分三步走。第一步我先确认问题出在渲染层还是数据层让 Claude Code 把创建提醒时传入 API 的时间戳原样打印出来。结果发现打印出的时间戳是 UTC 字符串数据层没有错。第二步我检查 expo-notifications 的 trigger 参数传入方式发现它接受的是 Date 对象而我在前端把 UTC 字符串转成 Date 对象时用的是new Date(isoString)这一步本身没问题。第三步问题定位前端传给通知 SDK 的 Date 对象在内部按本地时区解释但我的代码在转类型时把本地日期强行当成了 UTC 传入导致系统内部又做了一次时区换算。最终修复方式是把 createDate 直接用 ISO 字符串传给通知 API由其内部解析前端不手工干预。这个 bug 排查花了大半个晚上最后根因就是一行时区处理问题。复盘下来提前在 CLAUDE.md 里写清时间约定确实可以减少这类问题但更关键的还是测试时要多覆盖边界尤其是和“跨天”“跨时区”沾边的功能。5.3 让我警惕的“幻觉依赖”教训还有一个事情值得单独写一段。开发中期Claude Code 在某次生成头像上传功能时引用了一个听起来很像真实库的 npm 包名。我当时一眼扫过没细看直接让它在代码里 import结果一次npm install就报了包不存在。这算是我这次项目里最危险的一次失误——如果我是在生产环境这很可能导致构建部署直接中断。从那以后我加了硬性规则AI 生成的代码里出现任何 import 语句先核对依赖是否已经出现在 package.json 里新增依赖必须由我确认并且要求 Claude Code 在添加前解释这个包的用途和大致体积。这个规则成本很低但能挡住 90% 的幻觉依赖问题。AI 生成代码时对第三方包名的记忆确实不够可靠不要拿生产项目去赌这个问题。6. 复盘AI辅助开发适合做什么不适合做什么6.1 让AI发挥最大效率的三个场景这次项目跑下来我总结出 Claude Code 在三个场景下的生产力是肉眼可见的高。第一个是 CRUD 模板代码。宠物列表查询、事件新增、记录删除这类接口和页面基本是“结构已知、套路固定”AI 生成速度和准确率都很高。第二个是配置类文件的编写。比如路由、数据库迁移、通知渠道设置。这类文件格式严格内容相对独立AI 很少出错就算错了也有明确的报错信息可以定位。第三个是测试代码。让 AI 生成边界测试用例的性价比极高它能很快想出人类可能忽略的输入组合测试跑绿之后对功能改动很有信心。6.2 人工必须把关的部分与上面相对的有三个地方我坚持人工介入绝不让 AI 说了算。技术选型必须人工决策。AI 对项目背景的理解不够深它推荐的技术方案往往偏“标准答案”而不是“当前场景最优解”。比如它可能会建议上 Redis 做缓存但在这个项目规模下完全是多余的复杂度。安全边界必须人工守住。涉及用户数据权限、密钥配置等AI 的默认做法可能把密钥直接写进配置文件这种问题一旦上线就是事故。我不允许它生成任何涉及鉴权的核心逻辑这部分我全部手写。复杂业务的状态迁移也要人工把控。比如通知的“已创建 - 已提醒 - 已完成 - 已过期”状态机如果让 AI 自由发挥分布式开发很容易在边缘状态上漏掉流转路径。6.3 给新项目使用AI工具的建议清单最后按我的经验给一份浅显可操作的清单。第一上下文文件从项目第一天就写好别等项目大了再补。第二页面生成要分步骨架、数据、样式三段式推进每步验证。第三所有时间字段统一 UTC代码里面直接约定清楚。第四AI 引用的依赖包名必须人工核对不接受未确认的第三方库。第五新开会话做新功能别在长会话里堆到意识模糊。第六让 AI 生成单测是提升代码质量最省力的杠杆别省略。这六条如果从一开始就执行整个开发过程会顺畅非常多。我前期因为没有坚持其中几条后面花了不少时间去补救这是完全不必要的损耗。最后再分享一个直接可用的技巧每次和 Claude Code 开始一个阶段任务前花一分钟回想一下当前项目里最容易出错的那一类问题把它明确加进当前会话的指令里。比如做提醒功能时就说“注意时区统一用 UTC”做数据模型时就说“不要加冗余字段”。别小看这一句话AI 编码工具是需要你帮它锚定注意力的。项目做到后面你会发现真正决定开发效率的不是它能写多快而是你能否把需求和边界表达清楚。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑