
如果你长期开发一个程序,只添加功能而从不重组它以反映你对那些功能的理解,那么最终这个程序根本不包含任何理解,所有在它上面工作的努力都会越来越慢。
技术债不是乱代码。是缺乏理解。 代码是症状。病因是系统从未被重组以反映建造者沿途学到的东西。每一个不经反思添加的功能都是一层沉积物,掩埋了之前存在的任何理解。
简单的画面
想象一个图书馆,新书靠扔在地上来添加。几个月后你还能找到东西,你记得扔在哪里了。几年后只有最初的管理员能在书堆里导航。管理员离开后,图书馆功能上是空的:书都在,但找任何一本比从头写还慢。图书馆不欠任何人债。它有一个组织的赤字,而这个赤字随着每一本添加的书复利增长。
复利式的税
经济账很简单:要么你付钱让人在每次大迭代后重构重组,要么你付钱让每一个碰这个项目的开发者直到时间尽头盯着代码发几个小时的呆,想着这他妈到底是什么。
那种困惑式的发呆是复利的。复杂性对读者比对作者更明显,如果你写的代码在你看来很简单,但别人觉得复杂,那它就是复杂的。作者的流畅是局部幻觉;读者的困惑是系统的真相。一种弥漫的困惑感不断积累。新人花好几周试图理解没人费心让它可理解的系统。建造它们的人已经走了。决策背后的意图埋在没人读的commit信息里。工作的迷雾不断加厚,直到代码库变成了丘吉尔的报告:用它自身的长度来防御被阅读的风险。
在组织层面,你付出的是速度和人才流失。 有才华的人经过几轮这样的折磨就会离开。你不能指望一个在赶工代码、理解不充分的需求和已离职人员留下的捷径的积淀中导航的人有什么生产力。到那时技术债气球已经爆了,你手上拿着的是一个有毒资产。
Oracle Database 12.2是教科书式的终末案例:2500万行C代码,靠数以千计的标志位以神秘的方式互动维持在一起。修一个bug意味着花两周理解20个不同的标志位,再加一个新标志位来处理新场景,然后在200台服务器上运行数百万测试跑30小时,收到100到1000个失败。再重复两周直到你念对了咒语。然后加一百个测试来保护你的修复不被下一个开发者破坏。一个新功能需要六个月到两年。系统存活不是因为好的设计而是因为数百万测试充当了外骨骼,理解已经没了,但测试套件记得代码自己不再知道的东西。这是代码层面的消防员陷阱:每一次修复都增加复杂性让下一次修复更难,最终辞职的开发者永远不会回来。
组织健忘症
同样的动力在知识层面运作。大多数公司不应该有wiki,成功的wiki如维基百科是由一支编辑大军驱动的,而大多数组织永远不会给那么多内容策略排优先级。管理不善的知识让组织拥有金鱼的记忆。
我无法告诉你我参加过多少新产品提案会议,在那里没人记得两个季度前关于完全同一件事的会议。就像《土拨鼠之日》,但你一遍又一遍地开同样的会。
这是组织层面的问题定义陷阱。问题被辨识、讨论,然后被遗忘。它以新面目重新浮现。同样的论证被排演。同样的结论被达成。没人记得上次的结论因为组织从未重组其知识以反映它学到的东西。
约束理论加上了运营成本:每一次关于已解决问题的会议都是从约束那里偷走的时间。破坏手册中”回到上次会议决定的事项并重新讨论”的指示与一个单纯忘了自己决定了什么的组织无法区分。
低/中/高水平理解
低水平理解:“我们以后再清理,先发了再说。”
中等水平理解:“我们需要专门的重构冲刺来偿还技术债。”
更好的理解:技术债不是待办列表上需要排优先级的条目,它是理解的持续缺席,而修复方案不是定期清理而是一种拒绝只加不整理的文化。 另一面是过早美化,在你真正理解问题之前就试图通过抽象掉底层细节来让东西变漂亮。可见的丑陋诚实地揭示了无知的因素;过早的优雅把它埋在一个看起来干净但不被理解的表面之下。工程算法编码了这一点:质疑和删除必须先于构建。一个只通过添加而从不通过反思增长的代码库不是在积累债务,它在失血般地丧失理解。 而理解一旦丢失,恢复它比当初写代码更贵。
核心收获
最深的重构:技术债不是关于向未来借款的财务隐喻。它是关于未能从当下学习的认识论隐喻。代码能用。功能发布了。但系统不包含任何关于为什么事情是这样的记录,而没有那个记录,每一次未来的改变都需要重新发现原始建造者知道但忘记保存的东西。思考的意志应用于组织:一个从不暂停来理解自己已经建造了什么的系统,会把所有未来的能量花在困惑上,而困惑是最昂贵的无知形式。
参考:
- Dave Rupert, Technical Debt as a Lack of Understanding