← 回到建造日记
翻车

🚀 离上线只差一步,全是没人告诉你的麻烦 —— Babo 的「最后一公里」踩坑记

上一篇把架构拆得漂漂亮亮,我以为难的部分过去了。结果从「demo 能跑」到「敢给真人用」之间的最后一公里,全是不性感、写不进宣传语、却一件都绕不过去的事:备案是一堵你没法用代码翻过去的墙;一封发不出去的验证码邮件,照出了产品藏着的「欠债」;我刚做好的「入学测试」被自己推翻、改成了「跟 Babo 交朋友」;最后还给整个产品换了张脸——不是重写,是换一层皮。顺便,Cappy 正式改名叫 Babo 了。这一篇记的就是这些弯腰捡鸡毛的时刻。

上一篇我把 Cappy 的架构拆得挺漂亮 —— 听想说看、身份模式上下文、单一事实源。写完那篇我有种错觉:难的部分过去了,剩下就是上线。

结果「上线」这两个字,是我整个项目里最朴素、也最磨人的一段。从「demo 能跑」到「敢给真人用」之间,隔着一条最后一公里,里面全是不性感、写不进宣传语、却一件都绕不过去的事。

(顺便交代一句:这段时间产品正式改名叫 Babo(波波)了 —— 域名定在 readwithbabo,那只一直占位的 🎩 帽子,也终于换成了一只线描小怪兽。下面就都叫它 Babo。)

这一篇,不讲架构,讲这一公里上我弯腰捡的几地鸡毛


一、备案:一堵你没法用代码翻过去的墙

要在国内正经上线,第一关是 ICP 备案。这是我第一次撞上「这事没法靠聪明绕过去」的墙 —— 它不是技术问题,是流程问题,你只能照着规矩一步步来。

中间全是新手不知道的暗坑:

  • 域名实名通过当天不能马上提交备案,得等满一个自然日(T+1),急着交直接被系统打回;
  • 备案要给服务器申请一个「备案服务号」,按量计费的云主机可能根本申请不到,得先转成包月;
  • 网站名称里不能带「教育」「培训」这类词 —— 一带就触发「前置审批」,要办学许可证那一套;
  • 还有「两个域名(.com / .cn)到底要不要都备」这种,问谁谁语焉不详。

有意思的是最后那条红线:填网站名的时候,系统逼着我不能把自己叫成「教育」。而这恰恰是我从第一天就定的产品调性 —— Babo 是陪孩子的朋友,不是补习班。我把网站内容老老实实选了「其他」,名字叫「菠萝绘本伴读」。

🚧 有些墙不是用来翻的,是用来排队过的。 工程师的本能是「想个巧办法绕过去」,但备案这种事,唯一的解法是搞清楚规则 + 有耐心。而且它还顺手帮我把「不是学科培训」这条红线,焊得更死了。

现在材料已经提交,在工信部审核里排着,就等给负责人手机发的那条短信核验。这一步,急也没用。


二、一封发不出去的邮件,照出了产品的「欠债」

接下来是邮箱登录 —— 家长输邮箱、收验证码、进来,不要密码。逻辑早写好了,这次接上真实邮箱去发码。

我想稳妥点,先单独写了个脚本直连邮箱服务器测一封。结果被拒了,报 550。我钻进去看,原因很蠢:我给发件人加了个中文显示名「Babo 绘本伴读」,但没按邮件规范给中文编码,被服务器的严格校验挡了。

这本来是我测试脚本自己的 bug。但顺着它扒下去,我发现了两件更该修的事:

  1. app 真正发的那封验证码邮件,标题和正文里还写着旧名「Cappy」 —— 改名改了界面,却漏了这封藏在后台、平时根本看不到的邮件;
  2. 发件人这种带中文的字段,必须正确编码,不然换个严格点的邮箱服务商就发不出去。

于是我把邮件里的名字改成走配置(以后再改名,一处改、全网对)、把编码补上。

📮 demo 不发真邮件,所以「欠债」一直藏着。 一旦接上真用户的真实流程,那些 demo 阶段你能假装看不见的东西 —— 旧名字、没编码的字段、发不出去的邮件 —— 会一个不落地冒出来。上线,本质上是产品第一次照镜子。


三、我把刚做好的「入学测试」,自己推翻了

这是这段里我最满意的一个决定 —— 因为它是否定自己

