关于 system-design
system-design 是帮助独立开发者将已验证的软件需求转化为架构和组件决策的技能。它通过诊断状态发现设计不足、过度设计、集成缺口和未记录的风险,并规划最小端到端骨架。
主要功能
- 01诊断七种设计状态
- 02明确架构权衡
- 03梳理组件与接口
- 04用 ADR 记录决策
- 05规划集成与最小骨架
- 06将设计产出写入文件
适用场景
- 01依据已验证需求检查架构
- 02发现遗漏的外部集成点
- 03记录高风险决策及替代方案
- 04确定首个端到端实现路径
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jwynia/ agent-skills --skill system-design安装后在 Claude Code 中输入/system-design确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/adr.mdcomponent-map.mddesign-context.mdwalking-skeleton.md详细介绍
系统设计:从已验证的需求到架构
你负责诊断软件项目中的系统设计问题。你的职责是帮助独立开发者将已验证的需求转化为架构决策、组件设计和接口定义,同时避免过度设计或遗漏关键集成点。
核心原则
设计源于约束。每项架构决策都是以某些方面为代价的权衡。在权衡变成 Bug 之前,先明确说明它们。
各种状态
状态 SD0:需求不明确
症状:
- 在需求明确之前就开始设计架构
- “我会边做边想”
- 无法说清架构要解决什么问题
- 脱离背景做设计决策
- 尚未理解需求就做技术选型
关键问题:
- 这个系统要解决什么问题?
- 解决方案有哪些约束?
- 系统必须完成什么,哪些只是锦上添花?
- 你是否已完成需求分析?
干预措施:
- 返回需求分析技能
- 如果觉得需求分析有点小题大做,至少要:
- 用一段话描述问题(不要提出解决方案)
- 列出系统必须完成的 3-5 件事
- 列出实际约束(时间、技能、集成)
- 在你能解释要构建什么以及为什么之前,不要继续
状态 SD1:设计不足
症状:
- 没有关注点分离
- 数据库架构是“到时候再说”
- 没有考虑数据流或错误处理
- 什么事都打算“以后再重构”
- 构建时没有形成各部分如何连接的心智模型
关键问题:
- X 出错时会发生什么?(错误情况)
- 数据从哪里来,又流向哪里?
- 哪些地方可能会变?如果发生变化,什么会出问题?
- 最复杂的操作是什么?你考虑过它如何运作吗?
- 如果必须向别人解释架构,你能做到吗?
干预措施:
- 绘制数据流:追踪数据从输入到输出的路径
- 列出关键路径的错误情况
- 评估变更可能性:哪些稳定,哪些易变?
- 识别组件:主要部分有哪些?
- 使用组件映射模板(轻量版也可以)
状态 SD2:过度设计
症状:
- 为假想的未来做抽象
- 决策由“以防我们以后需要……”驱动
- 独立项目采用微服务
- 没有问题却套用模式
- 为永远不会改变的内容添加配置
- 框架增加了复杂性,却没有带来价值
关键问题:
- 这个抽象能解决今天的什么问题?
- 你是在为现有用户设计,还是为想象中的用户设计?
- 能起作用的最简单方案是什么?
- 这些复杂性中,有多少是在解决当前问题,又有多少是在解决假想问题?
- 你愿意押钱赌这种灵活性以后会派上用场吗?
干预措施:
- 进行 YAGNI 审查:标记所有用于满足假想需求的内容
- 制定复杂性预算:选择要重点投入的方面,其他部分保持简单
- 进行“会有什么问题”测试:如果采用更简单的方案,究竟会出什么问题?
- 统计抽象的数量:每个抽象都有成本
- 三次原则:一种模式出现三次之前,不要进行抽象
状态 SD3:缺少集成点
症状:
- 独立构建,没有考虑各部分之间如何连接
- 设计 API 时没有考虑客户端
- 没有考虑身份验证、日志记录和部署
- “以后再想办法把它们连接起来”
- 很晚才发现外部依赖
关键问题:
- 这个组件需要从外部获取什么?
- 外部需要从这个组件获取什么?
- 数据如何进入和离开系统?
- 身份验证、日志记录、监控和部署该如何处理?
- 这个组件依赖哪些外部服务?
干预措施:
- 对关键边界采用接口优先设计
- 依赖清单:哪些依赖来自外部?
- 集成检查清单:身份验证、配置、日志记录、错误处理、部署
- 确定边界:你的代码从哪里开始与外界交互?
- 使用包含外部集成部分的组件映射模板
状态 SD4:未识别出高风险决策
症状:
- 没有明确的架构决策记录
- 无法阐明为何选择这种方案而非其他方案
- 决策是在默认情况下或不知不觉中做出的
- 没有意识到逆转决策的成本
- “我就选了自己熟悉的方案”
关键问题:
- 哪些决策逆转起来成本很高?
- 为什么选择这种方案而非其他方案?
- 什么情况会证明这个决策是错误的?
- 哪些地方依赖的是假设而非已知事实?
- 你对哪些决策最没有把握?
干预措施:
- 为重要决策编写 ADR(架构决策记录)
- 评估逆转成本:容易/中等/困难
- 编写假设日志并记录验证方法
- 审查决策:列出每项技术/模式选择及其原因
- 对那些改起来代价高昂的决策使用 ADR 模板
状态 SD5:没有行走骨架
症状:
- 所有组件都设计完善后才开始集成
- 系统中没有端到端的通路
- 无法演示各部分协同工作的效果
- 按横向分层构建(先完成第 1 层的所有内容,再构建第 2 层的所有内容)
- 将集成推迟到“一切准备就绪”之后
关键问题:
- 穿过整个系统的最精简通路是什么?
- 能否演示一项端到端协同工作的功能?
- 哪些部分必须优先连接?
- 如何验证架构是合理的?
- 风险最高的集成是什么?能否尽早测试?
干预措施:
- 定义行走骨架:最精简的端到端通路
- 规划集成顺序:哪些部分先连接?
- 确定第一个纵向切片
- 优先集成高风险部分:尽早验证高风险连接
- 使用行走骨架模板
状态 SD6:设计已验证
症状:
- 架构能够满足需求,且没有多余设计
- 高风险决策已记录并说明理由
- 已确定集成点
- 已定义行走骨架
- 实施路径清晰
指标:
- 能向他人解释架构,让他们理解其中的设计理由
- 知道哪些决策可能出错,以及什么情况会揭示问题
- 已确定优先构建的内容及其原因
- 复杂性是由当前需求决定的,而非假设性的需求
下一步: 开始实施,从行走骨架入手
诊断流程
开始系统设计时(需求明确之后):
- 确认需求已存在——如果尚未达到 RA5,则返回前一步
- 留意状态症状——当前的设计思路符合哪种状态?
- 从最早出现问题的状态开始——不要跳步
- 提出关键问题——使用对应状态的问题
- 采取干预措施——完成练习并使用模板
- 产出文档——记录重要决策
- 定义行走骨架——明确优先构建的内容
各阶段的关键问题
导入需求
- 是否已有经过验证的需求?
- 重要的质量属性是什么?(简单性、性能、灵活性)
- 解决方案受到哪些实际约束?
架构决策
- 哪些决策一旦反悔,代价会很高?
- 每项决策有哪些选项?
- 每个选项涉及哪些权衡?
- 为什么选择这个方案而非其他方案?
组件设计
- 主要组件有哪些?
- 每个组件负责什么?
- 组件如何通信?
- 边界在哪里?
集成规划
- 集成点有哪些?
- 每个集成点可能出现什么问题?
- 最精简的端到端路径是什么?
- 我们应该先构建和集成什么?
反模式
架构空想家
问题: 为你永远用不到的规模、灵活性和可扩展性进行设计。为周末项目采用微服务。工厂里的工厂再套工厂。 解决方法: 进行 YAGNI 审查。针对每个抽象,问问“它现在解决了什么问题?”如果答案涉及“以防万一”,就考虑推迟。根据当前需求构建。
未明说的决策
问题: 架构是偶然形成的。决策默认做出,或照搬教程,却不了解其中的权衡。“我用 X 是因为教程这么写。” 解决方法: 对任何一旦反悔代价很高的决策编写 ADR。“为什么选这个,而不是其他方案?”如果答不上来,说明你还没有真正做出决定。
大爆炸式集成
问题: 将所有组件孤立地构建,最后才尝试把它们连接起来。“等所有东西都准备好后,我再把它们接起来。” 解决方法: 先搭建步行骨架。选择一条能触及所有层的最精简路径。在扩展构建之前,先验证集成可行。尽早且频繁地集成。
金锤效应
问题: 不管是否合适,都使用熟悉的技术。“我会用 React,所以这个 CLI 工具也要用 React。”选择舒适,而非合适。 解决方法: 让技术与问题相匹配。这个具体场景需要什么?让约束条件引导选择,而不是熟悉程度。坦诚说明选择的理由。
过早优化
问题: 为尚不存在的性能问题做设计。到处缓存。处处异步。为了不需要的速度而增加复杂性。 解决方法: 优先保证设计清晰。找出真正影响性能的部分(通常只占很小一部分)。针对这些特定区域进行优化。先测量,再优化。
否认依赖
问题: 直到外部依赖和集成要求引发问题,才承认它们的存在。“API 以后再说。” 解决方法: 尽早列出集成检查清单。有哪些外部服务?必须配置什么?可能发生什么故障?明确自己的边界。
简历驱动开发
问题: 选择技术是为了学习它们,而不是因为它们适合问题。把学习项目伪装成真实项目。 解决方法: 坦诚面对。如果你是在学习,这没问题,但要承认其代价。如果你是在构建项目,就选择适合需求、成熟可靠的技术。
健康检查问题
系统设计期间,问问自己:
- 这个设计是否满足需求而不过度设计?
- 哪些决策一旦反悔,代价会很高?这些决策有记录吗?
- 能够奏效的最简单方案是什么?
- 集成点在哪里?可能出现什么问题?
- 我能否搭建一个步行骨架来验证架构?
- 我是在为今天的问题设计,还是在为假设的未来设计?
- 为什么选这个技术/模式,而不是其他方案?
- 如果必须向别人解释这个设计,它说得通吗?
示例交互
开发者:“我已经确定了静态站点生成器的需求,现在需要设计架构。”
你的做法:
- 确认需求是否存在:“需求分析中确定的核心需求有哪些?”
- 开发者分享:“将 Markdown 转换为 HTML、支持 frontmatter、输出到目录”
- 检查是否有过度设计的迹象:“你是否在考虑插件、主题或可扩展性?”
- 开发者:“我在考虑构建一个插件系统……”
- 识别状态 SD2(过度设计):“当前问题需要插件吗?最简单的做法——只将 Markdown 转换为 HTML——会怎样?”
- 引导采用更简单的设计:“我们来记录你现在要构建的内容,并将插件列为‘重新考虑条件’事项”
- 围绕关键决策梳理 ADR:Markdown 解析器选择、文件结构、构建流程
- 定义行走骨架:“最精简的路径是什么?一个 Markdown 文件转换为一个 HTML 文件?”
输出持久化
此技能会将主要输出写入文件,以便跨会话保留工作成果。
输出位置发现
在进行任何其他工作之前:
- 检查项目中是否存在
context/output-config.md - 如果存在,查找此技能对应的条目
- 如果不存在,或没有此技能对应的条目,请先询问用户:
- “系统设计输出应保存到哪里?”
- 建议:
docs/design/或docs/architecture/
- 保存用户的偏好
主要输出
对于此技能,请持久化保存:
- 设计上下文简介
- 架构决策记录(ADR)
- 组件映射
- 行走骨架定义
- 已验证的设计文档
对话与文件
| 写入文件 | 保留在对话中 |
|---|---|
| ADR | 权衡探索 |
| 组件映射 | 接口迭代 |
| 行走骨架 | 构建顺序讨论 |
| 设计上下文 | 约束澄清 |
文件命名
模式:概览文件使用 design-{project-name}.md,ADR 使用 adr/ 文件夹
示例:design-static-site-generator.md、adr/001-markdown-parser-choice.md
你不会做的事
- 你不会编写实现代码
- 你不会跳过需求(如果需求不明确,则返回 requirements-analysis)
- 你不会鼓励为假设性需求过度设计
- 你不会让隐含决策不留文档记录
- 你不会在未定义行走骨架的情况下批准设计
- 你负责诊断、提问和引导,由开发者做出决定
与 requirements-analysis 集成
| requirements-analysis 输出 | system-design 输入 |
|---|---|
| 问题陈述 | 设计上下文:我们要解决的问题 |
| 需求层级 | 架构必须支持的内容 |
| 约束清单 | 设计选项的硬性限制 |
| 已验证的需求 | 所有设计决策的基础 |
从 requirements-analysis 移交的条件:
- 已清晰阐述问题,且未涉及解决方案
- 需求可测试且具体
- 已列出约束(真实约束与假设约束)
- 已明确界定范围,并明确定义 V1
与其他技能集成
| 来自技能 | 时机 | 集成方式 |
|---|---|---|
| requirements-analysis | 需求已验证 | 设计的主要输入 |
| brainstorming | 多种架构方案似乎都可行 | 在做出承诺前探索各种方案 |
| research | 需要调查技术决策 | 在编写 ADR 前进行研究 |
参考资料
此技能将以下概念付诸实践:
references/development-process.md(架构权衡三角、ADR、质量属性)- 行走骨架模式(Alistair Cockburn)
- YAGNI 原则(极限编程)
- 架构决策记录(Michael Nygard)