vibe coding
Vibe Coding 问题解决篇:项目在本地一切正常,上线就报错?
Vibe Coding

问题解决篇:项目在本地一切正常,上线就报错?

纯靠 vibe coding 做项目,本地跑得好好的,一上线就报错。你把报错甩给 AI,它在本地改了半天代码。

几轮下来,原来正常的地方被它改乱了,线上那个报错还在原地。

一天一个 vibe coding 小技巧,今天就来解决这个问题。

一共四步,每一步该发给 AI 的原话,我都直接给你。

一、同一份代码,不等于同一个运行结果

「本地能运行」这句话,只证明了一件事:这套代码,在你那台电脑的条件下能跑。

程序不是悬在空中运行的。它得靠操作系统、运行环境、依赖、配置、数据库和外部服务一起托着。

上线以后,跑它的那台机器换了。上面这些东西,可能一样都对不上。

你电脑上的运行环境是一个版本,服务器上可能是另一个。

本地连的是测试数据库,线上连的是真实数据库。

本地文件随便读写,服务器上那个运行账号,未必有同样的权限。

本地调第三方服务走的是测试地址,上线以后要换生产地址、生产密钥,还得让对方把你这台服务器的地址放进白名单。

甚至同一个文件名,有些系统里大小写写错了照样能找到,换个系统就被当成两个文件。

报错是从这些差异里长出来的。你在本地改业务代码,当然改不掉。

当然也有真该动代码的时候。比如代码里写死了只有你这台电脑上才有的东西,这个后面我专门讲。

但顺序不能反。那种问题你也是比出来才发现的,不是猜出来的。在你还没搞清楚两边差在哪儿之前,任何对代码的修改都是猜。

那要比什么?说到底就三个问题。

第一个:两边跑的,是不是同一份东西。

你以为服务器上跑的就是你电脑里这份代码。很多时候不是。

第二个:它跑在什么地方。

也就是刚才说的那块地面,从配置一直到网络。

第三个:它面对的是什么数据。

线上那批真实数据,跟你本地造的那十条,压根不是一回事。

同一份东西、同一个地方、同一批数据。三样都对上,本地和线上才会说同一句话。

下面四步就是按这个顺序来的。第一步查第一个问题,第二步把后两个一起对完,第三步和第四步管怎么改、怎么发上去。

不过在开始之前,有个例外得先说。

如果你的项目已经有真实用户在用,而且现在正在持续出错,比如订单一直付不了、注册全卡住,那别先查。先把出问题的那个入口临时关掉,或者把版本退回上一个确认能用的。

止住了再回来走这四步。

原因很简单:差在哪儿,晚一个小时查清楚没事;用户流失和错数据,晚一个小时就收不回来了。

要是暂时还没什么人用,那就不用急,从第一步开始。

二、第一步:先确认线上跑的是哪一版

很多人排查一上来就走错,是因为把今天电脑里的代码发给了 AI,却没告诉它服务器上实际在跑哪一版。

你本地可能已经改过两次了。

服务器上还在跑昨天发的那一版。

也可能代码是新的,但程序没重启成功,内存里那份还是旧的。

还有一种最容易骗过人:前端发布成功了,后端发布失败了。

新前端开始调新接口,服务器回给它的还是旧结果。页面看着都对,数据就是不对。

这时候你再去翻本地代码,只会越查越远。

所以第一步不查代码,先把版本对上。

本地正常、线上报错。本轮不修改任何代码。

依据项目文档和发布记录,帮我确认三件事:本地当前是哪个代码版本;线上正在运行的发布产物是什么时间、由哪一份代码构建的;如果这个项目在服务器上跑了不止一个副本,是不是每一个都已经更新。

需要我去服务器上看东西时,一次只给我一条只读命令,并说清这条命令的作用和正常应该看到的结果。

顺手再让它加一件事,以后能替你省很多回:让程序把版本号和构建时间显示在启动日志里,或者做一个只能看、不能改的状态页面。

下次再出问题,你不用登服务器翻文件,看一眼就知道用户访问到的是哪一版。

三、第二步:把两边的运行条件逐项对照

版本对上了还是报错,那就轮到后两个问题了:它跑在什么地方,它面对的是什么数据。

