关于「重写」的诱惑

作者:泡面星球 发布时间: 2026-06-19 阅读量:65 评论数:0

我这辈子提过五次重写,成功两次,失败两次,还有一次到今天也说不清算成功还是失败。这篇就讲这几件事,每件都有具体的数字和后果。

第一次:赢了,但赢得很险

那是一个订单状态机,最早是用一堆 if-else 写的,我接手时有 1400 行,17 个状态,我数出来 63 条状态转移分支。改一个需求平均要动 5 个地方,漏改一处线上就出脏数据。

我花了三周重写成显式的状态机表:一张二维表定义合法转移,加一个执行器。新代码 380 行。

关键在于我做了两件事保命:

  • 新旧两套代码并行跑了两周,每笔订单都算两遍,结果不一致就打日志但仍以旧逻辑为准。两周里抓到 47 次不一致,其中 41 次是我的新代码错了。
  • 那 63 条分支我全部写了对照测试,是从生产日志里抽的真实数据。

如果没有影子运行,我大概率会在上线第一天炸掉。

第二次:输了,输得很惨

一个内部的数据同步服务,Python 写的,我坚信应该换成静态类型的语言,理由是「运行时错误太多」。

我立了个项,估两个月。实际做了五个月,中途上线了两次又回滚两次。最后砍掉了。

失败的原因我事后想得很清楚:我重写的是形式,不是本质。那个服务真正的复杂度在于它对接了 9 个上游数据源,每个源都有自己的怪脾气——有一个源会在月末返回多余的空行,有一个源的时间戳是本地时区但不带标记,有一个源在数据缺失时返回字符串 NULL 而不是空。

这些知识全部散落在原代码的各种奇怪判断里,没有文档,没有注释。我重写的时候把那些「看起来很脏」的分支当垃圾清掉了,结果每清掉一个就在生产上还回来一个 bug。

老代码里那些莫名其妙的分支,往往是用事故换来的。你看不懂它,不代表它没有理由。

第三次:说不清的一次

一个前端管理后台,从旧框架迁到新框架。做了四个月,功能一比一还原。上线后用户体感几乎没变化,性能提升大概 15%。

从业务角度看,这四个月产出为零。从工程角度看,之前每次招人都要花两周教旧框架,之后新人第一天就能提 PR,而且我们两年里招了六个人。

我现在的看法是这次重写是对的,但当时我没法证明它对,只能凭直觉硬顶。这种事很难有标准答案。

我现在的四个自检问题

被坑过之后,每次想重写我都先回答这四个问题,答不上来就不动:

  1. 我能列出老代码里至少 10 个我看不懂的地方吗?如果一个都没有,说明我根本没读懂它。如果列出来了,我必须逐个搞清楚为什么存在。
  2. 有没有办法把重写切成能独立上线的小块?能切就切。不能切的重写,风险呈指数级上升。
  3. 能不能做影子运行或双写对比?没有对比手段,就相当于闭着眼开车。
  4. 如果重写失败,回滚代价是什么?数据结构变了的话,往往回不去。这时候需要格外谨慎。

一个更根本的观察

我发现自己想重写的冲动,绝大多数时候来自于「读代码很痛苦」这个感受,而不是「这段代码有具体的问题」。

这两者的区别非常大。读代码痛苦是我的问题,代码有问题才是代码的问题。分辨的方法是逼自己写下三个具体的、可度量的痛点:比如「改一个字段需要动 6 个文件」「新人上手平均 3 周」「这个模块过去半年出了 8 次线上问题」。

写得出来,重写有底气。写不出来,那我大概只是想找个理由重新写一遍我自己喜欢的风格。

顺带说,第二次失败之后我做了什么?我花两周给那个 Python 服务补了 130 个测试和一份 3000 字的《上游数据怪癖清单》。之后两年它稳定运行,我们只改过 4 次。有时候最好的重写就是不重写。

评论