PRD 在这类视频里究竟指什么
这段视频讲的 PRD,并不是传统产品经理在团队里流转的那种完整产品需求文档,而是更接近一种面向 AI 编码工具的实现说明。它的目标不是覆盖组织协作、商业背景、排期和验收流程,而是尽量把“我要做什么、怎么做、不要做什么”说清楚,让模型少猜、少跑偏。
讲者反复强调一个判断:很多人觉得 AI 生成代码“不靠谱”,真正的问题往往不是模型完全不会写,而是人的输入太模糊。你只说“帮我做个笔记应用”,模型就只能按自己理解补全;但你把页面、按钮、技术栈、离线要求、配色、跳转流程都写清楚,输出就会稳定很多。这个判断在小型项目里很实用,因为 AI 最怕的是目标不清、约束不明和边界缺失。
这类给 AI 用的 PRD,核心不是“写得像文档”,而是“把约束写明白”
视频里的示例项目是一个 Flutter 笔记应用,目标平台是 Android,要求完全离线运行,没有后端。这个例子虽然简单,但它很适合说明写法:先把项目边界钉住,再写页面和功能。边界一旦不清楚,模型就会自行脑补,比如默认加登录、默认接后端、默认做在线同步,结果就会偏离预期。
因此,PRD 里最先出现的是产品名和 Overview。产品名的作用很直接,就是让模型知道自己在处理什么项目;Overview 则用一两句话概括产品是什么、给谁用、解决什么问题。示例里把产品定义为“面向 Android 用户的简单笔记应用,用户可以方便地记录和保存日常笔记”,这就已经限定了它不是社交产品,不是协作工具,也不是云笔记系统。
再往后是 Tech Stack。这里写的不是炫技用的技术名词,而是告诉模型:框架用 Flutter,平台只做 Android,应用是离线型,没有后端。这个层次的信息非常重要,因为它决定了后面的实现路线。比如你一旦写了“无后端、离线”,那么数据存储就应该偏向本地方案,而不是让模型自己接云数据库。视频里举了 Firebase、Firestore、Realtime Database 和认证提供商的例子,本质上是在提醒:一旦项目有后端,就应该把数据库和认证方式也写清,不要留成“自己看着办”。
“问题”这一节,实际上是在告诉模型什么不能偏
视频里有一节叫 Problems,字面像“问题列表”,但从内容看,它更像“产品必须解决的用户需求”或“必须满足的设计约束”。例如:支持简单记录、无网可用、界面快而干净、不要多余功能。这个部分的价值在于,它不是描述页面,而是在限定设计方向。
这一点很容易被忽略。很多人写需求时只会列功能,却不写取舍,结果模型就会往“功能越多越好”的方向扩展。但小型工具型应用恰恰相反,重点常常是克制:只做记笔记,不做账号体系;只做本地保存,不做复杂同步;只做必要设置,不做一堆花哨能力。把这些约束提前写进 PRD,实际上是在降低模型自由发挥的空间,让生成结果更贴近目标。
页面级需求为什么要按 Screen-wise 写
这段视频最有用的部分,是它要求把功能按页面拆开写,而不是堆成一串总功能列表。因为 AI 在生成 UI 和交互流程时,最需要知道的是:哪些页面存在、页面之间如何跳转、每个页面里放什么控件、用户从哪里进入下一步。
示例里先有 Splash Screen。启动页只有一个明确要求:显示 App Logo。讲者还顺带解释了启动页的常见处理方式:可以加计时器,也可以在应用初始化完成后自动跳走;本例里不需要额外计时,直接进入主界面即可。这个写法很典型,它不是只说“有启动页”,而是顺便把行为也说清楚了。
之后是 Home Screen。这里除了显示笔记列表,还需要底部导航栏,并且底部只有两个入口:Home 和 Settings。同时,Home 页上有一个新增按钮,用户点击后进入新建笔记页。这样一来,主流程就已经被描述出来了:启动页 → 首页 → 新建页 → 保存 → 回到首页列表。对模型来说,这种流程性的说明比抽象概念更有用,因为它直接对应页面路由、按钮位置和状态更新。
New Note Screen 进一步把交互写细:用户可以输入文本,支持复制粘贴,点击保存后内容落入笔记列表,再返回 Home。这里的重点不只是“支持保存”,而是明确保存后的结果和返回路径。许多 AI 生成项目之所以体验差,就是因为“操作之后去哪儿”没有写清,结果页面逻辑会断掉。
Settings Screen 则承担附加设置功能,包括字体大小、深色模式 / 浅色模式和 About 信息。视频没有深入展开这些设置项的状态持久化方式,但它至少传达了一个写法:设置页的内容不一定很多,但每一项都应当说明它调整的是什么,以及以什么形式呈现。
组件名不是越多越好,而是要在需求清楚之后再查
视频里提到,如果记不清组件名,可以去查 Material 3 相关资料,看按钮、列表、菜单、底部导航等组件应该叫什么。这种做法本身是对的:组件名是实现层细节,不应先于需求存在。先确定页面要有什么,再去选合适组件,这样更稳。
从 Flutter 的官方文档看,Flutter 现在的 Material 组件体系已经默认走 Material 3 风格;从 Flutter 3.16 起,Material 3 默认启用。也就是说,今天如果你写一个基于 Flutter Material 的新项目,通常已经站在 Material 3 的语境里了。
不过对初学者来说,需要注意一点:把组件名写进 PRD 可以帮助模型收敛实现,但这不是强制要求。真正关键的是说明交互意图,例如“底部导航里有 Home 和 Settings”“About 用底部弹层展示”。至于它最后落成 NavigationBar、BottomSheet 还是其他 Material 组件,属于实现细化,可以由人或模型在第二轮补全。
基础 PRD 和详细 PRD 的关系
视频提出一个实用工作流:先人工写一份基础 PRD,再把它交给大模型扩写成更详细的版本。这个做法很适合初学者,因为人工负责方向,模型负责补细节。方向包括产品边界、页面结构、主要功能和关键约束;细节则可以交给模型去补,如组件建议、字段补充、颜色代码、交互状态、额外说明等。
讲者认为 Claude 在“把粗需求扩成详细 PRD”这一步里表现更强,ChatGPT 可能会漏一些细节。这类判断更适合作为经验而不是定律,因为效果会受到模型版本、提示词、上下文长度和是否启用代码代理等因素影响。不过,Anthropic 官方确实把 Claude 明确定位为可处理写作、问答、编码等任务的 AI 助手。
从教学角度看,这一步最值得学的不是“用哪个模型”,而是“扩写时要补什么”。视频里被补出的内容包括本地存储、支持版本、组件名、颜色代码,以及 CRUD 行为。这里说明了一件事:基础 PRD 负责把目标说清楚,详细 PRD 负责把实现边界说细。如果没有第一步,第二步再强也会补偏;如果只有第一步,没有第二步,生成代码时又容易漏掉工程细节。
这个 Note App 的实现思路,为什么会落到 Flutter + SQLite
示例项目要求完全离线、没有后端,这意味着数据必须保存在本地。对这种简单笔记应用来说,本地数据库确实是合理选择。视频里把这个方案落到了 SQLite 上,这个判断没有问题,因为 SQLite 天然适合单机、本地、小规模结构化数据存储。
但这里有一个工程层面的补充值得说明。Flutter 社区常见的 SQLite 插件是 sqflite,它官方主支持的平台主要是 Android、iOS 和 macOS;如果直接在 Web 上运行,通常不会像移动端那样原生可用。视频里演示时提到 Web 侧不支持、要连本地 Android 设备才能正常验证,这个方向是基本成立的。若一定要在 Web 上使用 SQLite,需要额外采用 Web 实现,例如 sqflite_common_ffi_web,而且这类方案并不是最省心的默认路径。
因此,这个案例里“离线笔记 + Flutter + Android + SQLite”是相互匹配的:需求简单,平台单一,不需要同步,不需要服务端,也不追求跨端一致数据库行为。换句话说,这个技术选型成立,是因为产品边界本身就被收得很窄。
从 PRD 到代码生成,视频演示的是一个很典型的小项目流程
讲者后半段把完整 PRD 复制到 AI 编码工具中,然后创建项目、删掉不用的平台、准备好 assets 下的应用图标,再让工具生成代码。这个流程本身并不神秘,它对应的是一种常见做法:先用自然语言把需求结构化,再让代理基于这个结构化描述去改代码库。
在这个过程中,图标资源提前准备好也很重要,因为它属于模型很难凭空精确生成的“外部输入”。视频里把 Logo 放进 assets 目录,再让生成代码引用它,这一步说明了一个实用原则:凡是资源文件、接口密钥、品牌文案、已有数据库结构这类“外部事实”,最好不要指望模型自己猜,而要在项目上下文里提前给齐。
演示结果里,应用具备了启动图标、笔记列表、新建与保存、再次编辑、设置页、深浅色切换和 About 页面。这些功能足以说明:对一个三屏左右的小型 App,只要 PRD 足够明确,AI 的确可以较快搭出一个可运行原型。
但这份视频里的 PRD,更适合小项目起步,不适合直接照搬到正式团队流程
从教学角度看,这个视频对入门者很有帮助,因为它把“不要让 AI 猜你的需求”这件事讲明白了。但如果把它放进真实团队开发,它还远远不够。原因不在于它错,而在于它的覆盖面太窄。
比如笔记应用如果真的要进入可维护阶段,PRD 至少还应补充这些信息:笔记的数据结构是什么,标题和正文是否分开,是否支持删除,删除是否需要二次确认,列表排序规则是什么,空状态怎么展示,超长文本怎么处理,主题和字体大小是否持久化,数据迁移怎么办,异常情况下保存失败如何提示。视频里没有涉及这些内容,是因为它聚焦的是“让 AI 快速做出一个能运行的小应用”,不是完整软件工程。
因此,正确的使用方式不是把这份模板当成标准答案,而是把它当成一个起点:先用它把小项目说明白,再根据复杂度逐步往里补数据模型、异常处理、验收标准和测试要求。
真正值得学会的,不是模板,而是这套表达顺序
把这段视频抽象出来,它真正有价值的地方其实只有一句话:先明确产品边界,再明确页面,再明确每个页面里的行为,再明确技术约束。
这个顺序为什么重要?因为它符合 AI 生成代码时的决策方式。模型首先需要知道自己在做什么,其次需要知道有哪些页面和流程,接着需要知道每个页面里的功能,最后需要知道哪些技术能用、哪些不能用。很多失败案例恰好反过来了:一开始就堆框架名和术语,却没有说清用户怎么走流程、页面里有什么、保存后回哪儿、哪些功能不要做。结果看似写了很多,真正能指导实现的信息却很少。
所以,这段视频虽然讲的是笔记应用,但它迁移到别的项目也成立。做网站、管理后台、移动端表单、离线工具、简单 CRUD 系统时,都可以先按这个逻辑写一份“面向 AI 的需求草稿”:项目是什么、解决什么问题、平台是什么、是否联网、有哪些页面、每个页面能做什么、页面之间如何跳转、必须满足什么约束。先把这些写好,AI 才有可能稳定地产生像样结果。
先写 PRD,再让 AI 写代码:为什么这一步能显著减少返工
这段视频讲的不是如何做一个博客模板,而是 AI 辅助开发里一个非常常见、也非常容易被忽略的问题:很多人一开始直接对着 AI 说“帮我做一个某某网站”,前几分钟看起来进展很快,但越往后越容易失控。页面结构变了,数据库结构没跟上;前端假设了一套字段,后端又是另一套;部署时才发现项目目录、构建命令、环境变量全没说清楚。结果不是不停修补,就是推倒重来。
作者的建议很简单:先花一点时间写一个 PRD。这里的 PRD 不是为了走正式流程,也不是要写成团队协作文档,而是为了在编码前把产品目标、技术栈、核心功能、数据模型和部署约束整理成一份 AI 能稳定理解的说明书。这样做的价值,不在于文档看起来专业,而在于它能把“模糊想法”变成“可执行规格”,让 AI 在更稳定的上下文里工作。
PRD 在 AI 辅助开发中的真正作用
视频里反复强调的一点是:AI 很擅长在信息充分时快速推进,但它不擅长替你猜测那些你自己都还没想清楚的地方。你没有明确说明,它就只能用惯性和概率去补全。问题在于,前端、后端、数据库、SEO、部署,这些部分一旦各自按不同假设展开,最后拼到一起时就会出现严重错位。
所以 PRD 的核心作用不是“写需求”,而是建立一个统一的约束面。你需要让 AI 知道你到底在做什么、打算用什么技术、哪些功能是必需的、哪些只是以后再说、数据之间是什么关系、最终要怎么部署。这份文档相当于整条生成链路的参考坐标。你可以把它喂给不同的工具,例如 Bolt、Claude Code、Cursor 之类,只要规格没有变,工具切换时也不至于完全失去连续性。
作者把这点说得很直接:数据库 schema 是单一事实来源的一部分。这其实很重要。很多 AI 编程失败案例,不是模型不会写代码,而是没有一个稳定的数据定义,导致 UI、API 和存储层分别朝三个方向长。
这个视频里的示例项目:一个带后台的 Astro 博客
作者示范的是一个 Astro 博客,要求 SEO 友好,视觉上是现代感的深色主题,并带一点紫色调。技术栈设定为前端用 Astro,部署到 Netlify,后端用 PocketBase。这套选择本身也说明了一件事:他不是只要一个静态页面,而是想做一个带后台能力的博客系统。
在这个前提下,PRD 里需要写清楚的就不只是“页面长什么样”,还包括系统具备哪些内容管理能力。视频里列出的核心功能包括:后台创建文章、创建用户、底部 newsletter 订阅表单、文章归档、标签、分类、规范的永久链接,以及每篇文章自己的 SEO 元数据。你会发现,只要把这些点写完整,AI 接到的任务就已经不再是“做个博客首页”,而是“构建一个有内容模型、有管理入口、有 SEO 要求的内容系统”。
这里还可以顺手补一句工程判断:如果你只是做个人技术博客,内容主要靠本地 Markdown 管理,那么 Astro 自带的内容集合通常已经很合适,未必一开始就需要外接数据库。官方文档也把内容集合作为 Astro 里管理结构化内容的推荐方式。只有当你明确需要在线后台、用户管理、内容录入或运营权限时,引入 PocketBase 这类轻量后端才更有意义。
为什么让模型先提问,再写 PRD,很有价值
视频里一个很实用的小技巧是,在给出初始需求后,再补一句:“如果有不清楚的地方,先问我,然后继续写 PRD。”这一步看似简单,实际非常有用,因为它会强迫模型把那些隐含歧义显式暴露出来。
例如,后台支持“创建用户”,这句话到底意味着什么?是只有管理员?还是有 editor、contributor 等角色?文章编辑用富文本编辑器,还是 Markdown 就够?newsletter 只是嵌入一个表单,还是要接第三方 API?SEO 只要标题和描述,还是还要 sitemap、robots、schema markup、Twitter Cards?这些问题如果不在文档阶段澄清,AI 通常会自己代你做决定,而那个决定未必符合你的真实意图。
这种做法的本质,是把“生成代码前的需求澄清”前置到文档阶段。你不是在和模型闲聊,而是在借它的追问能力做需求查漏。对个人开发来说,这比套一套项目管理模板更有实际价值。
PRD 里哪些内容最值得写清楚
从视频演示来看,作者最看重的内容可以归成几类。
第一类是产品层面的边界:你到底在做什么,核心目标是什么,视觉风格是什么,哪些功能是第一阶段必须要有的。这里不是写空话,而是帮 AI 确定它生成的是博客、目录站、内容平台,还是别的东西。
第二类是技术和部署约束:前端框架是什么,后端是什么,部署到哪里,是否需要环境变量,项目根目录在哪里,构建命令和发布目录是什么。比如 Astro 部署到 Netlify 时,官方文档就明确支持通过 netlify.toml 配置构建命令和发布目录;常见设置是 npm run build 和 dist。如果这些信息不清楚,AI 就很容易在仓库结构或部署配置上出错。
第三类是信息架构和 SEO。博客不是只有首页和文章页,往往还会有标签页、分类页、归档页、关于页、订阅入口等。SEO 也不只是给每篇文章加个标题而已,通常还要考虑 sitemap、robots、canonical、社交卡片和结构化数据。Astro 官方提供了 @astrojs/sitemap 集成,站点 URL 需要在配置中明确给出,否则 sitemap 无法正确生成。
第四类,也是作者最强调的一类,是数据模型。文章有哪些字段?分类和标签是怎么挂接的?用户和内容之间是什么关系?角色体系是否真的需要?如果模型阶段没说明白,AI 后面做出来的“可运行界面”很可能只是表面上能看,真正接后端就会散架。
数据库 schema 为什么是最关键的部分
视频里最值得记住的一句话,其实就是:没有 schema,AI 就会猜。 一旦让它猜,前后端就会各自长出自己的理解。最典型的情况是:前端默认文章只有一个分类,后来你又想加多个分类;或者 UI 上已经支持标签、分类、作者、草稿状态,但后端记录结构只建了标题和正文。此时你会发现,问题不是单个 bug,而是整个模型设定从一开始就没对齐。
作者举了 post 和 categories 的关系来解释这个问题。一个文章可能对应多个分类,而一个分类下也可能有多篇文章,这就是典型的 many-to-many 关系。很多初学者最容易在这里偷懒,先把它写成一个简单字段,等功能扩展时再补;但如果 AI 是基于这个简化假设生成界面和逻辑,后面返工成本会很高。
PocketBase 的数据模型是基于集合和记录展开的,也支持关系字段与权限规则,因此它可以承载这类内容系统。但是否直接用“多值 relation”,还是显式建一个中间集合,要看你对查询、扩展和权限的需要。视频里没有展开到这个深度,但把 schema 先写出来,至少能让 AI 不至于凭空捏造关系。
从工程角度看,一个更稳妥的博客最小模型往往包括:
postscategoriestagsusers- 以及在需要时用于多对多关联的中间结构
至于 editor、contributor 这种角色,作者自己也承认,如果真只是个人博客,第一阶段大概率不需要。这个判断很重要:PRD 不是把所有看起来专业的东西都写进去,而是明确哪些是 MVP,哪些是以后再说。
“只接最少必需集成”是更稳的做法
视频中还有一个很好的判断:newsletter 先不要急着做重集成。作者最初提到 ConvertKit,后来又说其实不需要完整 API,只要表单 form ID 就够了。这种修正体现的其实是很成熟的工程意识:先用最小可行方案打通流程,而不是一开始就把所有外部集成做重。
这点也能从官方资料得到印证。Kit(原 ConvertKit)提供表单嵌入方式,对博客场景来说,先通过嵌入表单完成订阅收集通常已经足够,不一定非要先接 API。
对 AI 编程来说,这种“先收紧边界”的方式尤其有用。因为每多一个第三方 API,就多一层认证、错误处理、环境变量和部署依赖。你在 PRD 里写得越精确,AI 越不容易把简单需求做成一团过度工程。
用 PRD 驱动不同 AI 工具时,应该怎么看它们的表现
作者后面把同一份 PRD 分别喂给 Bolt 和 Claude Code,本质上是在测试:当规格足够清晰时,不同 agent 的执行风格会呈现什么差异。
从他这次 demo 的结果看,Bolt 在“快速出页面、做出可见结果”这件事上表现更好,短时间内做出了更多页面,甚至带上了后台雏形,部署也比较顺。Claude Code 则更像一个在本地目录里连续工作的 agent:先出计划,再维护待办,再逐步执行命令,整体更接近真实工程流。官方文档也说明了 Claude Code 的权限模式会影响它何时暂停请求确认,用户可以通过 Shift+Tab 切换不同模式。
这说明一个现实问题:不同工具的优势不一样。浏览器内一体化 agent 往往更适合快速原型和视觉结果,本地终端型 agent 往往更适合持续修改、目录级操作和后续接入真实项目结构。视频作者最后也没有把结论说成“谁绝对更强”,而是认为:这次是 Bolt 更快更完整,但数据库接入这类后续工作,也许 Claude Code 会更顺手。
这个结论本身并不重要,重要的是:一份清楚的 PRD 能让你更公平地比较工具,而不是让工具替你决定项目形状。
部署失败时,问题往往不在框架,而在项目结构
视频后半段一个很真实的片段是:Claude Code 生成的项目在 Netlify 部署时失败了,报出 package.json is missing 一类问题。作者一开始怀疑是 Astro 难部署,但后面逐步发现,真正的问题更像是仓库根目录、项目子目录、构建目录之间没对齐。最后通过调整文件位置和配置才成功。
这类问题在 AI 辅助开发中非常常见。AI 可以帮你写出页面和配置,但“仓库根目录是不是应用根目录”“部署平台读取的是哪一层目录”“netlify.toml 放在哪里才生效”这些问题,如果你在 PRD 或部署规格里没有说清楚,它不一定会替你做出正确判断。
对于 Astro + Netlify 这种组合,最稳妥的方式通常是:
- 明确项目根目录;
- 明确构建命令;
- 明确发布目录;
- 明确站点 URL;
- 明确环境变量是否必须;
- 尽量避免“Git 仓库根目录外面再套一层真实应用目录”的混乱结构,除非你就是故意做 monorepo。
这也是视频想传达的另一个隐藏结论:部署规格也是产品规格的一部分,不该留到最后才碰运气。
这套方法真正适合什么场景
视频虽然举的是博客,但方法并不局限于博客。只要你准备让 AI 辅助你做一个有一点复杂度的项目,例如内容站、目录站、后台面板、小型 SaaS 原型、内部工具、轻量 CMS,这个思路都适用。尤其是当项目同时涉及页面结构、数据模型、权限、部署、第三方集成时,先写一份“足够短但足够具体”的 PRD,收益会非常高。
它最适合的不是大团队正式项目,而是下面这些情况:
- 你自己知道大概要做什么,但还没有把规格说清;
- 你要在多个 AI 工具之间切换;
- 你很怕做到一半发现模型不一致;
- 你不是专业后端开发,但又需要一个最起码能落地的数据结构;
- 你希望边做边学,而不是一次性全懂再开始。
它不适合的,是那种极小、极一次性的任务,比如“帮我生成一个单页 landing page”。这种场景下直接上手可能更快。但一旦项目里出现数据库、SEO、后台、角色、部署这些词,PRD 的价值就会迅速变大。
一个更稳的实操顺序
结合这段视频,可以把作者的方法整理成一个更可复用的顺序。
先用几句话描述产品:做什么、给谁用、风格怎样、最重要的结果是什么。然后立刻把技术栈写死:前端、后端、部署、第三方服务分别是什么。接着把你确定一定要有的功能列出来,不要把“以后可能会加”的内容混进 MVP。之后主动要求模型提问,把角色、编辑方式、SEO 细项、表单接入、路径结构这些容易含糊的地方澄清掉。
等 PRD 初稿出来后,不要急着生成代码,先审三件事。第一,看页面和功能结构有没有跑偏;第二,看第三方集成是不是做重了;第三,看数据库 schema 是否完整、关系是否合理。只有这三件事都过了,再把文档喂给 AI coding agent 去实际生成项目。这样做虽然比“一句话开干”多花几分钟,但后面通常会省下成倍的返工时间。
从想法到表结构:为什么数据库设计不能只靠生成
这段视频最想强调的一件事是:数据库设计不是“先让 AI 生成几张表,再慢慢修”。真正的顺序应该反过来——先把业务对象、边界和关系想清楚,再把它们落成表。AI 可以补语法、补样板,但它不会替你决定哪些对象该独立建模,哪些关系应该保留历史,哪些字段只是当前业务规则、不能拿来做长期标识。
视频用的是一个股票投资组合平台的例子。这个例子选得不错,因为它同时包含了用户、组合、股票、持仓、交易、自选股、偏好设置、AI 分析等多种对象,既有基础主数据,也有用户私有数据,还有历史记录和多对多关系。这样的题目很适合练习 ERD。
先写一个够用的 PRD,再从 PRD 抽实体
视频没有让人先写一大份复杂需求文档,而是先给了一个很短的 PRD:平台允许用户创建账户,管理投资组合,跟踪持仓与交易,维护自选股,并接收 AI 生成的分析建议。这个做法是合理的,因为数据库设计最怕的不是“文档不够长”,而是“对象没定义清楚”。
从这个 PRD 往下拆,比较自然就能得到八个核心实体:users、portfolios、stocks、holdings、transactions、watchlist_items、ai_insights、user_preferences。这一步的关键,不是机械地把名词全变成表,而是判断它们是否具有独立身份、独立属性和独立变化过程。比如 stocks 是全局共享主数据,portfolios 是用户下的聚合对象,transactions 是会不断追加的历史事件,holdings 则是“组合与股票之间的持有关系”这个中间业务对象,本身就值得单独建模。
这也是这段视频最有价值的一点:它没有把“持仓”粗暴塞进 portfolios 或 stocks 里,而是把它拆成独立实体。因为“某个组合持有某只股票”这件事本身就会有数量、成本、建仓时间、当前市值等属性,它不是简单的一条引用关系。
表名、字段名与时间戳:这些不是语法规则,但会影响长期维护
视频先从最表层的规范开始改:表名统一用小写、复数,字段名统一用 snake_case。这不是 SQL 语义上的强制要求,而是一种非常常见的工程约定。它的价值不在“更高级”,而在于团队协作时更稳定。数据库一旦进入联表查询、迁移脚本、ORM 映射、BI 报表和日志排障阶段,命名不一致会迅速放大维护成本。
所以像 user 改成 users,firstName 改成 first_name,本质上是在做可维护性设计。视频把这部分称为“优化”,是成立的。它不改变业务能力,但会直接影响后续使用体验。
时间戳部分,视频建议所有表都加 created_at 和 updated_at。这个建议很常见,也很实用。它特别适合排查批量异常写入、做审计、做数据回溯,以及未来补分析需求。视频举了一个“恶意刷注册”的例子:如果表里只有邮箱而没有创建时间,你想按攻击时间窗口回滚脏数据会很难;有了时间戳,就能更容易定位异常数据批次。
不过这里要把边界说清楚:时间戳不是关系型数据库的必选项,而是一种强工程导向的建模习惯。对业务表来说,大多数时候值得保留;但对极少数纯字典表、临时中间表或极端性能敏感表,是否都要统一加上,仍然取决于系统需求。
主键不是“找一个唯一字段”就结束了
视频接着进入主键设计,这部分讲得比较到位。它强调:数据库里每一行都必须有办法被稳定地识别。仅仅“当前看起来不重复”还不够,一个适合作为主键的列,至少要满足唯一、非空,并且尽量稳定,不要轻易变更。数据库文档对主键的约束语义也明确说明了这一点:主键要求唯一且非空,并作为可供其他表引用的主要标识。
他用 email 做了反例。把邮箱设成主键,问题不在“今天能不能唯一”,而在于它是业务规则的一部分。用户能改邮箱,产品也可能改登录方式,甚至未来完全不用邮箱。只要业务字段会变,它就不适合承担数据库长期标识的责任。这种思路比单纯背“主键必须唯一”更重要。
不过视频有一个地方需要补正:它把 id 直接写成字符串,容易让初学者误以为“主键就应该是 string”。更准确的理解是,主键可以是整数、自增序列、UUID、雪花 ID,甚至联合主键;数据库并不要求主键必须是字符串。真正重要的是约束语义和稳定性,而不是具体数据类型。
外键的意义不是“找同名字段”,而是把引用关系写进约束
视频把主键和外键区分得比较清楚:主键负责唯一识别本表中的一行,外键负责把这行数据和别的表关联起来。比如 portfolios.user_id 不是为了让组合本身可唯一识别,而是为了说明“这个组合属于哪个用户”;transactions.holding_id 不是为了给交易当主键,而是为了说明“这笔交易作用于哪个持仓”。
数据库约束层面,外键要求引用目标表中的主键或唯一约束列,用来维持引用完整性。也就是说,子表里写下来的值,必须能在父表里找到对应的那一行。
这部分还有一个容易漏掉的工程点:外键是逻辑约束,不等于性能优化。以 PostgreSQL 为例,主键和唯一约束会自动建唯一索引,但外键所在的“引用方列”不会自动建索引;如果这些列经常用于联表和筛选,通常应当手工加索引。 这也是为什么很多系统“关系建对了,但查起来还是慢”。
这份 ERD 里最关键的几组关系
用户与偏好设置:一对一不是“口头上一对一”,而是约束上一对一
视频把 users 和 user_preferences 设计成一对一,这是合理的:一个用户通常只有一份当前偏好设置。它前面先加了 user_preferences.user_id 外键,后面又补充说,要把这个外键设成唯一,才能成为真正的一对一。
这一点非常重要。仅有外键只能表达“这条偏好记录属于某个用户”,却不能阻止同一用户拥有多条偏好记录。只有再加 UNIQUE(user_id),数据库才会强制每个用户最多对应一条偏好记录。唯一约束会自动创建唯一索引来保证这一点。
顺便说一句,视频结尾复盘时把这一段口误成了“一对多”,这和前面的建模不一致。真正应该记住的是:这张表的业务意图是一对一,约束实现也应是一对一。
用户与投资组合:典型的一对多
users 到 portfolios 是最典型的一对多:一个用户可以有多个组合,但每个组合只能归属一个用户。这个关系没有太多歧义,也很符合业务直觉。真正重要的是后续查询路径:系统里很多页面其实都会沿着这条链路走,比如“查某用户所有组合”“统计某用户组合数”“删除用户时如何处理其组合”。
所以这类关系不仅是 ERD 上一条线,还是后续权限模型、删除策略和索引策略的入口。
组合、持仓、股票:这里才是视频里最值得学的一段
这段设计的重点不是“表与表怎么连”,而是要理解 holdings 的角色。一个 portfolio 里会有多个 holding,这是 portfolios -> holdings 的一对多;一只 stock 也会出现在许多不同用户、不同组合的持仓里,这是 stocks -> holdings 的一对多。于是 holdings 同时持有两个外键:portfolio_id 和 stock_id。
视频中间一度把 portfolios.id 错连到 holdings.stock_id,后面自己修正成 holdings.portfolio_id,这个修正很关键。它说明设计关系时不能只看“谁和谁有关”,而要看“这个字段到底代表哪一层含义”。stock_id 表示持的是哪只股票,portfolio_id 表示这笔持仓属于哪个组合,这两个维度不能混。
从工程上看,holdings 其实是一个带业务语义的关系实体。它不是单纯的桥接表,而是投资系统里的核心状态对象。交易记录会追加到它下面,当前持仓数量、均价、浮盈亏之类的派生状态通常也围绕它展开。
持仓与交易:为什么是一对多而不是一对一
holdings -> transactions 被设计成一对多,这也是正确的。一个持仓的形成和变化通常由多笔买卖交易累积而来。如果做成一对一,就相当于把一个持仓压缩成只能有一条历史事件,这会让交易历史、成本计算、对账和回放都失去意义。
这类设计体现了一个非常实用的建模判断:只要业务对象会随时间积累事件,通常就要有一张历史事件表,而不是把历史直接覆盖在当前状态表里。
组合与 AI 洞察:这是一个可根据产品策略变化的关系
视频把 portfolios -> ai_insights 设计成一对多,理由是要保留 AI 分析历史。这是合理的。因为 AI 洞察往往会随着市场行情、组合变动、分析模型版本变化而更新,保留历史可以让系统支持“上次分析”“趋势对比”“模型版本回溯”。
但视频也提到,如果产品只想保留当前最新分析,也可以改成一对一。这里很值得学习,因为它说明关系不是教条,关系是产品决策的投影。是否需要历史,会直接决定是一对一还是一对多。
多对多关系不要直接画两条线,要落成中间表
视频里 users 和 stocks 的关系,是“用户可以关注很多股票,一只股票也可以被很多用户关注”。这就是标准的多对多。它正确地引入了 watchlist_items 作为中间表,让这张表分别通过外键连接 users 和 stocks。
数据库文档中的典型多对多实现也是这样:通过中间表同时引用两边,并且常常把两列组合成联合主键,以防止重复关系。 这意味着在这个例子里,watchlist_items 最好不要只放一个独立 id 就结束,而应进一步保证 (user_id, stock_id) 唯一。否则同一用户把同一股票重复加入自选,数据库层面并不会阻止。
所以,一个更稳妥的做法通常是二选一:
- 直接用
(user_id, stock_id)做联合主键; - 或保留单独
id,再加UNIQUE(user_id, stock_id)。
前者更紧凑,后者在 ORM 和后续扩展字段时有时更方便。
只有 ERD 还不够,生产可用还需要补约束、索引和删除策略
视频标题里用了“production ready”,但严格来说,它完成的是“结构上合理的第一版 ERD”。离真正生产可用,还差三类东西。
第一类是业务约束。比如 users.email 通常应该唯一,stocks.symbol 往往也应该唯一;transactions.type 应该被约束在合法枚举范围内;数量和金额字段常常应配合 NOT NULL、CHECK (quantity > 0) 之类的约束来防止脏数据。数据库文档也建议多数列应明确是否允许空值,而不是全部靠应用层默认处理。
第二类是性能约束。主键和唯一约束会自动建唯一索引,但外键引用列不会自动建索引;如果 portfolio_id、stock_id、holding_id、user_id 这些列经常参与联表和过滤,通常应主动建索引。
第三类是删除与更新策略。比如删除用户时,是不是联动删除其组合、偏好、自选?删除组合时,持仓和 AI 洞察是否级联删除?删除股票主数据时,是否应该禁止,避免把历史交易打断?数据库提供了 NO ACTION、RESTRICT、CASCADE、SET NULL、SET DEFAULT 等动作,不同对象该选哪一种,取决于这两张表表达的是“组成关系”还是“独立对象关系”。文档对这些动作的语义和适用方向给了明确说明。
用这套标准看视频里的例子,比较自然的思路通常是:
users -> portfolios:常见做法是删除用户时连带删其私有数据;portfolios -> holdings、portfolios -> ai_insights:更像组成关系,级联删除常见;holdings -> transactions:交易是持仓历史的一部分,通常也会一起删;stocks则更像共享主数据,往往不应被轻易删除,至少不能随意级联删掉大量历史数据。
这份视频最值得学的,不是画图工具,而是建模顺序
整段视频里,真正值得迁移到别的项目中的不是 Eraser 的写法,也不是字符串 id 这种实现细节,而是它的顺序意识:
先定义产品范围,再抽实体;先分清对象职责,再谈关系;先有主键和外键,再谈一对一、一对多、多对多;最后回头检查设计是否自洽。数据库设计的难点从来不在“怎么写 CREATE TABLE”,而在“哪些东西该单独存在,哪些规则必须由数据库保证”。
如果把这个顺序掌握住了,那么无论你设计的是电商、SaaS、工单系统,还是这类投资平台,方法都是通的。