ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

以 Spec 为先的 AI 辅助开发工作流:cal.diy 仓库的 Spec-First Development 实践指南

以 Spec 为先的 AI 辅助开发工作流:cal.diy 仓库的 Spec-First Development 实践指南 以 Spec 为先的 AI 辅助开发工作流cal.diy 仓库的 Spec-First Development 实践指南【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy在 cal.diyCal.com 系的开源调度基础设施项目中specs/目录承载着一套名为Spec-First Development规范先行开发的 AI 辅助协作流程任何新功能在动手写代码之前必须先落成一份结构化的设计文档再由 Claude 等 AI 智能体读取设计、跟踪进度、记录决策并生成文档。本文以 specs/README.md 为骨架结合仓库中真实的cancellation-reason-requirement功能案例与对应源码完整讲解这套工作流的目录结构、启动方法、会话连续性机制、文档生成与公开推广流程以及每个 PR 必须 10 分钟内可审查这一核心约束帮助你在自己的项目中复刻一套可运行的 AI 协作开发范式。Spec-First Development为什么先写设计再写代码specs/目录是 cal.diy 仓库中所有开发中功能的设计文档集散地。它的定位非常明确Claude 等 AI 智能体通过阅读这些文档来理解要构建什么、当前进度如何从而在跨会话、跨提交的长期开发中保持上下文连续。这套模式解决的问题很典型AI 辅助开发时模型每次会话的上下文窗口有限而功能开发往往跨越多个会话、多个提交。如果缺少一份单一事实来源source of truth很容易出现模型重复实现已经完成的功能架构决策随会话丢失后续改动偏离最初意图提交内容失控产生难以审查的巨大 PR。Spec-First Development 正是为应对这些问题而设计的design.md 定义做什么和怎么做implementation.md 记录做到哪了decisions.md 保存为什么这么做三份文档各司其职配合CLAUDE.md的功能专属指令构成一个完整的 AI 协作闭环。核心工作流程五个固定步骤specs/README.md定义了每次功能开发必须遵守的五步流程实现功能之前先创建一个 spec 目录写入设计文档Claude 在写任何代码之前先完整阅读设计进度记录在implementation.md保证会话间的连续性决策记录在decisions.md即 ADR架构决策记录供未来参考功能完成时生成配套文档附带开发过程中捕获的截图。从源码结构看这套流程还得到了仓库根目录 SPEC-WORKFLOW.md 的强化——它将该流程标记为opt-in可选启用只有当开发者显式说出 use spec-driven development 或 follow the spec workflow 时才启用避免强制约束所有开发路径。如何启动一个新功能模板拷贝 一句话指令启动新功能的第一步是复制模板目录cp -r specs/_templates specs/{feature-name}命令执行后specs/{feature-name}/下会生成完整的文档骨架详见下一节。随后只需告诉 ClaudeI want to build {feature}. Heres my idea: [description]. Review the codebase and fill in specs/{feature}/design.mdClaude 收到指令后会做两件事审查现有代码库理解既有模式与约定把design.md填充成一份完整的技术设计而不是直接开始写代码。这一步是 Spec-First 的灵魂所在——设计先行实现随后。每个功能的标准文件结构specs/README.md用表格明确了每个 spec 目录的职责文件/目录用途CLAUDE.md处理该功能时给 Claude 的专属指令最先读取design.md单一事实来源——要构建什么、如何构建implementation.md进度追踪——已完成、进行中、被阻塞decisions.md架构决策记录ADRsprompts.md可复用的常见任务提示词future-work.md延期的想法与增强项docs/带截图的内部文档docs/screenshots/开发过程中捕获的截图模板的实际内容见 specs/_templates/其中CLAUDE.md与AGENTS.md内容一致包含项目上下文Project Context、开工前检查清单Before Starting Work要求先读 design.md、核对 implementation.md、参考相关目录既有模式、代码模式Code Patterns以及明确的 Dont 清单如不要添加 design.md 之外的功能不要跳过测试。仓库中已经有一个完整落地的真实案例——specs/cancellation-reason-requirement/其目录结构严格遵循上述模板CLAUDE.md、design.md、implementation.md、decisions.md、future-work.md、docs/一应俱全docs/下的 README.md 已写有功能概述与配置说明截图位置暂留空等待功能实现后补充。设计文档的设计design.md 模板拆解design.md是整套流程的核心模板 定义了七个必须覆盖的章节Overview2-3 句话说明功能做什么Problem Statement解决什么问题、为什么值得做User Stories以 As a {用户类型}, I want to {行为} so that {收益} 格式描述需求Technical Design分 Database Changes / API Changes / UI Changes 三个子节分别描述 schema 变更与迁移、新端点与 tRPC 路由及请求响应形状、组件/页面/用户流Edge Cases必须处理的边界情况Out of Scope明确本功能不做什么。以cancellation-reason-requirement的 design.md 为例可以直观看到模板如何被填充成可执行的技术方案它定义了CancellationReasonRequirement枚举的四个取值、requiresCancellationReason列的默认值、API 校验逻辑的落点handleCancelBooking.ts、UI 下拉框的插入位置以及数据流EventType 存储 →getEventTypesFromDBselect → 页面 props →CancelBooking组件校验甚至连需要穿透 props 的三个文件都逐一列出。Out of Scope 的实践价值值得强调的是design.md中的Out of Scope章节——它直接决定了 AI 智能体的行为边界。在cancellation-reason-requirement案例中明确排除了改期原因配置独立功能自定义原因下拉选项原因分析与报表。对应地该功能的 CLAUDE.md 在 Dont 清单中写有不要修改改期原因行为超出范围。这种设计文档限定范围 → CLAUDE.md 强化禁令的双重约束是防止 AI 在实现过程中顺手夹带无关改动的关键机制。会话连续性跨会话接续开发AI 会话是有状态的但状态不能持久保存。Spec-First 的解法是把状态落盘当开启一个新的 Claude 会话时只需说Continue working on {feature}Claude 会读取该功能的implementation.md从记录中断处继续工作。implementation.md的 模板 包含Statusnot-started / in-progress / complete、Completed、In Progress、Blocked、Next Steps、Session Notes几个区块。看cancellation-reason-requirement的 implementation.md状态已标记为completeCompleted 列表详细记录了 11 项工作从 添加CancellationReasonRequirement枚举到 schema.prisma第 129 行、新增requiresCancellationReason列第 269 行、创建迁移文件到 UI 下拉框EventAdvancedTab 第 691-719 行、服务端校验、props 穿透、动态标签修复等Next Steps 则列出了剩余验证事项Session Notes 补充了规划阶段背景枚举和列在规划期已加入 schema迁移已创建。这份文档本身就是一个典型的进度快照范本。文档生成功能完成后的截图流程功能进入文档化阶段后对 Claude 发出Generate docs with screenshots for {feature}Claude 将依次执行在浏览器中打开该功能捕获关键 UI 状态的截图保存到specs/{feature}/docs/screenshots/更新specs/{feature}/docs/README.md。模板文档 定义了内部文档的结构Overview、How to UseStep 1 / Step 2…每步配一张截图、Configuration Options 表格、Common Use Cases、FAQ。可以看到cancellation-reason-requirement/docs/README.md已按此结构填写了配置选项的四种取值说明截图位暂空等待实现完成后补拍——与implementation.md中 Status: complete 但还需端到端测试的中间状态完全吻合。推广到公共文档从内部 spec 到 Mintlify 文档内部文档面向开发者公共文档面向客户。当功能文档准备对外发布时对 Claude 发出Promote {feature} docs to publicClaude 将把内容拷贝到docs/{feature}.mdxMintlify 格式将截图移动到docs/images/{feature}/更新docs/mint.json导航将语言调整为面向客户的表述去除内部细节。在 cal.diy 仓库中apps/docs/content/下已有大量.mdx文件如 docs/content/apps、docs/content/deployments正是这一spec 内部文档 → 公共 Mintlify 文档管线的产物目录。prompts.md的 模板 也将 Generate Docs with Screenshots 和 Promote Docs to Public 两个流程固化为可复用提示词还包含 Sync Implementation Status、Generate Tests、Code Review、Continue Feature 等常用任务形成一套标准化的 AI 操作指令集。最重要的规则10 分钟可审查的 PRSpec-First 流程的最后一条也是 README 中被称为The Most Important Rule的硬性约束每个 PR 必须在 10 分钟内可以审查完最多改动 5-7 个文件测试文件除外最多改动 500 行每次只做一个聚焦的变更。若改动超出上述规模必须拆分为多个 PR。这条规则与implementation.md的小步实现、逐步更新工作法SPEC-WORKFLOW.md 中Implement in small pieces, update implementation.md after each形成呼应设计文档保证方向正确小 PR 保证变更可控二者共同把 AI 辅助开发的产出约束在可审查、可回滚的安全范围内。案例深潜从 spec 到源码的完整落地为了让读者理解 spec 是如何一步步映射到真实代码的这里沿着cancellation-reason-requirement的设计追踪它在仓库中的每一处实现落点。数据库层枚举 列 迁移设计文档要求新增枚举与列。对应实现位于 packages/prisma/schema.prismaenum CancellationReasonRequirement { MANDATORY_BOTH MANDATORY_HOST_ONLY MANDATORY_ATTENDEE_ONLY OPTIONAL_BOTH }EventType模型在第 287 行新增列requiresCancellationReason CancellationReasonRequirement? default(MANDATORY_HOST_ONLY)与既有的disableCancelling、disableRescheduling等核心开关并列存放。对应的迁移文件 20260115111819_add_cancellation_reason_require/migration.sql 内容简洁明了创建CancellationReasonRequirement枚举类型并为EventType表添加带默认值的列。这正是设计文档中数据库变更章节的直接产物。决策层为什么用列而不是 metadata JSONdecisions.md记录了 ADR-001在新增枚举数据库列与metadata JSON 字段之间最终选择前者。理由包括这是核心预订流程设置与disableCancelling、requiresConfirmation同级数据库层类型安全取消校验逻辑中查询更干净与同类设置存储方式保持一致。同时记录了代价需要数据库迁移。这份 ADR 展示了decisions.md的典型用法——在多个方案之间做选择时把背景、备选方案、决策与后果固化下来。校验逻辑层单一纯函数 双端调用设计文档要求在handleCancelBooking.ts中根据设置与取消者身份做校验。仓库将其抽象为一个独立纯函数 packages/features/bookings/lib/cancellationReason.tsexport function isCancellationReasonRequired( setting: CancellationReasonRequirement | null | undefined, isHost: boolean ): boolean { const requirement setting ?? CancellationReasonRequirement.MANDATORY_HOST_ONLY; switch (requirement) { case CancellationReasonRequirement.OPTIONAL_BOTH: return false; case CancellationReasonRequirement.MANDATORY_BOTH: return true; case CancellationReasonRequirement.MANDATORY_HOST_ONLY: return isHost; case CancellationReasonRequirement.MANDATORY_ATTENDEE_ONLY: return !isHost; default: return false; } }该函数体现了几处与设计文档 Edge Cases 的严格对应setting为null/undefined时回退到MANDATORY_HOST_ONLY对应空列值默认行为OPTIONAL_BOTH与MANDATORY_BOTH不区分身份直接返回固定值。此函数被服务端与客户端双端复用服务端 handleCancelBooking.ts 先判断取消者是否为 hostbookingToDelete.userId userId || bookingToDelete.user.email cancelledBy再调用isCancellationReasonRequired当!platformClientId !cancellationReason?.trim() isReasonRequired !skipCancellationReasonValidation时抛出 400 错误 Cancellation reason is required。注意platformClientId与skipCancellationReasonValidation两个豁免条件正好对应设计文档中平台用户应遵守设置与 API 调用方可选跳过校验的两类边界客户端 CancelBooking.tsx 同样先判定isCancellationUserHostprops.isHost || organizer.email currentUserEmail再用同一函数计算isReasonRequired进而推导missingRequiredReason并禁用取消按钮、在理由为空时阻止提交。为支撑服务端校验getBookingToDelete的 select 在 packages/features/bookings/lib/getBookingToDelete.ts 中加入了requiresCancellationReason: true为支撑页面渲染getEventTypesFromDB的 select 在 apps/web/lib/booking.ts 加入同名字段packages/prisma/zod-utils.ts的 eventTypeSelect第 694 行也同步引入供表单 schema 使用。UI 层高级设置下拉框与 props 穿透设计文档要求下拉框放在 Booking Questions 之后、RequiresConfirmationController 之前。实现在 apps/web/modules/event-types/components/tabs/advanced/EventAdvancedTab.tsx使用 React Hook Form 的ControllerdefaultValue取eventType.requiresCancellationReason ?? MANDATORY_HOST_ONLY再次落实空值回退四个选项分别映射mandatory_for_both、mandatory_for_host_only、mandatory_for_attendee_only、optional_for_both翻译键定义于 packages/i18n/locales/en/common.json标题文案为 Require cancellation reason / Ask for a reason when someone cancels a booking。注意代码中的!isPlatform条件与设计文档平台用户应遵守设置的边界表述相呼应——平台版事件类型暂不暴露该 UI。取值从页面到对话框的穿透链也完全符合 design.md 的规划bookings-single-view.tsx 将eventType.requiresCancellationReason传给视图组件CancelBookingDialog.tsx 声明requiresCancellationReason?: CancellationReasonRequirement | null并透传给CancelBooking。从这份案例可以清楚看到 spec 管线的价值枚举与列的默认值、空值回退行为、豁免条件、UI 插入位置、翻译键命名全部在动手前就已由 design.md 精确锁定implementation.md 则逐条追踪落地最终每一行实现都能回溯到设计文档中的某句话。可复用的实践要点模板先行所有新功能从specs/_templates/复制骨架保证目录结构与文档章节的一致性避免每个开发者各写一套单一事实来源design.md是所有实现的唯一依据Dont add features not in design.md 同时出现在模板和案例的 CLAUDE.md 中进度落盘每完成一小块就更新implementation.md这是跨会话接续开发的唯一凭据决策留痕任何多方案取舍都记入decisions.mdADR 编号递增方便未来追溯文档双轨内部docs/README.md带截图面向开发公开docs/{feature}.mdx面向客户通过 Promote 提示词自动转换小步提交5-7 个文件、500 行、单一焦点把 AI 生成的大改动强制拆碎保证 10 分钟可审查。这套流程本质上把AI 智能体当作一名远程协作者来管理给它设计文档作为任务书给它 implementation.md 作为工作日志给它 CLAUDE.md 作为行为守则再用小 PR 规则兜底审查质量。对于任何计划用 Claude 等智能体长期维护复杂代码库的团队specs/这套目录与提示词体系都是一份可以直接借鉴的工程实践范本。【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表