Vibe Coding 为什么经常翻车?深度解析 4 个根本原因

0 阅读9分钟

本文适合:已经在用 AI 辅助编程,但发现效果不稳定、经常翻车的开发者


我在上一篇文章里聊了 AI 编程的五个阶段,有读者在评论区问:

"Vibe Coding 我也试过,前几天用得挺顺的,但项目一大就开始出问题,改一个地方坏一个地方,到最后整个项目我自己都不认识了。这是为什么?"

这个问题问到点子上了。

Vibe Coding 翻车,不是因为你不会用 AI,也不是因为模型不够强——是因为有四个根本性的技术原因,大多数人不了解这些原因,所以踩了坑也不知道为什么

这篇文章不是在否定 Vibe Coding,而是帮你建立一个更准确的心智模型,知道它在哪里会出问题,以及怎么避开这些坑。


先看现象:为什么项目越大越容易翻车

一个典型的翻车路径是这样的:

  1. 项目初期,用 Vibe Coding 飞快搭出原型,感觉很爽
  2. 项目规模扩大,开始有多个模块、多个文件、复杂的依赖关系
  3. 你让 AI 改一个功能,它改好了,但另一个地方悄悄坏了
  4. 你把报错贴给 AI,它修了这里,那里又坏了
  5. 循环几轮之后,整个代码库变成一坨谁也看不懂的"面条代码"

这不是偶然,这是 Vibe Coding 的系统性问题。原因有四个。


根本原因一:上下文窗口是有限的,AI 看不到整个代码库

LLM 有一个核心限制:它只能"看到"当前对话里你给它的内容

每一次对话,AI 的视野范围就是你的输入——你粘贴的代码、你描述的需求、对话历史。这个范围有上限,叫做上下文窗口(Context Window)

Context Engineering 全貌

context-engineering.png

▲ LLM 的上下文窗口就是它的"工作台"——所有能影响输出的信息,必须在这个有限的空间里。(图源:Weaviate)

现代模型的上下文窗口已经很大了,Claude 是 200K token,Gemini 可以到百万 token。但问题是:大多数人用 Vibe Coding 的方式,根本没有系统地把项目上下文喂给 AI

具体会发生什么?

你的项目                    AI 看到的视野
────────────────           ─────────────────────
user.service.ts    ───►    user.service.ts(你粘贴的)
order.service.ts           (看不到)
auth.middleware.ts         (看不到)
database.config.ts         (看不到)
types/index.ts             (看不到)

你让 AI 修改 user.service.ts,它只看到这一个文件。它不知道 order.service.ts 里有个函数依赖 user.service.ts 的某个返回值格式,也不知道 auth.middleware.ts 有个类型断言依赖用户对象的结构。

于是 AI 修改了 user.service.ts在它的视野里是完全正确的。但它不知道自己破坏了其他地方,因为它根本看不到其他地方。

这就是为什么改一个地方会坏另一个地方——不是 AI 在乱改,是 AI 在"盲操作"

解法

使用 CLAUDE.md(Claude Code)或 .cursorrules(Cursor)把项目上下文强制注入到每次对话中:

# 项目结构
- user.service.ts:用户 CRUD,返回 UserDTO 类型
- order.service.ts:依赖 UserDTO 的 id 和 email 字段
- auth.middleware.ts:从 JWT 解析出 userId,注入 req.user

# 修改约定
- UserDTO 的字段结构不能随意改动,下游有依赖
- 所有 Service 层方法必须返回统一的 ApiResponse<T> 格式

有了这层说明,AI 就知道边界在哪里,不会随意破坏约定。


根本原因二:LLM 不维护状态,每次对话都从零开始

这个问题比很多人意识到的更严重。

LLM 本身是无状态的。每一次新对话,它对你的项目一无所知。它不记得上次你说"这个函数要兼容旧版 API",不记得你们约定了"所有异步函数必须处理错误",不记得你三天前做的架构决策。

一个真实的翻车案例:

某团队用 Vibe Coding 开发了一个支付回调接口。当时 AI 生成了功能代码,验证了主流程,大家测试通过了就上线了。两个月后线上出现回调失败问题,排查了三天,发现接口完全没有任何错误日志、请求埋点、异常告警。

原因:AI 生成代码时,没有人告诉它"这是生产系统,必须有完整的可观测性"。AI 只知道"实现支付回调验签逻辑",它把这个任务完成得很好,但它不知道"生产系统的标准"是什么。

AI 没有错,它完成了你给它的任务。问题是你给的任务描述里没有包含"隐性的工程标准"。

解法

把项目的工程标准写成文档,每次对话时附上:

# 工程规范(必读)
- 所有外部 API 调用必须有 try-catch,失败时 logger.error 记录完整请求参数
- 涉及支付/用户数据的接口,必须有请求级别的埋点(requestId, userId, timestamp)
- 不得使用 console.log,统一用 logger 模块
- 每个 Service 方法必须有对应的 unit test

把这些规范沉淀到 CLAUDE.md,让 AI 每次都"记得"你的工程标准。


根本原因三:没有测试,AI 的幻觉无从验证

这是 Vibe Coding 最隐蔽、也最致命的问题。

AI 生成的代码,看起来很像对的代码。变量命名合理,逻辑流程清晰,注释详尽。但"看起来对"和"真的对"之间,隔着很大一条鸿沟。

来看一个真实的例子:

// AI 生成的工作日计算函数
function getWorkingDays(startDate: Date, endDate: Date): number {
  let count = 0;
  const current = new Date(startDate);
  
  while (current <= endDate) {
    const dayOfWeek = current.getDay();
    if (dayOfWeek !== 0 && dayOfWeek !== 6) { // 排除周末
      count++;
    }
    current.setDate(current.getDate() + 1);
  }
  
  return count;
}

