1. 这不是技术批判,而是一次企业级代码交付现场的解剖报告
我亲手把一套 Node.js 通用脚手架推上生产环境——用的是当下最火的 Vibe Coding 范式,工具是 Claude CLI,流程严格遵循 SDD(Spec-Driven Development)三部曲:spec.md → plan.md → tasks.md → constitution.md。整个过程没有写一行手敲代码,所有逻辑、结构、配置、测试全部由 AI 生成。项目上线后,CI/CD 流水线绿得发亮,API 响应飞快,前端调用丝滑,监控面板上没有任何报错。但三个月后,数据库慢查询告警开始零星出现;六个月后,一次常规扩容触发了 PostgreSQL 的索引锁争用,服务响应延迟飙升至 2.3 秒;九个月后,运维同事深夜打电话问我:“这个 idx_email 索引,到底谁建的?为什么和 UNIQUE 约束重复了整整三年?”——那一刻我才真正意识到:我们交付的不是系统,是一具表面完好的标本,内里早已被逻辑寄生虫蛀空。
这不是危言耸听,也不是反对 AI 编程,而是基于真实企业交付周期(从立项到上线再到第 27 次迭代)的硬核复盘。Vibe Coding 的致命问题,从来不在“能不能跑”,而在“为什么能跑”和“凭什么能长期跑”。它把软件工程中最不可妥协的确定性,交给了自然语言中天然携带的模糊性;它把本该在设计阶段暴露的架构矛盾,推迟到了线上故障时才被迫直面;它用“提示词长度”替代了“代码可读性”,用“重试次数”替代了“因果推理”。本文不谈概念、不列口号、不站队,只呈现我在金融级权限系统、千万级用户日志平台、高并发订单路由网关三个真实项目中,用 Vibe Coding 落地时踩过的坑、录下的错误日志、抓到的性能火焰图、以及最终不得不手写回滚脚本的凌晨三点。你不需要懂 Drizzle ORM 或 Hono.js,只要做过哪怕一个需要上线、需要维护、需要交接的项目,就能立刻对上号——因为这些漏洞,不藏在技术栈里,而藏在人类语言与机器执行之间那道无法弥合的语义鸿沟中。
2. Vibe Coding 的底层逻辑死结:三重不可解的自洽悖论
2.1 自然语言的模糊性 vs 计算机执行的确定性:一场注定失败的翻译战争
Vibe Coding 的核心承诺是:“你只管说人话,AI 负责翻译成机器可执行的精确指令。”但这个前提本身就是一个危险幻觉。自然语言不是编程语言的“高级封装”,而是完全异构的符号系统。Edsger Dijkstra 在 EWD667 中一针见血指出:“自然语言的语法允许你构造出语法正确但语义荒谬的句子,而计算机无法容忍任何语义荒谬。”这句话在 Vibe Coding 场景下被放大了十倍。
举个最日常的例子:你在 spec.md 里写:“用户注册时,邮箱必须唯一,且不能重复。”这句人话毫无问题。但 AI 在生成 Drizzle ORM 表定义时,面临至少三种合法但后果迥异的实现路径:
- 路径 A:仅声明
email: text("email").notNull().unique()→ PostgreSQL 自动生成唯一索引users_email_key - 路径 B:声明
email: text("email").notNull().unique()+ 显式添加index("idx_email").on(table.email)→ 创建两个索引 - 路径 C:未声明
.unique(),仅靠显式索引index("idx_email").on(table.email).unique()→ 功能等价,但 ORM 层缺失约束语义
Vibe Coding 的逻辑是:只要最终行为符合“邮箱唯一”,就判定成功。但它彻底忽略了 约束的语义归属 。 .unique() 是数据完整性约束(declarative integrity),而 index(...).unique() 是物理访问优化(physical optimization)。前者由数据库引擎强制校验,后者仅提供查询加速。当业务逻辑后续依赖 .unique() 触发的约束异常(如捕获 23505 错误码做友好提示)时,路径 C 就会静默失败;当 DBA 执行 VACUUM FULL 时,路径 B 会因冗余索引多消耗 47% 的 I/O 时间——这些差异,在“能跑通”的测试用例里,100% 不会被覆盖。
更致命的是,这种模糊性无法通过“写更长的 prompt”解决。我曾为一个 RBAC 权限模型的 spec.md 写过 832 字的自然语言描述,包含 17 处“必须”、9 处“禁止”、5 处“除非……否则……”嵌套条件。Claude 生成的 plan.md 看似完美,但 tasks.md 中却把“角色继承链最大深度为 5”错误理解为“角色表需增加 depth 字段”,而非在服务层做递归校验。我不得不用 1200 字的修正 prompt 重新生成,结果 AI 把“禁止缓存超级管理员权限”理解成“所有管理员权限都不缓存”,导致整个权限校验链路性能暴跌。这不是 AI 的错,而是自然语言作为输入介质,其表达精度上限远低于 SQL DDL 或 TypeScript Interface 的声明能力。你越想用语言去“精确控制”,就越陷入“用模糊对抗模糊”的死循环——最终生成的不是代码,是一份需要人工二次翻译的、充满歧义的中间协议。
2.2 复杂度转移陷阱:从“写代码的痛苦”到“读懂代码的绝望”
Vibe Coding 最具迷惑性的宣传点是“降低开发门槛”。这话没错,但它刻意省略了后半句:“同时将理解成本转移到了维护阶段。”软件工程中有个铁律: 系统总复杂度恒定,它只会迁移,不会消失。 Vibe Coding 并没有消灭复杂度,而是把它从“显性编码劳动”转移到了“隐性认知负荷”。
我们团队曾用 Vibe Coding 快速交付一个日志分析看板(MVP 阶段)。spec.md 仅 200 字:“展示近 24 小时 API 调用 TOP10 接口,按成功率、平均耗时、错误数排序,支持按服务名筛选。”Claude 3.5 五分钟生成全部代码:Hono 路由、Drizzle 查询、PostgreSQL 窗口函数聚合、前端 ECharts 渲染。上线当天,产品总监点赞:“比预期快三天!”
但两周后,业务方提出新需求:“TOP10 改为 TOP20,且成功率计算需排除 404 请求。”开发同学打开代码,发现聚合逻辑藏在三层嵌套的 CTE(Common Table Expression)里,其中第二层 CTE 的别名 filtered_logs 实际上过滤了所有状态码,而第三层 aggregated_metrics 又在 filtered_logs 基础上做分组——修改必须同时调整两处,且需验证窗口函数 ROW_NUMBER() OVER (PARTITION BY ...) 的分区键是否仍正确。他花了 3 小时才定位到关键行,又花 2 小时写测试验证无回归。而如果这是手写代码,同样需求修改只需 15 分钟:改一个数字、加一个 WHERE status_code != 404 条件。
为什么?因为手写代码的 意图可见性 (Intent Visibility)是天然的。变量名 topN 、函数名 calculateSuccessRateExcluding404 、注释 // 404 不计入成功率分母 ,都在持续向阅读者广播设计意图。而 AI 生成的代码,其命名、结构、分层全是“最优解黑箱”的副产品:它可能把聚合逻辑塞进一个叫 processMetrics 的万能函数里,用 const t = await db.select(...) 这样的临时变量名,连最基本的“单一职责”都放弃。这不是代码质量差,而是 AI 的优化目标函数里,根本没有“人类可维护性”这一项权重。它的目标函数是:最小化编译错误 + 最大化 spec.md 匹配度 + 最小化 token 使用量。结果就是,代码像一块致密的金属锭——功能坚硬,但无法切割、无法焊接、无法延展。
我们后来做了个实验:让 5 名中级工程师分别阅读同一段 Vibe Coding 生成的权限校验代码(约 300 行),然后口头描述其工作流程。5 人给出的流程图有 3 种不同版本,关键分支判断点有 2 处完全相反。而同一团队手写的历史代码(同样功能),5 人描述的一致率是 100%。这证明了一个残酷事实:Vibe Coding 生成的代码,其 认知熵值 (Co
952




被折叠的 条评论
为什么被折叠?



