Skip to content

Matt Pocock - Prototype 替代 Spec 驱动开发

AI 时代,代码生成成本已降至极低。与其花大量时间写详细 Spec,不如直接用拋棄式原型(Throwaway Prototype)验证设计决策——更快、更具体、反馈更真实。

目录


一、核心问题:过度规格化(Over-specifying)

问题本质

许多开发者在用 AI 写代码时,倾向先花大量精力写极度详细的 Spec 或 Plan(Plan Mode、Spec-Driven Development),试图把所有细节都预先定义好,让 AI 输出完美匹配规格的代码。

在这个冲动中,他们忘记了一件关键的事:你可以在朝向 Spec 工作的同时直接写代码。

为什么现在要改变

传统开发时代                    AI 时代
┌──────────────┐              ┌──────────────┐
│  写代码很贵    │              │  写代码很便宜  │
│  Spec 很重要   │              │  原型是最佳    │
│  原型是奢侈品  │     ──→      │  沟通工具      │
└──────────────┘              └──────────────┘
     Agile 时期                   LLM 时代

"代码很便宜(Code is cheap)"这句话部分正确、部分让 Matt 讨厌。真正变便宜的是生成代码的成本——这让原型和 Spike 从"奢侈品"变成了最便宜且最有效的沟通工具。

✅ / ❌ 行为对比

✅ 应该做的 ❌ 常见误区
遇到"该怎么呈现"的问题时立即 /prototype 花数小时写详细 UI Spec
要求 AI 产出 2-3 种可运行方案做比较 在文字描述中反复纠结交互细节
在真实路由上注入原型获取真实体验 用独立测试页在真空环境中原型
用 Compact 保留设计决策后继续迭代 开新对话丢失所有上下文

二、保真度(Fidelity)概念

设计讨论中,问题需要被"解决(Resolved)"。不同问题需要不同保真度的讨论方式。

低保真 vs 高保真

维度 低保真(Low-Fidelity) 高保真(High-Fidelity)
适用场景 基础逻辑、简单交互 介面长怎样、行为如何表现
典型问题 "弹窗需要取消和确认按钮" "数据该怎么呈现?搜索结果如何分组?"
解决方式 文字讨论 / Grilling 质询 可操作的原型(半成品代码)
Token 成本 高(但值得)
反馈质量 抽象、容易遗漏细节 具体、能发现隐性决策

触发 /prototype 的判断标准

你遇到一个设计问题
        │
        ▼
  能用文字讨论清楚吗?
    /        \
  能          不能
   │            │
 文字讨论     "该怎么呈现/运作"?
 (Grilling)      │
            /        \
         是            否
          │            │
      /prototype    其他方式

核心判断信号:"我需要看到它实际运行,需要感受它的运作" → 立即 /prototype。


三、/prototype Skill 详解

定义

原型(Prototype)是为回答特定问题而写的拋棄式代码(Throwaway Code)。问题决定原型的形态。

两个分支(Branch)

                   /prototype
                       │
              ┌────────┴────────┐
              ▼                 ▼
     "看起来怎样?"        "逻辑/状态对吗?"
              │                 │
         UI 原型            逻辑原型
        (UI.md)           (LOGIC.md)

UI 原型(UI.md)

在单个路由上生成多个结构截然不同的 UI 变体,通过浮动底栏切换。

两种子形态(强烈优先 A):

子形态 场景 方式
A — 现有页面调整(优先) 已有路由可挂载 在真实路由上用 ?variant= 参数切换,保留现有数据获取和认证
B — 新页面(最后手段) 无现有页面可用 创建拋棄式路由,命名含 prototype

Matt 在视频中强调:在真实路由上注入原型(而非独立测试页)能给出"代码实际运作方式的更诚实呈现"。

关键规则:变体必须结构性不同——不同布局、不同信息层级、不同主要操作,不是换个颜色或文案。三个微调的卡片网格不是原型,是壁纸。

变体切换器工作流:

   ?variant=A    ?variant=B    ?variant=C
  ┌────────┐    ┌────────┐    ┌────────┐
  │ 方案A   │ ←→ │ 方案B   │ ←→ │ 方案C   │
  │ 搜索框  │    │ 侧边栏  │    │ 内联式  │
  │ + 分组  │    │ + 过滤  │    │ 无过滤  │
  └────────┘    └────────┘    └────────┘
       ↑              ↑              ↑
       └──────── 浮动底栏切换 ────────┘
                  ← 方案 B →

代码示例(框架适配):

// 变体切换器伪代码
const variant = searchParams.get('variant') ?? 'A';
return (
  <>
    {variant === 'A' && <VariantA {...data} />}
    {variant === 'B' && <VariantB {...data} />}
    {variant === 'C' && <VariantC {...data} />}
    <PrototypeSwitcher variants={['A','B','C']} current={variant} />
  </>
);

逻辑原型(LOGIC.md)

