Community编程与开发github.com

Baukaalm/system-design-first

在构建具有自身数据流的特性之前,先进行系统设计:需求、负载估算、现有与目标架构、模式权衡,以及包含“为什么”的简短设计说明。

system-design-first 是什么?

该技能(Skill)强制要求在进行任何涉及新服务、端点、存储、集成、实时通道、事件管道、定时任务或数据模型变更的开发任务之前,先完成系统设计。核心思想是通过结构化的六步流程(范围界定 → 负载估算 → 现有架构映射 → 目标架构设计 → 风险探究 → 总结与可视化),将隐藏在需求背后的关键问题(如用户量级、延迟、一致性、处理瓶颈等)显式化。它提供了一套完整的设计摘要模板,并集成了丰富的参考资料库,用于估算、模式选择和前端特有设计考量。该技能适用于 Claude Code 等 AI 代理,能够引导代理在编码前进行严谨的工程思考,产出包含取舍理由和量化依据的高质量设计文档,防止过度设计或设计不足。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Baukaalm/system-design-first/tree/HEAD

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

系统设计先行

大多数任务单只描述要构建什么,却隐藏了决定特性能否在生产环境存活的那些问题:多少用户、多频繁、多少数据、当依赖服务变慢时会发生什么、当两个写入者竞态时谁拥有真相。此技能让这些问题在代码编写之前变得显式化,并以任务作者能够阅读和提出异议的形式呈现。

设计过程包含:范围界定 → 估算 → 高层设计 → 深度探究 → 总结,并应用于实际工作:有一个现有代码库、一个有意图的作者和一个预算。目标是获得从系统视角出发的清晰特性图景。

使用用户写作的语言回复。设计摘要为他们而写,不是为你。

1. 首先评估深度

为拼写错误修复做过度设计与为支付流程设计不足一样糟糕。选择合适的级别,用一句话说明,并据此分配精力。

级别典型任务你的产出
L0 – 微不足道文案修改、重命名、样式调整、有明显局部修复的 bug无需额外内容。如果存在隐藏风险,最多一句话("此标签在 CDN 中也被缓存了 24 小时")。然后直接执行任务。
L1 – 局部修改新增字段、新过滤器、小的端点变更、一个钩子、查询调整一段 3-7 行的内联设计检查:它触及了什么、它承载的负载、可能会出问题的一件事(N+1、缓存失效、分页、幂等性)。然后实现。
L2 – 特性新页面及其数据流、新端点 + 存储、通知、搜索、导出、与第三方集成设计摘要(第 4 节),大约一页。然后根据摘要实现。
L3 – 子系统新服务、实时通道、事件管道、多租户数据模型、热路径迁移、任何包含 "扩展" 一词的任务单完整摘要加上每个有风险组件的深度探究、备选方案、迁移路径、发布后需要衡量的指标。

将任务提升级别的信号:用户可见的延迟、并发写入者、扇出(一个事件 → 多个接收者)、表或列表的无界增长、第三方速率限制、金钱、PII、任何按计划运行的任务,或明显会保留的"临时"解决方案。将任务降低级别的信号:只有少量用户的内部工具、永远适合内存的数据、带有终止开关的特性标记实验。

如果用户明确要求进行设计评审或更深入的分析,无论规模大小都视为 L3 处理。

2. 设计过程,逐步执行

按顺序执行这些步骤。对于 L1,将它们压缩成几行;对于 L2/L3,每个步骤成为摘要的一个章节。references/framework.md 包含每个步骤的问题清单;第一次在会话中执行 L2+ 过程时请阅读它。

第 1 步 — 理解与范围界定

  • 从系统的角度用一段话重新描述该特性:参与者、触发器、输入、输出、副作用。
  • 将功能需求(它做什么)与非功能需求(做得怎么样:规模、延迟、可用性、一致性、持久性、安全性、成本、可运维性)分开。任务单几乎从不说明后一种类型;从产品上下文中推断它们,并说明是推断的。
  • 写下不在范围内的内容,这样摘要就不会悄悄膨胀。
  • 收集作者的意图:任务单文本、PR 描述、评论、链接的设计、现有的 ADR、此仓库中类似特性是如何构建的。作者通常头脑中有一个设计;将其呈现出来而不是取代它。
  • 只向用户提问那些会实质性改变设计的问题(例如"必须在一秒内可见,还是一分钟也可以?")。对于其他所有问题,陈述一个假设并继续前进;假设会写入摘要。

第 2 步 — 估算