要对的东西不少。我按由内到外的顺序给你排好,一共五样,你跟着走就不会乱。

第一样,配置。

配置离代码最近,出问题也最多,从它开始。

对配置有个特别容易犯的错:看见两边名字一样,就认定是一回事。

你要核对的其实是四件事:这个配置项叫什么、在不在、由哪儿提供、属于哪个环境。

注意,只对名字,不对值。 数据库密码、密钥、令牌、完整的连接地址,一个都不要复制给 AI。

举个最常见的。本地有一个支付的测试密钥,服务器上压根没配生产密钥。

代码可能一个字都没写错。它只是到了真要发起支付那一刻,手上没有身份。

而配置这一样里,藏着我见过最能让人怀疑人生的一个坑。

它不是「配错了」,是「什么时候读进去的」两边不一样。

后端读配置,一般是程序运行的时候去读。所以你改完服务器上的配置,重启一下,新值就生效了,符合你的直觉。

前端不一定。不少前端工具是在打包那一刻,就把接口地址直接写死进最终文件里的。

于是就有了这个场面。

你在服务器上把接口地址改成了正式域名,文件里白纸黑字写着,重启也重启过了。

用户打开页面,请求还是发往 localhost。

为什么?那个地址早在打包的时候就焊死在文件里了。服务器上你改的那份配置,前端从头到尾没去读过它。

这种情况下,你在本地怎么改业务逻辑都改不出来,因为逻辑真没错,错的是那个值什么时候被塞进去的。

所以对配置的时候要多问一句:这个值是打包的时候写进去的,还是程序启动以后才读的。

这一句,能省你一整天。

还有一种跟它长得很像,但性质完全不同,就是我开头说的那种真得动代码的情况。

配置压根就没在配置里,被直接写死在代码里了。

这在 vibe coding 里特别常见。你让 AI 先跑通,它图省事,就把接口地址、密钥、文件路径直接塞进了代码。

本地当然没问题,那些值本来就是照着你这台电脑写的。

上线以后,它照着代码里的字面值去连你自己电脑上的地址、去找一个服务器上并不存在的目录。

这一种就得改代码了:把写死的值挪出来,改成从配置里读。

但注意顺序。它也是你比对配置的时候发现的,发现「这个项目压根没有这一项配置」,才回头去代码里找到它被写死在哪儿。不是让 AI 盲改代码,改着改着碰巧撞上的。

第二样,运行环境和依赖。

两边的系统是什么,语言运行时是哪个版本,构建和启动用的是不是同一套命令。

还有依赖,重点看一件事:装上去的那些,是不是来自同一份锁定文件。

锁定文件就是记着这个项目到底装了哪些确切版本的那份清单。有它,两边装出来的东西才一样;没有它,同一条安装命令,今天和明天装出来的都可能不一样。

第三样,路径和权限。

程序从哪个目录启动,要读的那些文件在不在。

上传目录、缓存目录、日志目录,写不写得进去。运行程序的那个账号,有没有权限碰这些文件和端口。

权限这一样最容易被跳过,因为它翻车的时候特别安静。

用户传个头像,页面转一圈,什么提示都没有,你在页面上也看不出任何报错。服务器上那个目录,运行账号根本写不进去。

第四样,外部服务和网络。

先看通不通:服务器能不能连上数据库、对象存储、短信、支付这些你用到的服务。

再看是不是同一套:对方那边分测试和生产两套地址和密钥,你线上用的是哪一套。

最后看有没有被拦:域名、回调地址、证书、地区限制、IP 白名单,这些都能让一个本地好好的调用,在线上直接被挡回来。

第五样,数据。

先看结构:生产数据库的表结构,有没有更新到程序需要的那一版。正式的基础数据齐不齐。

再看内容。这一条你在本地基本碰不到:真实数据是脏的。

你本地就十条,格式工工整整。线上有空值,有三年前的老格式,有长得离谱的内容。

同一段代码,喂给它不同的数据,结果当然不一样。

五样过完你会发现,从头到尾没一样是靠盯着本地代码能看出来的。全都得两边摆在一起比。

好在也不用你一条条记,更不用自己挨个比。让 AI 生成一份对照清单,你照着填就行。