构建一个微型交互式终端应用(TUI),手动驱动状态模型通过纸面上难以推理的边界情况。

适用场景: - "不确定这个状态机能否处理 X 然后 Y 的边界情况" - "这个数据模型能表示...的情况吗" - "想先感觉一下 API 应该长怎样" - 任何想"按按钮看状态变化"的场景

逻辑原型的核心设计原则——逻辑与 TUI 分离

┌─────────────────────────────┐
│       逻辑模块(可移植)       │    ← pure interface
│  (state, action) => state    │    可直接迁移到真实代码库
├─────────────────────────────┤
│       TUI 外壳(拋棄式)      │    ← import 逻辑模块
│  clear → render state →      │    用完即丢
│  read key → dispatch         │
└─────────────────────────────┘

TUI 每帧结构: 1. 当前状态(pretty-print,每行一个字段,字段名加粗) 2. 键盘快捷键(底部列出:[a] add [d] delete [q] quit) 3. 每次操作后清屏重绘整个画面(不追加滚动)

通用规则(两个分支共用)

┌─────────────────────────────────────────────────────┐
│  /prototype 的 6 条铁律                              │
├─────────────────────────────────────────────────────┤
│  1. 拋棄式,从第一天起就明确标记                      │
│     代码放在使用位置附近,但命名含 "prototype"        │
│                                                      │
│  2. 一条命令运行                                     │
│     pnpm <name> / python <path> / bun <path>        │
│                                                      │
│  3. 默认不持久化                                     │
│     状态在内存中,如需 DB 用 scratch DB              │
│                                                      │
│  4. 跳过打磨                                         │
│     无测试、无错误处理、无抽象                        │
│                                                      │
│  5. 暴露状态                                         │
│     每次操作后打印/渲染完整相关状态                   │
│                                                      │
│  6. 完成后捕获                                       │
│     验证的决策折叠进真实代码                          │
│     原型本身提交到拋棄式分支作为"原始来源"            │
│     主分支只保留验证的决策                            │
└─────────────────────────────────────────────────────┘

四、Wayfinder 工作流整合

Wayfinder 是什么

Wayfinder 是 Matt Pocock 的新 Skill,用于规划超出单个 Agent 会话容量的大型工作。它把大型工作拆解为多个规划会话(Planning Sessions),每个会话获得自己的"决策票据(Decision Ticket)"。