数字将意见转化为决策。即使任务单中没有数字,也要进行粗略估算;references/estimation.md 包含参考数字(延迟阶梯、2 的幂、可用性九位数、每节点容量)和示例计算。

  • 用户 / 租户 → 日活 → 每日操作 → 平均和峰值 QPS(峰值通常是平均值的 2-5 倍,对于突发流量如"每个人都在 9:00 打开应用"则更多)。
  • 读写比、负载大小 → 每日/每年存储量、带宽。
  • 增长:如果产品成功,12 个月后的数字是多少?
  • 在存在真实数字的地方(分析系统、数据库行数、现有仪表盘、配置限制),使用它们并引用来源;否则给出一个范围并标记为假设。

然后用这些数字来判断模式是否合适,而不仅仅是证明它们合理:"在 30 写入/秒的情况下,带索引的 Postgres 表没问题;不需要队列来提高吞吐量,只有当我们想要重试和与电子邮件提供商解耦时才需要。"

第 3 步 — 映射当前设计(现有架构)

在描述之前阅读代码。命名真实的文件、模块、表、端点、任务、缓存、队列。不要发明组件。

  • 该特性触及哪些组件?用简短的文本图绘制数据流(客户端 → BFF → 服务 → Postgres、事件 → 队列 → 工作者)。
  • 状态在哪里,谁拥有它(数据库、缓存、客户端存储、URL)?
  • 哪些现有原语可以重用(发件箱表、任务运行器、查询缓存、WebSocket 中心、共享的 Pagination 组件)?
  • 热路径、已知瓶颈、现有单点故障、该特性继承的当前限制(速率限制、页面大小、超时)。
  • 对于前端任务,现有架构仍然是一个系统:数据获取层、缓存策略、打包边界、实时传输——请参阅 references/frontend.md。

第 4 步 — 提出目标设计(目标架构)

从作者的意图开始。如果意图合理,就确认并收紧它;如果它与数字或众所周知的故障模式冲突,则展示冲突及其后果,并提供能够解决问题的最小改动。作者做决定;你的工作是让权衡变得可见。

  • 作为文本图的高层设计,加上 API 契约(端点 / 事件 / 消息及其形状)和数据模型(表、键、索引、保留策略)。
  • 对于每一个非平凡的选择,命名模式和你拒绝的替代方案,并给出与第 2 步数字相关联的理由。references/patterns.md 是目录:扩展、缓存、异步/队列、数据存储和一致性、API 风格和实时传输、速率限制、ID 生成、可靠性、可观测性——每个都有"何时使用 / 何时避免 / 权衡"。
  • 将特性映射到一个规范系统(如果合适)("这是一个通知系统:队列 + 每通道工作者 + 去重","这是新闻源扇出","这是一个速率限制器")。目录的最后一个章节列出了规范形状及其标准构建块。类比为读者提供了一个已知参考点和一个此类系统通常需要什么的事项清单。
  • 优先使用仓库的现有原语和满足数字要求且有预留空间的最简单设计。明确说明哪些升级被推迟以及触发它们的信号("当此查询的 p95 超过 200 毫秒或表超过 5000 万行时,添加一个只读副本")。

第 5 步 — 深度探究可能出问题的地方

对于 L2,选择风险最大的一两个方面;对于 L3,全部检查。标准问题,大致按照它们出问题的频率排序:

  1. 并发与幂等性 — 重复提交、重试、两个写入者、至少一次交付。幂等键在哪里?唯一约束是什么?
  2. 一致性 — 谁读取过时数据以及多长时间;这是否可以接受;缓存失效路径;读写一致性。
  3. 依赖失败 — 提供商宕机、队列延迟、远程模块加载失败。超时、带抖动的重试、断路器、优雅降级、用户看到什么。
  4. 热点 — 热键、大租户、无界列表、N+1、全表扫描、缺少索引、缓存过期时的惊群效应。
  5. 增长 — 无界增长的表/列表/队列;保留策略、归档、分页(游标优于偏移)、分区。
  6. 迁移与发布 — 回填、双写期、特性标志、回滚路径、向后兼容的契约变更。
  7. 安全与隐私 — 数据级别的授权(租户隔离)、日志/队列中的 PII、公共端点的速率限制、输入验证。
  8. 可观测性 — 哪个指标证明它工作(服务级别指标,SLI),哪个告警在不工作时触发,记录什么以调试单个请求。

第 6 步 — 总结,提供可视化选项

图表消耗 Token,因此是选择加入的。摘要本身只使用单行文本流(客户端 → API → Postgres)。

对于 L2 和 L3,以用户语言用一个问题结束摘要,同时列出未解决的问题:

