技术债这个比喻之所以好用,是因为它精准地抓住了两个特征。第一,有些债是主动借的,为了快速交付一个功能,明知某处实现不够好但先这样上线,图的是时间。第二,债是有利息的,越拖还的成本越高,久到一定程度利息超过本金,整个模块就没人敢碰了。我在蜂控重构情报中心那段时间,对这一点感受特别深,迭代周期能砍掉一半,前提是先把积压了好几个迭代的设计债还掉。
但技术债治理最大的误区,是把它等同于代码重构或者推倒重写。很多团队一提技术债就是 代码太烂了我们重写吧,这种冲动几乎总是错的。真正有效的技术债治理,是一套识别、记录、量化、有序偿还的持续工程,核心是排序和节奏,不是重写。
先认清债从哪来,不是所有债都该内疚
图:技术债的四种来源及对应处理方式——不是所有债都该内疚
技术债的来源不止一种,搞清楚来源能帮你判断哪些债该还、哪些债其实借得合理。
最常见的来源是时间压力。赶一个 deadline,明知某个抽象该抽出来但来不及,先硬编码上线,等下一版再优化。这种主动借债是合理的,前提是你清楚自己借了,并且打算还。可怕的不是借,是借了不记账,过几个迭代这段代码的来龙去脉谁都不记得,它就从主动债变成了隐形债。
第二种是认知局限。当时写的时候不知道有更好的方案,用的是自己掌握的最优解,但后来技术进步了或者团队水平上来了,回头看那套实现是次优的。这种债是被动欠下的,谁都没错,但它确实存在。
第三种是业务演进带来的自然老化。系统当初是为 A 业务设计的,现在 B 业务 C 业务都挂上来了,原来的架构跟不上,到处是缝缝补补。这种债不是谁的锅,是系统成长必须经历的。第四种是人员流动,原作者离开了,新接手的人理解断层,不敢动也懒得动,代码逐渐僵化。
认清这几种来源的意义在于,主动借的债要记账并安排偿还计划,认知局限和自然老化的债要靠持续重构消化,而人员流动造成的债则提醒你,知识沉淀和文档比代码本身更重要。
不记台账的债等于没在还
技术债治理第一步不是动手改代码,是建一个台账。听起来像官僚主义,但这是最关键的一步。
为什么这么重要?因为技术债最大的敌人是遗忘。一个 TODO 注释、一段 hack 代码,当下记得是债,过两个月需求一茬接一茬,谁还记得这里欠着。等它哪天爆出来,已经滚成了更大的问题。台账的作用就是把所有债显性地记录下来,让它无法被遗忘。
台账里每条记录至少要包含几项。位置,哪个文件哪个模块。描述,欠了什么债,为什么会欠。影响,这个债如果不还可能造成什么后果,是会拖慢后续迭代、是可能引发故障、还是单纯难看。预估工时,还掉它大概需要多少人日。
有了台账,下一步是定期评审。每隔一段时间把台账过一遍,结合当前的业务重点和技术规划,重新排定优先级。债的影响会随业务变化,半年前无关紧要的债可能因为新业务挂上来变成了高优,台账评审就是让优先级保持新鲜。
量化,影响和成本两个维度
图:技术债优先级矩阵——以影响和修复成本两个维度交叉排序决定偿还顺序
排优先级要靠量化。技术债的量化不看代码行数这种表面指标,看的是两个真正有意义的维度,影响和修复成本。
影响维度要回答的是,这个债如果不还,会造成什么后果。最严重的是那些有故障或资损风险的债,比如一个没有并发保护的关键路径,或者一个已知有边界条件的金额计算。这类债一旦触发就是线上事故,优先级最高。其次是拖慢迭代的债,那种改一个功能要牵动一片代码、动一处怕崩别处的设计债,它不直接引发故障,但持续侵蚀团队的开发效率。最低的是纯粹难看但不影响功能和效率的债,比如命名不规范、注释缺失,这种可以无限往后排。
修复成本维度评估的是还掉这个债需要多少投入。一个改改命名几小时能搞定的,和一个需要拆解整个模块耗时数周的,成本天差地别。
把两个维度交叉就能排出偿还顺序。高影响低成本的优先还,这是性价比最高的一类,花小钱解决大隐患,应该立刻排进迭代。高影响高成本的要专门立项,因为它需要专门的时间和规划。低影响的则让它继续待在台账里,等有机会主义重构时顺手处理。
偿还的几种姿势,以及为什么大重写是最后的下策
图:偿还技术债的三种姿势——从日常顺手优化到大重写,风险逐级升高,大重写是最后的下策
债怎么还,根据规模和风险有三种不同的做法。
最日常、成本最低的是机会主义重构,也叫童子军原则。每次因为业务需求去改某处代码时,顺手把周边的问题一起收拾了,重命名一个变量、提取一个冗长的函数、补一个缺失的测试。不需要专门排期,不需要大动干戈,但积少成多,代码会慢慢变干净。这种做法的风险极低,因为你本来就要改这块代码,顺手改进是顺手验证。
第二种是专项重构。当某块债影响已经大到机会主义重构消化不掉时,需要集中一段时间专门处理,比如重构一个服务、拆分一个臃肿的模块、替换一个老旧的中间件。这种重构风险中等,必须要有测试网兜底,先补测试再动结构,保证重构前后外部行为不变。
代码烂到一定程度,每个程序员都会冒出一个念头,推倒重来吧。这个念头要警惕。Joel Spolsky 早年那篇著名的文章警告过大重写是战略错误,这么多年过去这个判断依然成立。大重写的实际成本几乎总是远超预期,周期一拉长,期间新功能开发停滞,业务方会不断施压。更致命的是,新代码未必比旧代码好,而且重写过程中会丢失旧代码里沉淀的业务知识,那些用惨痛教训换来的边界处理和兼容逻辑。绝大多数技术债通过渐进重构都能偿还,大重写只在系统已经完全无法维护、且重构成本确实高于重写时才考虑。
还债和做需求抢时间,怎么平衡
技术债治理落到执行层面,最现实的问题是它和业务需求在争同一批人的时间。业务方永远认为自己的需求最紧急,技术债永远排不上号。
靠 有空再还 是行不通的,因为永远不会有空。唯一有效的办法是制度性的时间预留。每个迭代固定拨出一个比例的时间专门还债,我们团队的经验是 15% 到 25% 比较合适。这个比例写进迭代的容量规划,像固定支出一样雷打不动,业务需求再紧也不能挤占。
这看似减少了业务需求的开发时间,实则是保证迭代速度不衰减。技术债不还,代码越来越臃肿,每改一个需求都要趟过一片泥潭,开发效率会持续下滑。固定时间还债,本质是在给未来的开发速度做投资。
还旧债的同时防新债
光还旧债不够,还得控制新债的产生,否则前面还后面欠,永远还不完。防新债靠的是几道关卡。
代码规范和代码评审是第一道,统一标准加上同行把关,能拦住大部分低质量的代码债。架构评审是第二道,重大设计在动手前过一次评审,把明显的架构债挡在设计阶段。CI 流水线上的静态检查和自动化测试是第三道,机器把关不怕累不嫌烦,能持续守住底线。
还有一类债容易被忽略,就是主动借的债。赶进度走捷径是难免的,但走捷径的同时必须在台账里记一笔,写清楚这里欠了什么、为什么借、计划什么时候还。借债不可怕,有意识地借、有计划地还,这才是技术债管理的正循环。技术债治理的目标是让债始终处在可控、可见、有序偿还的状态,而非消灭所有债,那是乌托邦。








