Spec Coding vs Vibe Coding:AI 编程的两条路线

AI 编程正在把「写代码」这件事拆成两条路线:一种是凭感觉快速推进的 Vibe Coding,另一种是先写清楚规格再实现的 Spec Coding。

它们都能把一个想法变成代码,但它们解决的问题、承担的风险、适合的场景完全不同。

一、一个 Todo List,两种写法

同一个需求:做一个 Todo List 应用,要求支持添加、删除、切换完成、筛选、计数、清除已完成、内联编辑、数据持久化。

我们用两种方式分别来做。

Vibe Coding 的过程

第 1 轮:

帮我做一个 Todo List,单个 HTML 文件,能添加、删除、标记完成,界面简洁好看。

AI 生成了一个基本版本,能用,但功能不全。

第 2 轮:

再加个筛选,可以看全部、未完成、已完成。左下角显示还有多少项没完成。

第 3 轮:

加个清除已完成的按钮。数据存 localStorage,刷新不丢。

第 4 轮:

双击文字可以编辑,回车保存,Esc 取消。

第 5 轮:

输入框空的时候别让提交。界面再美化一下。

5 轮对话,约 10 分钟,得到一个能用的 Todo 应用。功能齐全,界面也过得去。

Spec Coding 的过程

Spec Coding 的第一步不是写代码,而是先写一份 Spec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# Todo List 应用 — 技术规格

## 1. 数据模型

每个待办事项:
- id: string (使用 crypto.randomUUID,回退到时间戳+随机数)
- text: string (trim 后存储,1~500 字符)
- completed: boolean
- createdAt: number (毫秒时间戳)

应用状态:
- todos: Todo[]
- filter: 'all' | 'active' | 'completed'
- editingId: string | null

## 2. 功能定义

### 添加
- 触发:输入框 Enter 或点击添加按钮
- 规则:trim 后为空则忽略,超过 500 字符截断
- 行为:清空输入框,焦点保持在输入框

### 切换完成
- 触发:点击复选框
- 行为:toggle 对应 todo 的 completed 字段

### 删除
- 触发:点击删除按钮
- 行为:直接移除(无需确认)

### 内联编辑
- 触发:双击 todo 文本
- 行为:替换为 input,自动聚焦并选中文本
- 保存:Enter 或失焦
- 取消:Escape(恢复原值)
- 边界:编辑后为空则删除该条目

### 筛选
- 三个状态:all / active / completed
- 切换筛选不修改数据

### 清除已完成
- 移除所有 completed === true 的条目

### 计数
- 底部显示:「X 项未完成」

## 3. 持久化

- 存储方式:localStorage
- Key:spec-todo-data
- 时机:每次状态变更后同步写入
- 容错:解析失败时回退为空数组

## 4. 渲染策略

- 使用 document.createElement 创建 DOM,不用 innerHTML(防止 XSS)
- 使用事件委托
- 每次状态变更后全量重渲染

## 5. 键盘支持

- 主输入框:Enter 添加
- 编辑输入框:Enter 保存 / Escape 取消

## 6. 样式要求

- CSS 自定义属性管理颜色和间距
- 完成态:删除线 + 降低透明度
- 筛选按钮:当前选中态有视觉区分
- 删除按钮 hover 时才显示
- 支持最小宽度 320px

然后把 Spec 交给 AI,一次性生成。约 15 分钟(含写 Spec),同样得到一个功能完整的 Todo 应用。

过程差异

对比项Vibe CodingSpec Coding
步骤5 轮对话,逐步添加功能1 份 Spec + 1 次生成
耗时约 10 分钟约 15 分钟(含写 Spec)
认知负荷低,每轮只需想一件事高,需要一次性想清楚所有边界
出错成本低,每轮可以立即修正中,Spec 有遗漏会影响最终结果

二、产出差异:看得见的和看不见的

功能上,两个版本几乎一样。用户能看到的差异很小,真正的差距藏在用户看不到的地方。

1. 安全性

Vibe Coding 产出的代码可能会使用 innerHTML + 字符串拼接来渲染列表。如果用户输入下面这段内容作为 todo:

1
<img src=x onerror=alert(1)>

这段脚本就可能被直接执行。

Spec Coding 在 Spec 中明确写了「使用 document.createElement,不用 innerHTML」。通过 textContent 处理文本内容,特殊字符会被当作普通文本展示,XSS 风险从根本上被降低。

2. ID 冲突

Vibe Coding 常见写法是用 Date.now() 生成 ID。同一毫秒内添加两个 todo,就可能发生 ID 冲突。

Spec Coding 在 Spec 中定义了 crypto.randomUUID(),使用 128 位随机 UUID,几乎不可能冲突。

3. 错误处理

Vibe Coding 可能直接这样读取数据:

1
JSON.parse(localStorage.getItem('todos'))

如果 localStorage 里存了非法 JSON,页面可能直接报错甚至白屏。

Spec Coding 在 Spec 中定义了容错策略:用 try/catch 包裹,解析失败时回退为空数组。

4. 空状态

