Teaser Image

mindwind

十日画一水,五日画一石




当我们在埋怨上一个程序员留下的系统时,是在埋怨什么?是债务,技术债务。我在曾经的文章里写过,代码既是资产也是债务,资产的部分属于公司,债务的部分属于我们。

债务

技术债务来自于对金融债务的比喻,它指的是在程序设计与开发过程中,做出的错误或不理想的技术决策,由此带来的后果,逐步累积,就像债务一样。

技术债务的产生,可能是有意的,也可能是无意的。有意产生的债务,一般是根据实际项目情况(资源与期限)做出的妥协。而无意产生的债务,一般就都是经验缺乏引入的。不管怎么说,只要程序员在不断的生产代码,他们就是在同时创造资产与债务。

那如何知道技术债务已经积累到了需要去警示并着手计划进行还债的阶段了?一般来说,我们直觉都是知道的。举个例子,好几年前我们接手继续开发并维护一个系统,系统的业务开始发展很快,不停的添加功能, 每周都要上好几次线。一年后,还是每周都要上好几次线,但每次上线的时间越来越长,回归测试的工作量越来越大。再后来,系统迎来了更多的新业务,我们不得不复制了整个系统的代码去修改再重新部署,以免影响现有线上系统的正常运行…

到了这样的状况,每个人都知道,债务在报警了,债主找上门了。一次重大的还债行动计划开始了,还债的名声不太好听,所以我们喜欢叫:架构升级。架构升级除了还债,还是为未来铺路 —— 当然,前提是要有未来,如果未来还能迎来更大的业务爆发增长,架构升级就是为了在那时能消化更多的长短期债务。

之前看过一篇写技术债务的文章,也是一个老码农写的,名字就叫《老码农看技术债务》,文中把技术债务分成了好几类,大概记得如下:

  • 战略债务
  • 战术债务
  • 疏忽债务

战略债务,老码农说是为了战略利益故意为之,并长期存在。我理解就是在公司或业务高速发展的阶段,主动放弃了一些技术上的完备与完美性,而保持快速的迭代与试错性。这个阶段公司的战略利益是业务的抢占,所以这个阶段的公司都有一些类似的口号,比如:先完成,再完美;优雅的接口,狗屎的实现。战略债务的特点是,负债时间长,但利息不算高且稳定,只要保持长期“付息”,不还本金也能维持下去。

战术债务,一般是为了应对短期紧急情况采取的折衷方法。这种债务的特点就是高息,说高利贷也不为过。举个例子,曾经做电信项目时,系统处理工单,主流程上有缺陷,对某一类工单处理会卡住。这时又不太方便停机更新程序,就基于系统的动态脚本能力去写了个脚本临时处理这类工单,可以应对当时业务经营的连续性,缺陷是资源开销大,当超过一定量时 CPU 也就满了。这样的技术方案就属于战术债务的应用,当天半夜的业务低谷,就重新修复了程序,归还了这笔短期临时债务。

疏忽债务,这类债务一般都是无意识的。从某种意义上来说,这就是程序员的成长性债务,随着知识、技能与经验的积累,这类债务会逐步减少。

对于不同的角色,关注的债务分类与形态也不太一样,比如架构师关注的更多是战略债务,保持系统能够健康长期演进的债务平衡。作为架构师,就像 CFO,需要长期持续的关注系统的资产负债表。战略债务可能更多体现为架构、设计与交互方面的形态。而具体某个功能实现层面的代码债务,则更多落在相关开发工程师的关注范围内。测试工程师,会关注质量方面的债务,而一到交接时,各种文档债务就冒出来了。

分析了这么多关于技术债务的方方面面,那么程序员该如何面对技术债务,该怎么还债?

信用

拥有债务,并不代表信用差。而一个程序员的信用体现在面对技术债务的态度与方法。

现实生活中,债务依附于借债的主体方,比如金融债务依附于个体或组织,但如果个体死亡或组织破产了,债务就失去了依附体(此类情况不考虑担保,担保是一种依附的转移),自然也就消失了。

而技术债务的依附体,并不是程序员,而是依附于程序构造的产品或系统,所以当产品或系统的生命周期结束,相应的技术债务也会消失。因而,此种情况下,程序员是有充足的理由不还技术债的,这是技术决策的一种,并不会降低程序员的信用。

任何一个程序系统或其一部分都会与某一个程序员建立关联,这个程序员此时就负责这部分系统。那么程序员在此基础上继续创造代码时,既增加了资产也可能引入了新的债务,那么他的一个重要职责就是,维持好资产与债务的平衡关系,如果在此期间,系统的债务失衡导致“破产”(需要被迫大规模重构或重写),那么这个程序员的信用必将受到关联伤害。

所以,我们才需要谨慎且持续的关注与管理程序系统的技术债务问题。

对于战略债务,长期来说都是持续付利。就像现实中一些大企业从银行借钱经营发展,每年按期付息,但基本不还本金。支持公司快速发展到了一定阶段,基本进入成熟期后(市场大局已定),再主动降低负债风险和经营成本。创业公司从小到大的发展过程中,技术上的战略债务与之类似。

而疏忽债务,作为有信用的程序员,需要坚持成长性归还策略。一旦发现过去的自己写下了愚蠢的代码,就需要主动积极的确认并计划归还。

如上,我们首先要认识理解技术债务,还要有效识别技术债务,最后就是合理的还债计划了。

还债

在产品突进,四处攻城略地时,还需要配合周期性的还债,保持债务平衡,才能保证整体健康的快速发展。

还债时,我们主要考虑债务的大小和还债的时机,在不同的时间还债也许研发成本相差不大,但机会成本相差很大。而按不同债务按大小,又可以分为大债务和小债务。一般,我把需要以周为单位计算的债务算作大债务,而只需一个程序员几天时间归还的债务算作小债务,所以这不是一个精确的定义。

小债务的归还,基本都属于日常的重构活动,局限在局部区域(模块、子服务)的实现层面。而大债务 —— 架构升级还的一般都是大债务 ——的归还,需要仔细的考虑和分析机会成本与潜在收益,所以大债务归还要分三步走:

  1. 规划:代表愿景,分析哪些债务需要在什么时间还,机会成本的损失与预期收益
  2. 计划:代表路径,细致的债务分期偿还计划
  3. 跟踪:真正上路了,确认债务的偿还质量与到位情况

如今微服务架构的流行,基本把小债务锁定在了一个或几个微服务体内。即使碰上没有信用的程序员把自己负责的微服务搞烂了,形成债务破产,这时的还债方式无非就是完全重写一个,在微服务拆分合理的情况下,一个服务的重写成本是完全可预期和可控的。

做一个有信用的程序员的关键是:知道何时引入债务解决紧急情况,之后立刻还债;无意识引入的负债,当时看不见,也许多年后你成长了,看见了,但别假装看不到啊。

再有代码洁癖的人也没法写出无债务的代码,写出无债务的代码可能是一种极限,我们一直在追求与接近,却很难达到。而无债务的系统也不存在,其实负债高的系统往往活力还比较强。就像人一样,年轻时可以适当战略负债,等老了,债也还得差不多了,负债的意义也不大了。


写点文字,画点画儿,记录成长瞬间。 微信公众号「瞬息之间」,既然遇见,不如同行。