你的 vibe coding 应用总是坏,你却查不出原因
你让 AI 给结账页加个折扣字段,它加好了。你把结账走了一遍,没问题,就发布了。两天后有用户发邮件说购物车页显示的总价不对。你去一看,购物车总价从改折扣那天起就一直是错的——因为 AI 顺手改了购物车引用的一个共享货币工具函数。那个工具函数你从没打开过,那天购物车你也没打开过。这个回归在生产环境里躺了两天,因为压根没东西提醒你出了岔子。
这就是 vibe coding 里没人提醒你的那一面。发布一个功能变便宜了,可要知道应用其余部分还正不正常,一点没变便宜。
快出来的代码是你没读过的代码
自己手写应用的时候,脑子里会有一个大致的模型。你知道哪些文件跟购物车有关,知道那个格式化金额的工具函数,也知道一处改动大概会牵连到哪儿。出问题时你能猜到该去哪看,靠的基本就是这个模型。
AI 帮你飞快写掉一大半应用时,这一步就被跳过了。代码落了地,扫一眼没毛病,你就接着往下走。几周下来,一堆页面你从没逐行读过,一堆依赖关系你根本不知道存在。脑子里的模型现在比应用本身还小。于是下一条 prompt 一改动什么,你既没法预测它碰了哪儿,也没有那种直觉知道问题可能藏在哪。
为什么这事在 vibe coding 里更难受
改动出问题的地方在三个页面之外,这是常态,不是偶发。AI 没有你那种对模块边界的感觉。为了把当前功能写得干净点,它会很痛快地重构一个共享工具函数,而这个函数没准正是某条你早忘了自己写过的流程的承重墙。
预测不了影响范围时,你只有两条路。一是每次改动都手动把整个应用重测一遍——这比写功能本身还慢,撑不到周四你就放弃了。二是直接发布,让用户替你发现故障。你其实没真做过这个决定,就选了第二条,因为第一条根本扛不住规模,而眼前又没有第三条路。
其实是有第三条路的。人类团队用了几十年就是这一条,而且你不用自己动手写测试就能用上它。
一张由可读 spec 织成的回归网
回归测试只干一件事:把一条真实流程跑一遍,行为一变就大声报错。每条 prompt 之后都跑一遍这套 suite,购物车总价那个 bug 几秒钟就会现形,而不是拖上两天,还会附带一条指向出错断言的堆栈。你再也不用猜问题在哪了。
一直以来的坎在于:总得有人来写、来维护这些测试,而在 vibe coding 应用里,本该写测试的那个人,恰恰是没读过代码的那个人。Hover 就是来解决这个的。它是一个免费、开源的 MCP server,加进你已经在用的编码 agent 就行。agent 会在你真实的浏览器里把你的真实应用跑一遍,然后 Hover 把这次运行 crystallize 成一个普通的 @playwright/test spec,selector 用的是语义化的 getByRole 和 getByLabel。没有新的测试框架,没有私有格式,这个文件就是你能读、能改的标准 Playwright。
它在 CI 里跑,回路里没有任何 AI。agent 只是帮你把 spec 写出来,spec 真正运行时它并不在场。每次 push 执行的都是确定性的 Playwright 代码,跟人手写出来的一模一样。测试这一面本身是另一个话题,第一篇已经讲过。
让 spec 一直保持鲜活
回归网只有一直可读、一直跟得上应用,才真正有用,而自己攒的 suite 通常就烂在这儿。Hover 从两方面让 spec 保持好维护。
spec 变长以后,你可以跑一个可选的优化流程。它会给出一个更干净的版本:用 page object 把 selector 收拢到一处,用带名字的 test.step 阶段让失败能直接告诉你流程哪一段断了,断言则取自运行期间真正发生的事。你像看 diff 一样审一遍,满意就接受。原来那个确定性 spec 会一直留着,所以优化绝不会让你丢掉一个能用的测试。
UI 真的变了的时候,你从扩展里把这个 spec 重新录一遍,而不是一个个手动去补 selector。你把新流程走一遍,Hover crystallize 出新版本,spec 就反映出应用现在的样子。与其在一个你压根不想维护的文件里追着坏掉的 selector 跑,不如干脆重录一遍。更多让 suite 不腐烂的招数,都在这篇指南里。
这套模式,正是优秀工程团队一直在用的那套。你改动应用,suite 告诉你什么动了,你要么修好,要么认下这次改动。vibe coding 没有终结这个循环,它只是拿掉了其中一环——你不再需要逐行读代码才能参与进来。
系列下一篇:那些不会弄坏流程、却会敞开一扇门的 bug。第三篇讲 vibe coding 应用发布时自带的安全漏洞。
在你自己的应用上试试 Hover。
把 Hover 的 MCP 加到你已经在用的编码 agent。它探索你的应用,结晶出你自己拥有的纯 Playwright spec。
npm i -g @hover-dev/mcp && claude mcp add hover -- hover-mcp阅读快速开始 →