本地正常、生产报错。本轮不修改代码,也不连接或变更生产服务器。

依据故障时间、报错内容和项目文档,生成一份本地与生产环境的只读对照清单,逐项比较:程序版本与发布产物、操作系统与语言运行时、依赖锁定结果、启动命令、配置项、文件路径与权限、端口与网络可达性、数据库结构、真实数据差异、第三方服务。

配置项只核对名称、是否存在、来源和所属环境,不要向我索取密码、令牌、私钥和完整的生产连接信息。

每一项写明:本地看哪里、生产看哪里、正常结果是什么、对不上会表现成什么故障。

需要服务器信息时,每次只给一条只读命令,附上作用、预期输出,以及哪些内容我要先抹掉再发给你。

把已确认的差异、还缺的证据、下一步要查什么,分开记录。

在拿到差异证据之前,不要修改业务代码。报错长得像代码缺陷,不构成修改依据。

清单拉出来,你可能会想:那是不是得把服务器弄得跟我电脑一模一样?

不用,也做不到。

生产服务器本来就该用不同的系统、不同的硬件。真正需要对上的,只有程序会碰到的那几样。

运行时版本,得在项目支持的范围里。

配置项,要齐,而且要指向正确的那个环境。

线上跑的产物,要来自你确认过的那份代码。

数据库结构,要跟程序的版本对得上。

这几样之外的差异,看见了也别管。跟程序行为无关的东西,你去对齐它只是浪费时间。

四、第三步:先在隔离环境里复现,再动手修

清单填完,差异找到了。这时候最危险的一刻来了:你会特别想直接上手,去服务器上把它抹平。

打住。

先说最常见的那种:你发现服务器少了一个配置。

别急着补上去。

补之前得弄清楚:这个配置本来该从哪儿来、格式长什么样、影响的是哪几个功能。补错了,你只是把一个报错换成了另一个。

比这更要命的是另一种:你发现两边运行时版本不一致,然后想上服务器直接升一下。

千万别。

生产环境上跑着真实用户。你在上面做实验,翻车的是他们。

正确的做法是把差异搬回来:在本地或者一个隔离的测试环境里,换成生产在用的那个版本,看同一个错误能不能稳定地重新出现。

如果问题是被生产数据触发的,就只取能复现问题的最小一份数据,真实用户信息先删掉或者换掉。

生产密码、生产密钥和完整数据库,不要往本地带。

复现这一步,值得你多花点耐心。

改之前,先让错误在隔离环境里出现一次;改之后,再用同一套条件确认它消失了。

这一前一后,才能证明你修的是真正的原因,而不是碰巧躲过去了。

基于已确认的本地与生产差异,在本地或隔离测试环境里设计复现方法,本轮不连接生产环境做任何修改。

写明需要还原哪些条件:程序版本、语言运行时、依赖、配置结构、权限、数据库结构和外部服务。全程只用测试密钥;数据只用脱敏后的最小集合,也就是把真实用户信息删掉或替换掉,只留够复现问题的那一点。

先证明修改前能稳定复现同一个错误,再给出只针对这个原因的修复方案、影响范围,以及改完要回头验证哪些功能。

本轮只交付复现方法和修复方案,我确认后再动代码。

有条件的话,顺手把运行环境固定下来:语言运行时锁定版本,依赖锁定文件保留好,构建和启动用同一套命令。

你可能听人提过容器,就是把系统、运行时、依赖一起打成一个固定的壳子,搬到哪儿跑都一样。它确实能替你锁住其中一部分条件。

但它管不了全部。生产数据库、权限设置、第三方平台那边的规则,都在这个壳子外面,它一样都复制不过来。

目标从来不是让你的电脑长得像服务器。

而是让出问题的那几个关键条件,能被稳定地还原出来。

五、第四步:发布同一份验证过的产物

在开发环境里修好了,别登上生产服务器再手工改一遍。

手工改的那一下,不在任何一份代码里。下次重新发布,它就没了,问题原样回来。

正确的做法,是重新打一个带明确版本号的包。

这时候前面那个坑又要来找你了,就是配置什么时候被读进去那件事。

