9月6日,程序员社区里流传着Simon Willison在一条评论里写下的场景:技术债积重难返,管理层拍板推倒重来,结果不是解脱,而是同时背上两套系统的重担。这条评论被大量转发,不是因为观点多新颖,而是它精准复刻了一个几乎每个工程团队都亲历过的失败剧本。
这段话其实脱胎于另一位作者Zach Kehs的文章标题——《There's No Limit to How Bad Code Can Get》。软件不像建筑物,没有承重墙压垮的物理极限,团队可以无限叠加补丁、抽象层和临时变通,却不会触发一次性的系统性崩溃。这正是"重写"诱惑经久不衰的根源:眼前的代码烂得看不出头,团队自然想要一张白纸重新开始。
重写为什么总是走向"双系统陷阱"
Willison描述的失败路径有清晰的六个阶段:宣布旧系统不可救药,组建新团队从零开始;旧系统因为仍在支撑核心业务,还得持续加功能、继续腐烂;负责旧系统的人知道自己迟早被替换,干脆能应付就应付;新团队起初斗志高昂,但很快发现自己根本不了解要替换的东西的全部行为边界——毕竟如果旧系统文档齐全、测试充分,一开始就不需要被替换;拖了数月甚至数年交不出成果后,压力逼着新系统仓促上线,往往只覆盖一小部分场景;最终公司手里同时握着两套系统——没人愿意碰的旧系统,和八成是闲置代码的新系统。
如果运气够好,公司还愿意让新系统继续追赶;运气差一点,旧系统顽固地持续运转得越久,"优先级变了"这句话出现的概率就越高,重写工程被搁置,公司从一套系统变成了两套系统。
解药:先测试,再重构,而不是推倒重来
Willison自己给出的建议很朴素——给旧系统尽可能补上自动化测试,再尝试有针对性的重构,让它逐步逼近理想形态,而不是被一场"绿地重写"的诱惑带走。他同时点名推荐了Will Larson那篇《Migrations: the sole scalable fix to tech debt》,把这条路径从一句原则,变成了一套可执行的工程流程:先定义目标状态,再建立一条迁移接缝,按切片逐步搬迁,新旧路径并行跑通验证,确认无误后切换流量和所有权,最后才删除遗留系统。
代码不会因债务倒塌,压垮团队的从来是人心先散。
Larson的框架填上了原文没说透的中间地带:重构解决不了语言过时、数据模型陈旧、服务边界不合理这类根本性约束;重写又对大型业务关键系统风险太高;真正能扛住规模的,是这条又慢又麻烦的迁移路径。这也回应了一个常被忽略的前提——重写并非绝对禁忌,它只在领域边界清晰、需求已被充分理解、有旧系统资深工程师坐镇、组织愿意长期支持的条件下,才值得一试。多数失败案例,恰恰是把这几个前提都省略了。
谁该盯着这条经验法则
- 结论.如果重写迟迟不能在可衡量的周期里替换掉某个真实生产依赖,它大概率不是在消灭第一个技术债项目,而是在制造第二个。
- 风险.压垮重写项目的,常常不是技术难度,而是预算周期或管理层换届带来的"优先级变了"——这类政治因素比代码复杂度更难提前评估。
对工程管理者和架构师来说,批准一次重写之前,先问自己旧系统有没有测试覆盖、有没有懂它全部行为的人还在团队里。对一线工程师而言,被分到"即将被淘汰"的旧系统维护组,是最容易被磨掉斗志的位置——这也是为什么旧系统在重写期间反而债务越滚越多。至于业务方,数月甚至数年拿不到新功能的机会成本,才是这场赌注里最贵的部分。