核心理念:规划而非执行(Plan, don't do)。每张票据解决一个决策,当通往目标的路径清晰、无需再决策时,地图就完成了。

四种票据类型

票据类型 模式 说明 解决方式
Grilling HITL 通过质询技能逐个提问,厘清基础范畴。默认类型 对话质询
Prototype HITL 提升讨论保真度,制作廉价粗糙的具体产物 /prototype Skill
Research AFK 阅读文档/API/知识库,为决策提供事实 /research 子代理
Task HITL/AFK 决策前必须完成的手动工作(注册服务、迁移数据等) 执行

地图结构(The Map)

地图是 issue tracker 上一个标记为 wayfinder:map 的 issue,包含:

## Destination       ← 终点是什么(spec/决策/变更)
## Notes            ← 领域、相关技能、偏好
## Decisions so far ← 已做决策的索引(一行一个,含链接)
## Not yet specified ← 迷雾区(暂无法精确表述的决策)
## Out of scope     ← 明确排除的工作

迷雾机制(Fog of War)

地图刻意不完整。无法清晰表述的决策留在"Not yet specified"区域,随前沿推进逐步"毕业(Graduate)"为正式票据。

判断标准——不是"现在能否回答",而是"现在能否精确提问": - 能精确提问 → 创建票据(即使被阻塞) - 无法精确提问 → 放入"Not yet specified"

Prototype 在 Wayfinder 中的位置

当讨论需要提升保真度时——核心问题是"该怎么呈现或运作"——就从默认的 Grilling 票据切换到 Prototype 票据。原型完成后作为资产链接到票据上。

大型工作拆解流程:

  模糊想法 → Wayfinder → Chart the Map
               │
               ├── Grilling 票据 ──→ 基础范畴厘清
               ├── Prototype 票据 ──→ 高保真设计验证
               ├── Research 票据 ──→ 外部事实收集
               └── Task 票据 ──→ 前置手动工作
                       │
                       ▼
               路径清晰,移交执行
               (Implement Agent / AFK Agent)

五、实战演示:搜索栏原型

Matt 用 Wayfinder 扩展他的 TLDraw 图表应用,需要一个搜索旧图表的功能。数据模型复杂(图表 + 时间快照),不确定搜索栏该怎么呈现和行为。

多版本迭代过程

方案 A              方案 B              方案 C
┌──────────┐       ┌──────────┐       ┌──────────┐
│ 搜索框在顶部│       │ 左侧过滤栏│       │ 内联展示  │
│ 按图表名   │       │ 可过滤筛选│       │ 无过滤器  │
│ 分组快照   │       │ 搜索后重置│       │ 一切内联  │
└──────────┘       └──────────┘       └──────────┘
     │                   │                   │
     │     Matt 反馈:     │                   │
     │  ✓ 搜索框位置好    │  ✓ 过滤栏不错      │  ✓ 内联布局好
     │  ✗ 分组不对        │  ✗ 搜索后重置不好  │  ✗ 顶部标题丑
     │                   │                   │
     └─────── Compact + 反馈迭代 ────────────┘
                       │
                       ▼
              ┌──────────────────┐
              │ 方案 D (最终)      │
              │ ✓ A 的搜索框      │
              │ ✓ C 的内联布局    │
              │ ✓ 去重修复        │
              │ ✓ 点击功能正常    │
              └──────────────────┘

关键操作:Compact

原型会话消耗了约 100,000 Token。此时执行 Compact: - 保留所有设计决策的上下文 - 压缩历史对话 - 在同一代码库位置继续迭代

100K Token 后的决策树:

  消耗 ~100K Token
       │
       ▼
  执行 Compact
  (压缩历史,保留设计决策)
       │
       ▼
  注入反馈:
  "搜索框用 A 的,
   布局用 C 的"
       │
       ▼
  生成方案 D
  (融合 A+C 的优点)
       │
       ▼
  原型完成 → 保存到拋棄式分支

交付给 Implement Agent

原型确认后,交给 AFK Agent(离线代理): 1. 参照拋棄式分支中的原型代码 2. 清理原型代码(删除测试文件、无用的变体) 3. 对齐原始 Spec 正式落地 4. 实现 Agent 通常可以直接从原型分支中复制粘贴前端代码

因为经过了高保真讨论——看到了实际运行的版本——反馈极为精准,实现结果质量极高。


六、原型不限前端:逻辑原型

核心洞察

很多人看到"原型"就想到 UI/前端。但更复杂的后端工作同样受益:

问题类型 UI 原型 逻辑原型
核心问题 "应该长怎样" "逻辑/状态模型感觉对吗"
适用场景 页面布局、交互设计 状态机、业务逻辑、数据模型
产物形态 多个 UI 变体 + 切换器 微型交互终端应用
验证方式 视觉比较 手动驱动状态机通过边界情况

逻辑原型实战

针对复杂状态机,指示 AI 构建一个微型终端应用:

┌─────────────────────────────────────────┐
│  状态机原型 — TUI 界面                    │
├─────────────────────────────────────────┤
│                                          │
│  当前状态:                               │
│    用户: alice                           │
│    权限: [read, write]                   │
│    会话: active                          │
│    最后操作: delete_resource              │
│                                          │
│  ──────────────────────────────────────  │
│  [a] 添加用户  [d] 删除用户              │
│  [t] 推进时钟  [r] 切换权限              │
│  [q] 退出                                │
│                                          │
└─────────────────────────────────────────┘

这种方式能发现"纸面上看起来合理但实际运作时暴露问题"的逻辑缺陷——Matt 称收到了大量反馈说这是"非常棒的小功能"。

两个分支都有各自的参考文档(UI.md / LOGIC.md),Agent 会根据问题类型自动选择对应分支。


七、方法论根基:Shape Up

Shape Up by Ryan Singer

Matt 在 2019 年读了 Basecamp 的《Shape Up》一书(Ryan Singer 著),深受影响,彻底改变了他构建应用的方式。该书完全免费在线阅读

核心思想映射:

Shape Up 概念              →  Prototype Skill 实践
─────────────────────────────────────────────
将想法具象化(Concrete)     →  原型是可操作的半成品
降低沟通误解               →  高保真讨论替代纯文字 Spec
降低项目风险               →  先拋棄式验证再正式实现
"做出来看看"               →  /prototype 生成多个方案

Shape Up 的核心影响

Shape Up 强调将抽象想法快速转化为具体产物(Concrete artifact)来降低沟通误解和项目风险。Matt 的 /prototype Skill 是这一理念在 AI 时代的延伸:

  • AI 时代前:手工做原型成本高,需要克制
  • AI 时代后:AI 生成原型成本极低,可以大量产出比较
  • 保真度越高,从"讨论到生产"的跨越就越小
保真度与实现成本的关系:

  高保真               低保真
  ┌────────────────────────────────┐
  │  原型     │  ← 小跨越           │
  │           │                    │
  │     ↓     │  讨论到生产         │
  │           │  的跨越很大         │
  │  生产代码  │                    │
  └────────────────────────────────┘
       ↑ 容易                ↑ 困难

Matt 预测:随着 Agent 更擅长处理画布和设计工具,线框图(Wireframes)也会回归。


参考资料

相关笔记

  • [[AI 辅助开发方法论]]
  • [[Spec-Driven vs Prototype-Driven Development]]
  • [[Shape Up 方法论]]
  • [[Wayfinder 工作流详解]]