新用户第一次进来,得让 Babo 先认识这个孩子、摸一下他的英语水平。我之前老老实实做了一套能力测评:一道道固定的题、由易到难、答完给个评级。逻辑严丝合缝,我还挺得意。

然后我自己点了一遍。那一下我就知道不对了 —— 它太像「入学考试」了。 一个 5 岁的孩子第一次见到 Babo,劈头盖脸一堆测试题,紧张都来不及,谈何「交朋友」?这恰恰是我发誓不做的东西。

所以我把它推倒重做,从「考试」改成「跟 Babo 聊天」:

旧:入学测试 😬 一道道固定题,由易到难 答完打个分、给个评级 → 孩子第一次见面就被「考」 推翻重做 新:跟 Babo 交朋友 🪐 自然聊天,Babo 说英文 + 中文字幕 不会说能切中文,不卡死孩子 → AI 看整段对话悄悄判水平,不打分

Babo 像个新朋友一样跟孩子聊,只说英文、配中文字幕;孩子英语卡壳了能一键切中文,不会被一道答不上的题卡死。聊的过程里,AI 看整段对话判断他的水平 —— 家长那头完全看不到「分数」,体验就是「Babo 认识了你家宝贝」。

但体验对了,技术的麻烦才刚开始。 这一改捅出两个挺缠人的 bug:

  • 从「跟家长聊」切到「跟孩子聊」时,实时语音会断掉 —— 切场景的瞬间没接好,得专门修;
  • AI 接孩子话的时候,会把「英文+中文字幕」那个两行格式给丢了,直接回一段纯英文。后来我想明白:我喂回给它的对话历史里,它自己之前说的话没带格式,它就「学」着不带了。解法是把它说过的话按格式原样喂回去,等于不断提醒它「你一直是这么说话的」,格式就锁住了。

🧩 对的产品方向,常常是把自己刚做好的东西推翻。 更扎心的是:方向对了不等于轻松了 —— 体验顺了,底下的技术细节才开始一个个跳出来要你还。


四、给整个产品换张脸 —— 不是重写,是「换一层皮」

最后一件大事:把整个产品的样子,统一换成一套暖夜手绘星球的绘本风(就是你现在在 Babo 里看到的:夜空星点、奶油色卡片、墨线小怪兽)。

听起来是「美化」,其实最难的是工程。产品有十几个页面,麻烦在于它们不是一种东西:

  • 有些页面内容是写死的,换皮直接改;
  • 但像家长看板、四步精读这种,内容是程序实时画出来的 —— 如果为了换皮去重写这部分逻辑,等于动手术,几周起步还容易出 bug。

我没那么干。我的办法是:抽一套共享的「设计皮」,一处改、全站变;对那些实时渲染的页面,加一层**「变量覆盖层」整体调暗** —— 一行渲染逻辑都不碰,只是把它用的颜色变量换掉。

一套共享「设计皮」 颜色 / 字体 / 手绘描边 / 小怪兽 写死的页面 直接套皮 实时渲染的页面(看板/精读) 加「变量覆盖层」整体变暗,不碰逻辑 十几个页面 一致变皮

中间还有几个只有做了才知道的小坑:未登录能看的公开页加载不了脚本,只能把图标做成自包含的;新做的小怪兽头像,id 撞上了一个共享图标的名字,得改名错开。一个个都不大,但不踩一遍不知道。

🎨 换皮的功夫,不在「画得好看」,在「怎么改得起、不返工」。 把「不变的样式」抽成一份共享的、让程序自己画的页面靠「换变量」整体变 —— 同样是上线前那句老话:先想清楚怎么改,再动手改。


写在最后:架构是骨头,这些是把它推上线的力气

回头看这一段,没有一件事是性感的。备案是排队,邮件是修个编码 bug,onboarding 是把自己推翻,换皮是工程上的精打细算 —— 没有一条能写进宣传语。

但产品就是这样,从「能跑的 demo」长成「敢给真人用的东西」的。上一篇我说架构是把系统想清楚;这一篇全是把想清楚的东西,真正推到一个 5 岁孩子面前,路上需要弯腰捡起来的一地鸡毛。

我越来越觉得,做产品到后面,拼的不是想法 —— 想法早有了。拼的是愿不愿意一件件去收拾这些不性感的小事,直到它真的能稳稳地、合规地、温柔地,出现在一个孩子的屏幕上。

下一步就一句话:等备案过,正式上线,让 Babo 真的去陪第一个孩子读他自己的绘本。