Vibe Coding 的版本里,列表为空时可能只显示一片空白。用户不知道是没有 todo,还是页面坏了。

Spec Coding 会明确要求显示「暂无待办事项」这样的空状态提示。

5. 代码结构

对比项Vibe CodingSpec Coding
CSS 颜色硬编码,比如 #5b7ff5 出现多次CSS 自定义属性,改一处全局生效
DOM 创建innerHTML 模板字符串createElement 逐个构建
事件绑定内联 onclickaddEventListener,行为与结构分离
状态管理散落的全局变量和函数集中式 state 对象 + setState 入口
代码分区无明显分层Data / State / Actions / Rendering / Events 分层

6. 交互细节

Vibe Coding 的删除按钮可能始终可见。Spec Coding 可以明确要求删除按钮 hover 时才显示,让界面更干净,减少视觉噪音。

核心差异总结

维度Vibe CodingSpec Coding
速度快,分分钟出活慢,前期投入多
功能完整度完整,靠多轮追问补齐完整,靠 Spec 一次覆盖
安全性有隐患,比如 innerHTML、XSS主动防御,比如 DOM API、校验
错误处理基本没有从 Spec 开始就定义
代码可维护性差,结构松散,全局函数散落好,分层清晰,职责分明
边界情况容易遗漏Spec 已列明,实现时不容易漏
可扩展性低,加功能容易牵一发动全身高,各层独立,改动范围更可控

三、定义

Vibe Coding:用自然语言描述你想要什么,让 AI 直接生成代码。你不需要理解每一行代码的含义,只需要看结果「对不对」,不对就再用自然语言让 AI 改。全程凭感觉、凭直觉。

Spec Coding:在 AI 生成代码之前,先写一份结构化的规格说明,定义清楚数据模型、功能边界、接口契约、错误处理、渲染策略。然后让 AI 按照 Spec 来实现。

一个靠感觉驱动,一个靠规格驱动。名字本身就是最好的注脚。

四、起源

Vibe Coding 由 Andrej Karpathy 在 2025 年 2 月提出。他的原话大意是:有一种新的编程方式,你完全沉浸在氛围里,接受 AI 给出的一切,不深究代码细节,只看它能不能跑。他把这叫 vibe coding。

这个概念迅速走红,因为它精确描述了大量开发者的实际体验,特别是用 Cursor、Copilot 这类工具时的那种状态。

Spec Coding 没有一个标志性的「命名时刻」,但它的实践正在快速凝聚成共识。随着越来越多团队发现纯 vibe coding 在复杂项目中的天花板,一种「先写清楚再让 AI 干活」的方法论逐渐成型。

本质上,这是软件工程中「先设计再编码」传统在 AI 时代的复兴。

五、各自真正的优势

Vibe Coding 的优势不只是「快」

很多人以为 vibe coding 就是偷懒,其实不是。它的核心优势是降低了认知负荷的启动成本。

编程最难的部分从来不是写代码,而是「从零到一的那一步」:确定技术栈、搭建项目结构、写第一个文件。Vibe Coding 把这一步几乎降为零。你有一个想法,说出来,AI 给你一个能跑的东西,然后你在这个基础上迭代。

这对以下场景特别有价值:

  • 快速验证想法:产品经理想试试某个交互方案能不能实现
  • 学习新技术:不熟悉某个框架,让 AI 生成一个 working example 来研究
  • 一次性任务:数据格式转换、批量文件处理、临时脚本
  • 创意探索:艺术项目、互动实验、hackathon

Spec Coding 的优势不只是「规范」

Spec Coding 的核心优势不是产出了一份文档,而是强迫你在写代码之前想清楚。

这一点被严重低估了。大部分软件项目失败的原因不是代码写得烂,而是需求没想清楚、边界没定义好、异常情况没考虑到。写 Spec 的过程本身就是一种深度思考的工具。

从 Todo List 的例子就能看出来:当你在 Spec 中写下「容错:解析失败时回退为空数组」,你就是在主动思考一个之前可能忽略的边界情况。Vibe Coding 不会逼你想到这一点,直到用户真的遇到白屏。

此外:

  • AI 的输出质量与输入质量成正比。你给的约束越精确,AI 越不容易自由发挥出错
  • 可调试性更强。当出了问题,你有一个明确的规格来判断「是 Spec 的问题还是实现的问题」
  • 可维护性更好。三个月后回来看,Spec 告诉你当时的意图,而不是让你去猜
  • 团队协同更稳定。多个人可以用同一份 Spec 让 AI 生成不同模块,保证一致性

六、各自真正的代价

Vibe Coding 的代价

你以为你在省时间,但你可能在积累债务。

Todo List 的例子很清楚:Vibe Coding 产出的代码功能上没问题,但 innerHTML 有 XSS 风险、Date.now() 会 ID 冲突、localStorage 没有容错、列表为空没有提示。这些问题在 demo 阶段看不到,在生产环境里会一个个冒出来。