这个函数看起来没问题。但如果你去测试边界情况:

// 测试:startDate 和 endDate 是同一天,且是工作日
getWorkingDays(new Date('2026-05-11'), new Date('2026-05-11'))
// 期望:1,实际:1 ✓

// 测试:startDate 比 endDate 晚(参数传反了)
getWorkingDays(new Date('2026-05-15'), new Date('2026-05-11'))
// 期望:0 或 报错,实际:0 ✓(while 条件直接不满足)

// 测试:跨年,包含法定节假日
getWorkingDays(new Date('2025-12-31'), new Date('2026-01-02'))
// 期望:1(元旦是法定假日),实际:2 ✗
// AI 没有处理法定节假日!

这个 bug 在没有测试的情况下,可能在生产环境跑了很久才被发现——或者用在了薪资计算场景,直接产生了错误数据。

Context 的常见失败模式

failure-modes.png

▲ LLM 的四类失败模式:Context Poisoning(错误信息污染上下文)、Context Distraction(被无关信息分散注意力)、Context Confusion(指令混淆)、Context Clash(信息冲突)。没有测试,这四类问题都无法被及时发现。(图源:Weaviate)

解法:TDD + AI = 最高效的组合

不是让 AI 写代码然后你来写测试,而是反过来:先写测试,让 AI 写实现。

# 你先写测试文件,明确预期行为
你:帮我实现 getWorkingDays 函数,要求通过以下测试:
    - 同一天(工作日)返回 1
    - 两个工作日之间返回正确天数
    - 跨越周末正确排除
    - 包含法定节假日时抛出提示(节假日表由 holidays 参数传入)

# AI 写实现
AI:[生成代码]

# 你跑测试
$ npm test
# 如果失败,把失败信息贴给 AI 继续修

这个流程的好处:测试是你控制的,AI 的输出必须通过你写的测试才算完成。AI 的"幻觉"无论多自洽,测试不过就是不过。


根本原因四:提示质量决定输出质量

"垃圾进,垃圾出"——这句话对 AI 同样成立,甚至更加成立。

很多人 Vibe Coding 翻车,不是因为模型差,是因为提示写得太模糊

来看一个对比:

模糊提示

"帮我写一个用户登录接口"

AI 生成了一个登录接口,但:

  • 密码是明文存储的
  • 没有限速(可以暴力破解)
  • Token 有效期是 7 天(你的需求是 2 小时)
  • 没有记录登录失败日志

这些都是"登录接口"应该考虑的事,但你没说,AI 就按它认为合理的方式做了。

精确提示

"帮我写一个用户登录接口,要求:

  1. 密码使用 bcrypt 哈希,salt rounds = 12
  2. 同一 IP 登录失败 5 次后锁定 15 分钟(用 Redis 计数)
  3. JWT Token 有效期 2 小时,不存 Cookie,放 Authorization Header
  4. 登录失败时 logger.warn 记录 IP、用户名、失败原因
  5. 参考项目里 registerUser 函数的错误处理风格"

同样的模型,同样的 Vibe Coding,输出质量天差地别。

提示质量的三个维度

维度差的提示好的提示
约束条件"写一个缓存""用 Redis,TTL 30分钟,Key 格式 user:{id}:profile"
边界情况"处理错误""网络超时重试 3 次,重试间隔指数退避,最终失败返回 503"
参考上下文"参考项目里 OrderService 的写法风格"

一个实用技巧:在提示里加上"参考项目里 XXX 的写法",AI 会自动对齐你的代码风格,而不是生成一段风格迥异的代码插入到你的项目里。


四个原因的关系

把四个原因放在一起看:

┌─────────────────────────────────────────────────────┐
│                  Vibe Coding 翻车地图                │
│                                                     │
│  原因一:上下文不足  ──►  AI 不知道项目全貌          │
│         ↓                      ↓                   │
│  原因二:无状态     ──►  AI 不记得工程约定           │
│         ↓                      ↓                   │
│  原因三:缺少测试   ──►  错误无法被发现              │
│         ↓                      ↓                   │
│  原因四:提示模糊   ──►  AI 用"合理猜测"填补空白     │
│                                                     │
│  四个问题叠加 ──► 项目越大,失控越严重              │
└─────────────────────────────────────────────────────┘

这四个原因不是独立的,它们会叠加放大。上下文不足让 AI 猜测,无状态让猜测无法被纠正,缺少测试让错误积累,模糊提示让一切更不确定。


解法对照表

根本原因核心解法工具/方法
上下文窗口有限系统性注入项目上下文CLAUDE.md / .cursorrules
LLM 无状态把工程规范文档化CLAUDE.md 的规范区块
缺少测试TDD:先写测试,让 AI 写实现Jest / Vitest + AI
提示模糊精确描述约束、边界、参考结构化 prompt 模板

小结

Vibe Coding 翻车,根本原因不是"AI 不够智能",而是你和 AI 之间的信息不对等

  • AI 看不到你的整个代码库
  • AI 不记得你的工程约定
  • AI 生成的代码没有被验证
  • AI 用"合理猜测"填补了你没说清楚的部分

理解了这四个原因,Vibe Coding 就从"玄学"变成了"可控的工程实践"。

下一篇,我会聊聊 Context Engineering——如何系统性地设计"喂给 AI 的信息",让它从一次次的盲操作,变成真正了解你项目的协作者。


作者:与 AI 结对编程
关注公众号「与 AI 结对编程」,持续更新 AI 工程实践。