你的项目要是运行时才读配置,那就好办:测试和生产尽量用同一份产物,只换各自的环境配置。

要是那种打包时就把配置写进文件的,就必须拿正式配置单独打一次生产的包,还要记下这次用的是哪份代码、哪套配置。

也正因为这样,这类项目发布前一定要多做一个动作:翻一眼包里的接口地址和环境标识,确认你没把本地开发用的那个包直接传上去。

不管走哪一种,你都得能说清这一句:线上这份东西,来自哪份代码,什么时候拿到了哪套配置。

发上去以后,先看版本号和构建时间,确认用户访问到的确实是新的那一版。

再把原来失败的那个操作,亲手重做一遍。

顺带看看相关的正常功能、错误日志和第三方返回的结果,别修好一个、坏了三个。

修复已在隔离环境验证通过,现在只产出发布方案,本轮不对生产环境执行任何操作。

产出带明确版本号和构建时间的发布产物。配置在运行时读取的项目,测试与生产尽量共用同一份产物,只用各自的环境配置区分;配置在构建时写入产物的项目,单独记录生产构建所用的配置来源,并在发布前核对产物里的接口地址和环境标识。

给出发布前检查、发布步骤和发布后验收。验收项必须包含:实际运行版本、原故障是否消失、相关正常功能、日志、数据库和第三方服务的返回结果。

如果这次故障是配置缺失引起的,补上启动阶段的必需配置校验,以及一份不含任何机密值的配置示例。

本轮只交付发布方案,我确认后再执行。

上面那段话最后要的两样东西,值得我单独说一句,因为它们是这次故障能给你留下的长期收益。

一样是启动时的配置检查。它管的是:缺了哪个配置,程序一启动就直说,而不是等用户点到一半,甩出来一个谁也看不懂的错误。

另一样是配置示例。就是一份不含任何密码密钥的清单,只写这个项目需要哪些配置、各自什么格式。

有了它,以后你再部署一个新环境,AI 拿着这份清单就能先把缺的挑出来,不用等报错了才发现。

六、最后

本地一切正常,上线就报错,这事一点都不神秘。

代码只是程序能不能跑起来的一部分。线上到底跑的哪一版、运行环境、配置、权限、外部服务、真实数据,缺哪一样都能让结果变个样。

所以别再围着本地代码反复改了。四步走完它。

第一步,先确认线上跑的是哪一版。

第二步,把两边的条件按由内到外对一遍:配置、运行环境和依赖、路径权限、外部服务、数据。让 AI 出清单,你照着填。

第三步,找到差异以后,在隔离环境里复现,确认修的是真原因。

第四步,把同一份验证过的产物发布出去,再逐项验收。

四步听着挺专业,我知道你在想什么:这里面好多东西我压根不会。

所以我把分工给你说清楚。这一圈下来,真正要你自己动手的只有三件事。

一是把现象说准。 什么时候开始的、用户做了什么、你上线前动过什么。这个只有你知道,AI 编不出来。

二是当它的手。 它给你一条只读命令,你去执行,把结果抹掉敏感信息再贴回去。你不用懂那条命令什么意思,因为你已经要求它把作用和正常结果都写清楚了。

三是最后亲手点一遍。 把原来失败的那个操作重做一次,再顺着它给的清单点一遍相关功能。

剩下的全是它的活:该查哪一项、命令怎么写、正常结果长什么样、差异怎么解释、方案怎么定。

从头到尾,没有一步要求你看懂代码,也没有一步要求你会运维。

你管不了它怎么改,但你管得住它按什么顺序查。

走完这一圈,你解决的也不只是眼前这次报错。版本标识、配置示例、依赖锁定和启动检查都留下来了,下一次上线会好查得多。

要是 AI 只甩给你一句「可能是环境问题」,别接受。让它说清楚具体比哪一项、怎么只读检查、正常结果长什么样。

它讲的你要是看不懂,就让它用大白话重讲一遍。

在拿到能验证的差异之前,不要同意它继续改代码。

说到底就一句话:同一份东西、同一个地方、同一批数据。三样都对上了,本地和线上才不会各说各话。

该内容为@openbeetles原创内容,未经授权,禁止转载,违者必究!