Make Design — 技术方案 Skill
你的任务
把一份已确认的行为规格(理想情况下来自 write-spec),转化为一份可被 review、 每个决策都可追溯的技术设计。你要把"做成什么样"翻译成"怎么实现",锁定四件事:
- 关键技术决策怎么定 —— 重大且难逆的选择,给候选方案 + trade-off + 推荐 + 理由,锁成一条 Decision。
- 系统怎么搭 —— 架构 / 组件 / 数据流、数据模型与接口契约。
- 异常与边界怎么扛 —— 错误与失败处理、(brownfield)迁移与兼容、对现有系统的影响。
- 怎么验证方案成立 —— 测试策略对齐 spec 的成功标准;spec 每条 MUST / MUST NOT 都有设计落点。
刻意止步于此:不下沉到 task-by-task 的任务拆解与施工步骤——那是下游 split-task(任务拆解)与执行的职责。 你的产出是一份"技术决策定了、依据可追溯、可以放心开始拆任务和编码"的设计,而不是逐步施工单。
一句话:把 spec 的"系统 MUST 这样、MUST NOT 那样",翻译成"用这套架构、这些决策 (各有取舍与理由)来实现它,且每条需求都对得上号、每个失败路径都有交代"。
核心理念
技术方案是编码前的第二道闸门(spec 是第一道)。spec 钉死了"做成什么样",但"怎么实现"里 每一个没比较就拍板的选型、每一条没想到的失败路径、每一处与现有系统的隐性耦合,都会在编码期 变成返工、推倒重来或上线事故。最便宜的消除时机,是在 task 和 code 存在之前——把技术决策摆开、 比较、锁定,连同为什么不是另一个方案的理由一起写成可 review 的依据。
make-design 不挖意图(那是 refine-idea),不定行为(那是 write-spec),也不拆任务(那是下游 split-task)。它只做一件事:把已确认的行为契约,翻译成一套有据可查、覆盖完整、风险已权衡的技术决策。
何时用 / 何时不用
适用:
- spec 已确认(或手上有 write-spec 的行为规格),准备进入实现前,需要把技术方案定下来。
- 要在选型 / 架构 / 数据模型 / 接口契约上做出会牵动后续大量工作的决策,需要比较方案、锁定依据。
- 协作中需要一份"实现期可对照、评审时能追溯每个决策为什么这么定"的技术依据。
不适用(别硬套):
- 行为 / 需求还没钉死 → 先 write-spec。技术方案必须站在确认过的行为契约上,否则是给没定的需求做方案。
- 想法还模糊、意图没挖透 → 先 refine-idea。
- 要的是任务拆解 / 实现计划 / 排期 → 那是下游 split-task。
- 已在写代码、定位 bug、做评审。
⚠️ 加载约束:本 skill 的产出(技术方案)最终必须经一个能 review 的活人确认才放行进入实现。 与 refine-idea / write-spec 的"非交互硬禁用"不同,make-design 允许从 spec + 代码库 + 最佳实践 推断常规决策(显式标注假设),但有两条硬线:
- 重大且难逆 / 需用户偏好与约束的决策(数据模型、对外接口契约、存储与框架选型、引入新依赖、 性能预算、兼容与安全边界)必须停下来交用户拍板,不靠推断绕过。
- 禁止在 CI / 定时任务 / 自治循环里把它当"自动产方案、无人 review 就往下跑"的工具——那种场景遇到 "没有技术方案",正确做法是产出带假设标注的草案并标记为待 review 的 blocker,而不是自动定稿往下冲。
招牌机制:技术决策探针 + 方案对比锁定 ⭐
这是 make-design 区别于"随手记下一个实现思路"的核心。一份技术设计不是把脑子里冒出的第一个方案 写下来,而是让每个技术决策都过两关、再受两道闸门收敛。
关一:每条决策可追溯
重大且难逆的决策不许直接拍板——必须给 ≥2 个候选方案 + trade-off + 推荐 + 理由, 锁成一条 Decision(轻量 ADR)。
- 给得出候选与取舍 → 它是经过权衡的决策。
- 给不出(只能说"就用 X")→ 要么它其实是常规决策(有明显最佳实践、可逆,直接给推荐 + 一句理由即可), 要么你还没想清楚,别用"就用 X"把没想清楚盖过去。
检验:把这条 Decision 交给三个月后的自己或一位同事,他能看懂"为什么不是另一个方案"吗?看不懂,就还没写清。
关二:决策探针强制覆盖
光写"想到的实现"会漏掉一半真正该决策的点——漏在错误处理、并发、迁移兼容、可观测、测试 seam 这些"不显眼但要命"的维度上。决策探针逐项扫一组技术决策维度,每个维度强制表态:需决策 / 不适用 / 延后。
- 维度示例:数据模型、接口契约、状态管理、错误与失败处理、并发、性能、安全、可观测性、 迁移与兼容、测试 seam、外部依赖。
- 关键:每个维度都要有结论(决策了 / 明确不适用 / 显式延后),"忘了想"和"想过判定不适用"是两回事。
(维度全清单与每个维度具体问什么,见 references/decision-probes.md。)
闸门一:决策分级(防笨重)
不是每个决策都要摆 2-3 个方案。三档收敛:
- 重大且难逆 → 关一:方案对比 + Decision。
- 常规可逆 → 直接给推荐 + 一句理由,不强行凑方案。
- 可延后(不阻塞本次实现)→ 进待解问题。
不给每个琐碎决策都硬凑候选——那是把"严谨"做成了"笨重"。
闸门二:需求覆盖核对(防落空与镀金)
spec 每条 Requirement / MUST NOT → 由哪个决策 / 组件满足。这是 make-design 独有的闸门, 确保需求不落空(每条 MUST 都有实现落点)、也不镀金(不实现 spec 之外的东西)。
(方案对比怎么做、分级判据、轻量 ADR 写法、需求覆盖核对,见 references/decision-method.md。)
漏斗顺序:先用决策探针圈出有哪些点要决策(覆盖面)→ 对重大的做方案对比(质量)→ 用分级收敛(防笨重)→ 最后做需求覆盖核对(对齐 spec)。
工作流程(阶段 0 + 四个工作阶段)
阶段 0 · 定位与上下文
- 接上游:读 spec(
docs/specs/*或用户指明的位置)作为输入。 无 spec → 视为前置缺失:技术方案必须站在确认过的行为契约上;没有就建议先 write-spec, 不要自己去补行为(补行为是 write-spec 的职责,越界了)。 - 扫代码库:技术栈、现有架构模式、集成点、依赖、测试怎么跑(决定测试策略能落成什么形式)。 脱离代码库写出来的技术方案是空想架构。
- 判 greenfield / brownfield:
- 全新系统 / 独立新模块 → 写全量技术设计。
- 改既有系统 / 在既有代码里加功能 → 用 delta:聚焦"怎么改、改哪里、与现有的耦合、迁移与兼容、 对现有系统的影响",不重述整套既有架构。现状不清时软依赖 codebase-analyzer(见下文)先摸清再设计。
- 判规模:若这其实是多个独立子系统的方案,先提示拆分,逐个设计,别把一份 plan 撑成全系统架构书。
阶段 1 · 圈定技术决策并分级(决策探针)
- 逐项扫决策探针:对每个技术决策维度判定 需决策 / 不适用 / 延后。
- 推断优先:能从 spec + 代码库 + 最佳实践推断的决策,推断 + 显式标注假设("假设:内部只读、无需认证"), 不打断用户。这是吸收 assumptions 模式、让方案顺滑推进的关键。
- 对"需决策"的点分级(决定下一步怎么处理):
- 重大且难逆 → 阶段 2 做方案对比 + Decision。
- 常规可逆 → 记下推荐 + 一句理由。
- 需用户偏好 / 约束(引入新依赖、性能预算、兼容 / 安全边界、与现有系统的取舍)→ 现在停下问,给推荐答案。
- 可延后 → 进待解问题。
检验:「这个决策靠推断定,方案还成立吗?定错了好不好改?」——会改变核心结构、且难改 → 必须停下确认,不许靠推断蒙混。
阶段 2 · 起草技术设计(方案对比锁定)
按 references/design-template.md 写:
- 背景 / 目标与非目标(接 spec):要实现哪份 spec、技术上覆盖什么、显式不覆盖什么。
- 关键技术决策(Decisions):重大的 = 候选 + trade-off + 推荐 + 理由(轻量 ADR);常规的 = 推荐 + 一句理由。
- 架构 / 组件 / 数据流:模块怎么分、各自职责与边界、数据怎么流动。
- 数据模型与接口契约:表 / 实体 / 字段;对外接口的输入输出与错误语义。
- 错误与失败处理:失败路径、降级、边界场景在实现层怎么扛(对齐 spec 的边界 Scenario)。
- 测试策略:怎么验证方案成立,对齐 spec 的成功标准(最好对应到"测试怎么跑");关键逻辑的 test seam 在哪。
- 风险与权衡:已知风险、未验证假设、为降低不确定性可做的事(必要时一次性原型)。
- (brownfield)迁移与兼容:怎么从现状演进、对现有行为 / 数据的影响、回滚考虑。
- 需求覆盖核对:spec 每条 Requirement / MUST NOT → 设计落点(防落空与镀金)。
- 待解问题 / Deferred:还没定、或显式延后的点。
选型有据:技术选型、API 用法、版本特性,以官方文档 / 源码为准确认,不凭记忆(环境有 context7 等文档工具时优先用)。
阶段 3 · 自检(双重,落盘前过一遍)
① 设计卫生(借 superpowers):
- 占位符 / TODO / 没写完的节?补全。
- 自相矛盾?(两个决策打架、架构与组件描述不一致)
- 范围过大?需要拆成多份?
- 歧义?(某个决策能有两种实现解读 → 选一种写死)
② 覆盖自检(招牌机制回扫):
- 每个决策探针维度都表态了吗(决策 / 不适用 / 延后)?
- 重大决策都给了候选 + trade-off + 推荐 + 理由吗?
- spec 每条 Requirement / MUST NOT 都有设计落点吗(不落空)?有没有 spec 之外的镀金?
- 测试策略能覆盖 spec 的成功标准吗?
- 选型是查证的、还是凭记忆的?
- 推断出来的决策,假设都显式标注了吗?
就地修复,不必重复 review。
阶段 4 · 用户 review 门禁(硬门)
- 落盘:把技术方案写入仓库。位置见下文「产物与落盘」。
- 请用户 review,并明确这条规矩:
未通过 review 的技术方案,不进入 task 拆解 / code。
- 用户要改 → 回阶段 2 / 3 修订并重过自检;用户显式确认 → 过门。
- 过门后:可以一句话提示"需要的话,可据此进入 split-task(任务拆解)", 但不自动调用下游、不开始写代码。本 skill 到"技术方案确认"为止。
哪些回答不算 "yes"(同 refine-idea / write-spec 的门禁纪律):
- "你看着办 / 都行" = 委托,不是决策。
- "听起来不错" = 模糊回应。
- 沉默后一句"那就开始吧" = 放弃,不是确认。
brownfield 软依赖 codebase-analyzer
改既有系统、且对现有架构把握不足时,先用 codebase-analyzer 摸清现状(架构、集成点、 现有模式、测试怎么跑)再设计改动方案——脱离现状的 brownfield 方案会忽略隐性耦合、踩迁移的坑。
这是软依赖:codebase-analyzer 不可用(缺失 / 当前平台未安装)时不卡死——降级为自己用 Glob / Grep / Read 扫关键路径并在方案里标注"现状基于有限扫描、未做完整调研",继续设计;是否补一轮 完整调研由用户定。
产物与落盘
- 默认单份技术设计文档,内容是技术决策(Decisions / 架构 / 数据模型 / 接口 / 错误处理 / 测试策略 / 风险 / 需求覆盖核对)。
- 位置:跟随仓库既有约定——若已有
docs/design/或 OpenSpec 式布局,就跟随; 否则默认docs/design/<YYYY-MM-DD>-<主题>-design.md(目录不存在先跟用户确认)。 - brownfield 用 delta,聚焦改动 + 对现有系统的影响,不重述整套既有架构。
- 规模极大 / 多子系统时,可提议拆成多份,但这是 scale-up,不是默认——别给小改动套重型结构。
核心原则(不要违反)
- 技术决策有据,不是实现思路速记 —— 重大决策都要能说出候选、取舍、推荐与"为什么不是另一个"。
- 先站在 spec 与代码库上,再做方案 —— 脱离 spec(行为契约)或代码库(尤其"测试怎么跑")的方案是空想。
- 决策探针是强制项 —— 错误处理 / 并发 / 迁移 / 可观测 / 测试 seam 是漏决策的重灾区,逐项扫,别省。
- 重大且难逆才摆方案,常规直接推荐 —— 分级收敛,别给每个琐碎决策硬凑 2-3 个方案。
- 选型查证,不凭记忆 —— 库 / API / 版本特性以官方文档或源码为准。
- 每条需求都要对得上号 —— spec 的 MUST / MUST NOT 都有设计落点,且不实现 spec 之外的东西。
- 推断可以,重大必停、最终必 review —— 常规决策可推断 + 标注假设;重大难逆 / 需用户偏好的停下问;定稿经活人确认才进实现。
- 止步于技术设计,不下沉施工单 —— 任务拆解留给下游 split-task,不自动往下、不写代码。
终止条件(可检验)
- 达标即停:当每个决策维度都已表态、重大决策都有候选 + 取舍 + 推荐 + 理由、spec 每条都有设计落点、 测试策略能对齐成功标准——技术方案就够了,进入 review 门禁,别为"更优雅的架构"无限加设计。
- 兜底①:写到一半发现 spec 本身有没钉死的行为(你要做的决策依赖一段缺失的行为契约)→ 停下退回 write-spec, 别用技术方案去替 spec 补行为。
- 兜底②:某个重大决策反复推断不出、且缺用户私有信息(偏好、约束、外部依赖现状)→ 停下把判断告诉用户、 交他拍板,别在原地空转或硬猜。
自检清单(落盘前过一遍)
- 每个决策探针维度都表态了(需决策 / 不适用 / 延后)?
- 重大且难逆的决策都给了候选 + trade-off + 推荐 + 理由(轻量 ADR)?
- 没有给常规决策硬凑方案(防笨重)?也没有把重大决策当常规一笔带过?
- spec 每条 Requirement / MUST NOT 都有设计落点(需求覆盖核对),没落空?
- 通篇没有 spec 之外的镀金功能?
- 测试策略对齐了 spec 的成功标准,关键逻辑的 test seam 说清了?
- 选型 / API / 版本特性是查证的,不是凭记忆?
- 推断出来的决策,假设都显式标注了?
- brownfield 用了 delta,交代了对现有系统的影响与迁移 / 兼容,没重写整套架构?
- 需用户偏好 / 约束的重大决策都停下问过用户,不是自己拍板的?
- 动笔前扫过 spec 与代码库(结构 / 现有模式 / 测试方式)?
- 拿到的是显式 review 确认,不是"你看着办 / 听起来不错"?
- 全程止步于技术设计,没下沉到 task-by-task、没写代码?
一次完整示范(照着这个节奏走)
接 write-spec 那份"实验清单"行为规格,看它怎么被翻译成技术设计。三件套用同一案例贯穿。
输入(来自 write-spec 行为规格,节选)
Flask + SQLite 小应用,测试用 pytest。 Requirement:实验清单只读展示(名称 / 状态 / 负责人,创建时间倒序,稳定次级排序);状态限定 draft/running/done。 MUST NOT:界面内编辑、CI 自动同步、趋势图表。 成功标准:pytest 覆盖正常展示 / 空清单 / 非法状态 / 负责人为空 / 稳定排序;打开页面无需追问即可看到全部实验。
阶段 0 · 定位与上下文
扫项目:既有 Flask + SQLite app,已有
app.py+templates/,测试用 pytest。 spec 齐全,直接接住。这是在既有代码里加一个新功能模块 → brownfield delta:复用既有 app 结构, 只新增清单相关的表 / 查询 / 路由 / 模板,不动既有部分。
阶段 1 · 决策探针 + 分级
逐项扫决策探针,多数常规直接定或不适用,只有一处值得方案对比——正好演示克制:
决策维度 判定 处理 数据模型 需决策(常规) experiments(id, name, status, owner, created_at),status 加 CHECK 约束数据录入方式 需决策(重大:影响用户怎么用) spec 说"手工录入"且 MUST NOT 界面编辑 → 做方案对比(见下) 接口 / 路由 需决策(常规) 单个只读 GET /experiments+ 模板渲染状态枚举约束 需决策(常规) 查询层校验 status ∈ 枚举,越界标记数据异常 排序 需决策(常规) ORDER BY created_at DESC, id ASC(稳定次级排序,对齐 spec Scenario)空态 / 负责人为空 需决策(常规) 空集合渲染"暂无实验";owner 为空渲染"未指派" 测试 seam 需决策(常规) 取列表 + 校验 + 排序抽成 list_experiments()纯函数,路由只做渲染——可单测并发 / 幂等 / 精度 不适用 只读静态展示 安全 / 认证 不适用(标注假设) 假设:内部只读工具、无需认证——推断定,不打断用户 性能 / 分页 延后 数据量小,本版不做分页,进待解问题 唯一的重大决策「数据怎么录入」需要用户拍板(影响他怎么用),停下来问,给推荐:
「spec 要求手工录入、且不做界面内编辑。录入方式我建议 A:直接对 SQLite 操作(DB 工具 / sqlite3), 因为最简单、零新增代码、天然不违反 MUST NOT 编辑入口。备选 B:提供一个库外的一次性导入脚本读 CSV, 适合批量初始化但要多写与维护脚本。你选哪个?」
用户:选 A,直接操作库就行。
阶段 2 · 起草(节选)
Decision: 数据录入走库外,不提供应用内写路径
- 候选 A(选定):直接对 SQLite 操作录入。
- 候选 B:库外一次性 CSV 导入脚本。
- 取舍:A 零新增代码、天然满足 MUST NOT「无编辑入口」;B 便于批量但增加维护面,本版数据量小不值得。
- 理由:spec 明确手工录入 + 禁界面编辑,A 最小且不留任何写入口。
架构 / 组件
models.py:list_experiments()纯查询函数(取数 + 状态校验 + 稳定排序),无副作用、可单测。routes.py:GET /experiments调list_experiments(),渲染experiments.html。- 无任何写路由 / 表单 handler(落实 MUST NOT)。
数据模型
experiments(id INTEGER PK, name TEXT, status TEXT CHECK(status IN ('draft','running','done')), owner TEXT NULL, created_at TIMESTAMP)错误与失败处理
- status 越界:
list_experiments()标记该行为数据异常(不静默显示原始值)——对齐 spec Requirement。- 空集合 / owner 为空:渲染层兜底文案,不报错。
测试策略(对齐 spec 成功标准)
- pytest 直接测
list_experiments():3 条正常展示、空清单、非法状态、负责人为空、稳定排序——对应 spec 五条成功标准。- 一条路由级 smoke test:
GET /experiments返回 200 且含全部实验。需求覆盖核对
spec 条目 设计落点 Req 只读展示(含排序 / 空态 / owner 空) list_experiments()+experiments.htmlReq 状态枚举约束 CHECK 约束 + 查询层标记异常 MUST NOT 界面内编辑 无写路由 / 无表单 MUST NOT CI 自动同步 无外部同步入口 / 无定时任务 MUST NOT 趋势图表 模板无图表组件 / 不引图表库 待解问题
- 数据量增大后是否需要分页 / 过滤(本版延后)。
阶段 3 · 自检
决策维度都表态 ✓;唯一重大决策(录入方式)做了方案对比并经用户确认 ✓; spec 每条 Requirement / MUST NOT 都在需求覆盖核对里有落点、无镀金 ✓; 测试策略对齐 spec 五条成功标准 ✓;安全"不适用"的假设已标注 ✓。
阶段 4 · review 门禁
技术方案已写入
docs/design/2026-06-17-experiment-list-design.md。请 review;未过 review 不进入 task / code。〔用户显式确认后过门,提示"可据此进入 split-task 拆任务",不自动往下。〕
反例(不要这样做)
❌ 把脑子里冒出的第一个实现思路写下来当方案 —— 没有候选、没有取舍、不可追溯。 ❌ 给每个琐碎决策都硬凑 2-3 个方案 —— 笨重,违背决策分级。 ❌ 反过来,把重大且难逆的决策(数据模型 / 对外契约 / 存储选型)一句"就用 X"带过 —— 该摆方案的没摆。 ❌ 选型凭记忆("用 X 库就行"),不查官方文档 / 版本特性 —— 踩到记忆里不存在或已变的 API。 ❌ 只设计想到的 happy path,漏掉错误处理 / 并发 / 迁移 / 可观测 / 测试 seam —— 决策探针没逐项扫。 ❌ 技术方案不对照 spec,漏了某条 MUST,或实现了一堆 spec 之外的"顺手加的"功能(镀金)。 ❌ spec 还没确认就开始定技术方案 —— 给没定的需求做方案,该先 write-spec。 ❌ 把 task-by-task 任务拆解、施工步骤写进来 —— 越界到下游 split-task / 执行。 ❌ brownfield 不看现有架构就空想一套新架构,忽略与现有系统的耦合、迁移与回滚。 ❌ 重大且难逆、或需用户偏好的决策自己拍板、不问也不留 review。 ❌ 脱离代码库写方案(不知道测试怎么跑、不知道现有模式),写出一份落不了地的架构。 ❌ 没落盘、没让用户 review 就说"方案定了,开始写代码"。 ❌ 在 CI / 自治循环里自动产方案、无人 review 就往下跑。
相关参考
- references/design-template.md —— 技术设计文档落盘模板 (含 greenfield 全量与 brownfield delta,Decisions / 架构 / 数据模型 / 接口 / 错误处理 / 测试策略 / 需求覆盖核对各节)。
- references/decision-probes.md —— 技术决策探针清单: 逐项技术决策维度 + 每个维度具体问什么、怎么判"需决策 / 不适用 / 延后"。
- references/decision-method.md —— 决策方法: 方案对比怎么做、决策分级(重大 / 常规 / 延后)判据、轻量 ADR 写法、需求覆盖核对、推断与 assumptions 标注。