技术债务:复杂度的复利

45 阅读 1665 字 · 约 6 分钟

每个项目都有技术债。不是因为决策做错了——在当时的条件下,那个决策很可能就是最优的。技术债之所以是债,不是因为当时选错了,是因为后来条件变了:信息多了、需求长了、团队换了,当初合理的决策在新条件下不再合理了。这笔"不再合理"就是债,而且它有利息。

技术债不是"代码写得烂"。代码写得烂是质量问题,和技术债不是一回事。技术债是一个比喻:你当时为了快,选了一条捷径,这条捷径在后来产生了额外的维护成本——就像借了钱要还利息。Ward Cunningham 最初提出这个概念时,打的比方就是财务债务:你借债买房,提前住上了,代价是以后每个月还利息。技术债也一样:你为了先跑起来,硬编码了配置、跳过了测试、省掉了校验,代价是以后每次改动都多一层风险和摩擦。关键区别在于:有的债是主动背的,有的债是不知道自己背了。

利息怎么产生

技术债的利息,不是一次性付清的,是每次改动都在付。

举个例子:一个系统上线时,三个环境的数据库连接串硬编码在代码里。当时只有三个环境,改起来不费事——这笔债在当时几乎没利息。后来环境变成五个、十个,每次加环境都要改代码、重新打包、重新部署。每次改动多花半天,半年下来十几个环境,光改连接串就花了一个工程师一周。而且利息在"复利":环境越多,改错的风险越大,加回归测试的时间越长,一笔小债滚成了一笔大债。

另一种常见的技术债:没有测试。上线快,但没有测试意味着每次改动都不敢动——不知道改了这里会不会影响别处。于是改动变慢了,不是因为代码难,是因为没有安全网。改动越慢,需求积压越多;需求积压越多,赶工时又欠下新的债——这是"复利"。

不是所有债都该还

这是最重要的一点:技术债和财务债一样,有的债该还,有的债该一直背着。

该还的债:利息高、还在持续产生成本的。比如那个硬编码连接串,每次加环境都在付利息,还掉它(改成配置中心)的成本远低于不还。判断标准很简单:半年内你为这笔债多花的时间,超过了还债的时间,就该还。

该背的债:利息低、或还债成本太高的。一个早期快速验证的系统,用单文件 SQLite 而不搭数据库集群——这是一笔债,但利息极低,因为 SQLite 在单机场景下完全够用。还掉它(迁移到 PostgreSQL 集群)的成本远高于利息,这笔债就该背着。

还有一种债叫战略性技术债:你明确知道现在欠着,但故意不还,因为现在的优先级在业务验证上。等业务验证通过了,系统要扩了,再回来还。这种债不可怕——可怕的是不知道自己在欠债。

怎么处理

技术债最危险的地方是不可见:财务债有人盯着,每月对账;技术债没人盯,它藏在代码里、配置里、架构里,不主动看就看不到。等到改动越来越慢、bug 越来越多,才发现债已经还不起了——利息滚到了本金的好几倍。

但"专门排一个迭代还技术债"本身就是一个信号:说明债已经积到正常开发兜不住了。健康的做法不是集中还债,是每次碰代码时顺手还一点——加功能时发现旁边的老代码该清理了,就一起改了。童子军原则:离开时比来时干净。

架构级的债——单体拆微服务、单机改分布式——不是顺手能还的,要专门安排时间。前提是你确认不还不行了:它在持续拖慢开发,或者下一个需求来时会变成瓶颈。如果只是"不好看"但没在拖慢什么,就不该专门花时间——你的时间该花在产出业务价值上。

谁来管?没有专职管理员,也不需要。债散落在代码的每个角落,一个人不可能比写代码的人更清楚哪里有问题。实际运作靠三层:写代码的人在自己碰的区域顺手还;技术负责人盯着结构性大债,判断哪些到了该还的时候;团队有一个共识——碰到老代码时留得比来时干净。谁做了有代价的决策,谁就记一条 ADR——不是为了建一本账本,是为了三个月后翻出来知道这笔债该不该还。

最怕的是有人管但只管记不管还——清单越列越长,每条都"待处理",最后变成一本没人翻的废纸。记债是为了还债,不是为了自我安慰。

收尾

技术债不是该不该欠的问题——不欠债的项目不存在。问题是:你知不知道自己欠了多少、利息多大、哪些该还哪些该背。知道,你就能选择什么时候还、什么时候背;不知道,利息就在暗处一直滚,滚到你改不动为止。