当 AI 生成的代码你没读过、不理解,这些问题会在未来某个时刻爆发:

  • 隐蔽的安全漏洞:AI 可能生成了有注入风险的代码,你没检查
  • 不一致的状态管理:第一个模块用 Redux,第二个用 Context API,因为是不同对话生成的
  • 无法调试:出了 bug,你不知道从哪下手,因为你不知道代码是怎么组织的
  • 不可扩展:当需求变复杂,推倒重来的成本可能比从头设计还高

最致命的问题是:Vibe Coding 会给你一种虚假的确定感。东西能跑,你觉得做完了;但实际上,你只看到了冰山露出水面的部分。

Spec Coding 的代价

你需要在还不确定的时候就做出决定。

  • 前期投入大:写一份好的 Spec 可能比直接让 AI 写代码还花时间
  • 过度设计的风险:在探索阶段,过早固化 Spec 可能是浪费
  • 需要经验:写 Spec 本身是一种需要练习的技能,新手可能写不出有价值的 Spec
  • 灵活性降低:方向变了,Spec 也要跟着改,维护成本不为零

七、什么时候用哪个

可以用一个确定性和复杂度矩阵来判断:

低复杂度高复杂度
高确定性(需求清晰)轻量 Spec,够用就好完整 Spec,必须写
低确定性(还在探索)纯 Vibe Coding,最佳场景Vibe 开始,逐步补 Spec

左上角:我要写一个 CSV 转 JSON 的脚本。需求明确,也简单。写个 Spec 没必要,直接 vibe 出来就行。

右下角:我想做一个新产品,但还不确定用户要什么。先 vibe 一个原型验证,确认方向后,再补 Spec 进入正式开发。

右上角:我要做一个处理支付的系统。需求明确,复杂度高,错误成本大。必须先写清楚 Spec,不能让 AI 自己决定如何处理退款逻辑。

回到 Todo List 这个例子。它本身是个低复杂度项目。但如果你的 Todo 应用要处理用户数据、要多人协作、要长期维护,那即使项目看起来「简单」,也应该走 Spec Coding。项目的正式程度,不只是由代码行数决定的。

八、一个被忽略的事实

最好的工作流不是二选一,而是在两者之间切换。

实际场景往往是这样的:

  1. Vibe:用自然语言让 AI 生成初始原型,快速看到结果
  2. 审视:读代码,理解 AI 做了什么设计决策,找到问题
  3. 提炼 Spec:基于原型反向提炼出架构和接口定义,把发现的问题写进约束
  4. 重构:让 AI 按照 Spec 重写或重构代码
  5. 迭代:新功能用 Spec 驱动,小修补用 Vibe 驱动

Todo List 的例子可以这样用:先 vibe 出来一个能跑的版本,然后审视代码发现 innerHTML 有 XSS 风险、ID 可能冲突、没有错误处理。把这些发现写进 Spec,再让 AI 重写一版。

这个流程的本质是:用 Vibe Coding 来探索,用 Spec Coding 来固化。探索阶段容忍模糊,固化阶段要求精确。

九、更深一层的思考

AI 让写代码变便宜了,但让想清楚变贵了

当实现成本趋近于零,真正的瓶颈回到了思考本身。你能多精确地描述你想要的东西?你能多全面地考虑边界情况?你能多清晰地定义系统之间的交互?

Todo List 例子中,Vibe Coding 5 轮对话 10 分钟就搞定了。但那些 XSS 风险、ID 冲突、存储容错、空状态处理,这些「想清楚」的部分,全都是事后才补上的,甚至根本没被发现。

这些问题,Vibe Coding 回答不了。Spec Coding 逼着你回答。

Spec 是给 AI 的「类型系统」

TypeScript 通过类型约束让 JavaScript 更可靠。Spec 通过结构化描述让 AI 生成的代码更可靠。两者解决的是同一个问题:如何在灵活性和可靠性之间取得平衡。

从这个角度看,写 Spec 正在成为一种新的「编程」:你不是在写代码,你是在用自然语言编程,而 AI 是编译器。

开发者的角色正在分化

Vibe Coder:擅长快速原型、创意探索、跨领域尝试。门槛低,上限取决于品味和判断力。

Spec Coder:擅长系统设计、架构思考、精确表达。门槛高,上限取决于工程能力和抽象能力。

两者兼具:知道什么时候该 vibe,什么时候该 spec,能在两种模式间自由切换。这是目前最有价值的开发者画像。

十、结论

Vibe Coding 和 Spec Coding 不是对立的。它们是 AI 编程光谱的两端,解决的是不同阶段的问题。

Vibe Coding 的价值在于速度和低门槛:让更多人能把想法变成现实。

Spec Coding 的价值在于质量和可维护性:让现实能持续存在和发展。

纯 Vibe Coding 出来的东西,大概率会在三个月内变成没人能维护的债务。纯 Spec Coding 在小项目上又可能是过度工程。

一个 Todo List 的例子已经说明了一切:功能可以一样,但工程质量完全不同。安全性、容错性、可维护性这些用户看不到的东西,恰恰是区分玩具和产品的分界线。

真正的技能不是选一个,而是知道什么时候该切换。