跳转到内容

我不 vibe code,不是因为洁癖

昨天读到 Jacob Harris 写的一篇长文,题目很直白:Why I Don’t Vibe Code。他列了六条理由——抠门、老了、热爱混乱、摩擦是礼物、在乎、以及一些零碎的个人偏好。读完后我想了挺久。

我自己不 vibe code。但理由跟 Harris 不太一样。他写的是一个资深开发者对编程的感情,我写的是一个非科班程序员对工具边界的实际判断。

先把话说清楚:我不是 AI 编程的反对者。我的工作流里塞满了 AI 工具。把重复三次的事交给 AI 做,这是我自己的信条。重构一个写了八百遍的模块、生成正则表达式、查 ImageMagick 的参数——这些我都丢给 AI,而且丢得很痛快。

vibe code 和用 AI 辅助编程的区别在哪?vibe code 是用自然语言描述需求,然后让 LLM 吐出完整代码,你坐在旁边当产品经理。AI 辅助编程是你握着方向盘,AI 帮你踩油门。

听起来差别不大。关键在”谁在思考”。

当你让 LLM 生成整个模块时,真正做设计决策的是它。它选了哪个架构模式、哪个数据流方向、哪层抽象——你不知道。你知道的是它跑通了测试,demo 看起来不错。

但软件不是活在 demo 里的。

有人总结过一句话,我印象很深:vibe code 在 demo 中完美,在 production 中崩溃。代码通过了测试,但悄悄违反了系统里另一个模块的事务边界假设。为什么?因为 LLM 不知道你系统里十五年前那个工程师为什么写了那个看起来多余的 try-catch,它只知道当前这个文件的上下文。

Harris 有一条我很认同:friction is a gift。编程中感到别扭的时候,通常是架构在给你发信号。

我有一次写一个状态管理模块,越写越难受。每个新功能的加入都要在三个地方做同样的判断逻辑,改一个漏一个就出 bug。那种感觉像是在泥地里走路——不是走不了,是每走一步都在消耗额外的能量。

我停下来走了一圈。回来把判断逻辑提到一个中间层,三处变一处。那种”通了”的感觉,不是 LLM 能给你的。因为 LLM 感觉不到泥地。你让它写,它会继续在泥地里写三处,写得很工整,注释也漂亮,然后问你还有没有别的需求。

摩擦不是要你消除的东西。它是系统在告诉你:这里的设计有问题,换个角度想。

消除摩擦最快的方式是把键盘交给 LLM。它会写出跑通过的代码。但那个”通了”的瞬间——你失去了。而那个瞬间恰恰是你在理解系统的形状。

你手写代码:
困难 ──→ 困惑 ──→ 散步 ──→ 想通 ──→ 架构变简单
vibe code:
描述需求 ──→ 代码生成 ──→ 测试通过
(然后六个月后出了 bug,你看不懂自己没写过的东西)

Harris 在文章里引了一个类比,很准:让 LLM 认识自身视角的局限,就像让金鱼描述水。

Robin Sloan 写过一篇散文叫 Are Language Models in Hell?。他说 LLM 接收的是 stripped-down 的 subword tokens——它看不到文本的上下文,格式、作者简介、来源网站,全都没有。文字就是它的世界的全部。

这让我想到 James Scott 在《国家的视角》里讲的森林。政府看森林,看到的是造船木材的百分比。一个活生生的生态系统被抽象成一个数字。然后政府用这个数字做决策——种单一树种,砍掉其他所有树。高效、可读、脆弱。

LLM 看代码也是如此。它看到的是 token 序列,不是这个系统要解决的实际问题、用户的真实场景、历史决策的约束条件。它给你的代码在 token 层面是通的,在现实层面可能是漏的。

这不是 LLM 的错。它的认知边界就是这样。问题是人把自己的判断外包给了一个不知道”自己不知道什么”的东西。

Harris 在文章里举了一个更狠的例子:2025 年 DOGE 查 SSA 数据库,发现 900 万条超过 120 岁但没标记死亡的记录,Musk 说是大规模欺诈。实际上是数据质量问题。DOGE 那帮人看数据就像 LLM 看 token——只看表面,不看来源,不问”这个数字是怎么来的”。

前 SSA 局长 Michael Astrue 说了一句话,我觉得可以原封不动用在 vibe coding 上:

我们派了一群二十来岁的年轻人拿着笔记本电脑进去,他们从没见过这套系统。出问题不可怕,出问题了还不承认才可怕。

你让一个没读过你代码库的 LLM 去设计架构,和让一个没看过 SSA 历史数据的人去查欺诈,逻辑是一样的。

我不 vibe code,不代表我不用 AI 写代码。我用 AI 的方式可能更接近 Harris 说的”AI 辅助编程”——我握着方向盘。

具体来说,我用 AI 写代码分三种情况。

第一种是加速已知路径。正则怎么写、某个 API 的参数是什么、ImageMagick 怎么批量压缩图片——我知道要什么,只是不想翻文档。AI 做这个比搜索快十倍。

第二种是探索没碰过的东西。要接一个新 API,会让 AI 先出一个架子。但我不直接用,逐行读,理解它为什么这样写,改掉不认同的部分。比从头写快,但比让 AI 生成全部代码慢——慢的那部分是我在学习。

第三种是对抗自己的盲区。写完一个模块丢给 AI review,它经常能发现我忽略的 edge case。但它指出来就好,我自己改。因为怎么改本身就是一个设计决策。

这三种情况的共同点:判断权在我手里。AI 给我信息、给我选项、给我加速度。但”为什么选这个”是我来答。

Harris 最后一条是”我在乎”。编程是他创造力的表达方式,他不想把这个交给机器。

我有同感,但角度不同。我更关心的是所有权——出了事谁负责。

在新闻行业,代码写错了要登更正,严重的会吃官司。在 civic tech,代码错了可能是一整个弱势群体拿不到福利。我虽然不在这些行业一线,但我做工具给别人用的时候,那个责任感是一样的。

LLM 不负责任。它不出错——因为”错”这个概念对它不存在。它只是输出概率最高的 token 序列。出错了怪你 prompt 写得不对,成功了怪你没早用。这个动态很眼熟——跟 Tesla 的粉丝文化一模一样。新功能不经过安全测试直接推送,出了事怪用户操作不当。

我不愿意把我的判断权交给一个不能负责的东西。不是因为洁癖,是因为我睡不着觉

我不 vibe code,不是因为 AI 编程没有价值。我认为它有价值,所以才要搞清楚边界在哪。

AI 编程最擅长的是把已知的东西做得更快。它不擅长的是把模糊的东西变清晰。而编程里最难的部分,恰恰是把模糊的东西变清晰——理解用户到底要什么、系统现在的瓶颈在哪、哪个抽象能同时覆盖今天的场景和明天的变化。

那部分工作,friction 带来 insight,insight 带来好设计。没有好设计,后面跑得再快也是朝错误的方向。

所以我还是自己写。AI 帮我踩油门,我自己转方向盘。不是因为这样做更”纯粹”,是因为只有这样,出了 bug 的时候我知道去哪找原因。

你在使用 AI 编程工具时,有没有一条明确的线——线这边你放心交给 AI,线那边你一定要自己动手?那条线在哪?

Jacob Harris, Why I Don’t Vibe Code, March 05, 2026 https://jacobharr.is/personal/i-dont-vibe-code