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 | # Todo List 应用 — 技术规格 |
然后把 Spec 交给 AI,一次性生成。约 15 分钟(含写 Spec),同样得到一个功能完整的 Todo 应用。
过程差异
| 对比项 | Vibe Coding | Spec 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 Coding | Spec Coding |
|---|---|---|
| CSS 颜色 | 硬编码,比如 #5b7ff5 出现多次 | CSS 自定义属性,改一处全局生效 |
| DOM 创建 | innerHTML 模板字符串 | createElement 逐个构建 |
| 事件绑定 | 内联 onclick | addEventListener,行为与结构分离 |
| 状态管理 | 散落的全局变量和函数 | 集中式 state 对象 + setState 入口 |
| 代码分区 | 无明显分层 | Data / State / Actions / Rendering / Events 分层 |
6. 交互细节
Vibe Coding 的删除按钮可能始终可见。Spec Coding 可以明确要求删除按钮 hover 时才显示,让界面更干净,减少视觉噪音。
核心差异总结
| 维度 | Vibe Coding | Spec 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。项目的正式程度,不只是由代码行数决定的。
八、一个被忽略的事实
最好的工作流不是二选一,而是在两者之间切换。
实际场景往往是这样的:
- Vibe:用自然语言让 AI 生成初始原型,快速看到结果
- 审视:读代码,理解 AI 做了什么设计决策,找到问题
- 提炼 Spec:基于原型反向提炼出架构和接口定义,把发现的问题写进约束
- 重构:让 AI 按照 Spec 重写或重构代码
- 迭代:新功能用 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 的例子已经说明了一切:功能可以一样,但工程质量完全不同。安全性、容错性、可维护性这些用户看不到的东西,恰恰是区分玩具和产品的分界线。
真正的技能不是选一个,而是知道什么时候该切换。