绘制架构图?1 — 终端中的现有/目标架构图 · 2 — 加上设计文件夹(Mermaid + architecture.html) · 3 — 否

  • 仅在用户选择了 1 或 2 之后才绘制,遵循 references/visualization.md。不要等待答案才开始实现;如果用户继续前进而不回答,则视为 "否"。
  • 仅当用户已经要求图表、图片、可视化或设计文件夹时,才跳过问题并立即绘制。
  • 如果没有人能回答(非交互式运行、CI、评估),则不绘制也不询问;用一行说明图表可按需提供。
  • 对于 L0 或 L1 永远不要提供此选项。

生成摘要(下一节),然后继续执行任务:根据摘要实现,将摘要也保留在 PR / MR 描述中,并为作者列出未解决的问题,而不是默默地决定它们。

3. 判断规则

  • 数字优先于模式。 如果没有需要它们的估算,切勿推荐 Kafka、分片、CQRS、专用缓存集群或微服务。简单性是特性;复杂性必须赢得其位置。
  • 解释,而非说教。 每个推荐都附带一句作者可以验证的"因为"。欢迎对规范系统的类比;不欢迎空洞的技术名词列表。
  • 尊重作者的设计。 将分歧呈现为"X 在 Z 负载下将导致 Y;最便宜的修复是 W"。如果他们仍然偏好自己的方式,就按他们的方式实现,并在摘要中注明风险。
  • 现有代码是证据。 阅读它。描述不存在的组件的摘要比没有摘要更糟糕。
  • 区分必须项与锦上添花项。 将每条推荐标记为"现在需要"、"发布前需要"或"大约在 N 用户时需要"。
  • 前端也算。 客户端缓存、分页、乐观更新、实时传输、打包边界和故障隔离都是系统设计;references/frontend.md 涵盖了每个步骤的客户端版本。
  • 使用用户的语言和词汇。 如果团队这样做,翻译模式名称;首次出现时在括号中保留英文术语。

4. 设计摘要

对于 L2 和 L3,使用此结构。保留标题;仅当一个章节确实为空时才删除它,并在一行中说明。对于 L2,目标为一页。references/brief-template.md 包含一个填充好的示例和紧凑的 L1 内联形式。

# 设计摘要:<特性>

**深度:** L2 · **状态:** 提案 · **作者意图:** <一行>

## 1. 范围
功能:...
非功能(除非另有说明,否则为推断):规模 · 延迟 · 可用性 · 一致性 · 安全性 · 成本
不在范围:...
假设:...

## 2. 估算
| 指标 | 现在 | +12 个月 | 来源 |
| --- | --- | --- | --- |
| 日活用户 / 租户 | | | |
| 峰值 QPS(读 / 写) | | | |
| 存储增长 | | | |
结论:<数字允许 / 禁止的内容>

## 3. 当前设计(现有架构)
<单行文本流> · 状态所有权 · 可重用原语 · 继承的限制
文件:...

## 4. 目标设计(目标架构)
<单行文本流> · API 契约 · 数据模型 · 规范系统类比
模式选择:<模式> — 因为 <理由>;拒绝了 <替代方案> 因为 <理由>。

## 5. 风险与深度探究
1. <风险> → 缓解措施(现在需要 / 发布前需要 / 在 N 用户时需要)
...

## 6. 发布、迁移、可观测性
标志、回填、回滚 · 服务级别指标 + 告警 · 记录什么

## 7. 给作者的未解决问题
- ...
- 绘制架构图?1 — 终端图 · 2 — 加上设计文件夹 · 3 — 否

## 8. 实现计划
有序步骤,每个步骤足够小以便审查。

5. 参考资料 — 何时阅读

文件何时阅读
references/framework.md会话中首次执行 L2+ 过程:每步的问题清单、非功能需求列表、如何提取作者意图、分析反模式。
references/estimation.md任何时候需要数字:延迟阶梯、九位数、每节点容量、QPS/存储计算、示例。
references/patterns.md选择或证明模式合理;将特性映射到规范系统(聊天、信息流、通知、搜索自动补全、文件同步、速率限制器、键值存储、ID 生成器、支付...)。包含目录。
references/frontend.md任务是 UI / 单页应用 / 微前端修改:数据获取缓存、分页、实时、打包边界、故障隔离、客户端数字。
references/brief-template.md编写摘要:完整的 L2 示例和紧凑的 L1 内联形式。
references/visualization.md仅在用户要求图表之后(第 6 步)。绘制图:终端框图规则、Mermaid 约定、设计文件夹布局、如何填充 assets/architecture.html。

相关技能