前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术债:the good, the bad, and the tao

技术债:the good, the bad, and the tao

作者头像
tyrchen
发布2018-03-29 10:01:30
9300
发布2018-03-29 10:01:30
举报
文章被收录于专栏:程序人生程序人生

安姐最近一篇文章『关于技术债务』(请戳文末阅读原文访问)阐述了如何避免和处理技术债(tech debt),值得一读。也许是年关将至,出来混,欠的账都要还了,我正好也打算写写技术债,干脆趁热打铁,也来一篇。本文从另外一个视角看技术债。

首先,什么是技术债?通常意义上的技术债是指我们在开发产品或者功能过程中,快速(往往伴随着混乱和各种限制)地在时间和资源受限的情况下完成正常情况下无法完成的工作时引入的技术问题。这些问题可能会导致将来引入新的功能变得很困难,或者开发出来的功能很快到达容量上的瓶颈。这种由于时间和资源受限的情况下不得不牺牲质量引入的技术债是最主要的一种技术债。

第二种技术债是因为开发团队未能事先进行合理的设计,导致架构混乱或者过度设计,从而引发在未来时刻爆发的技术问题。

第三种技术债是随着时间的推移,一个原本设计良好的架构不断叠加第一种技术债,导致代码是 case by case 堆叠起来的。老的 case 不敢删除,新的 case 不断增长,使得代码膨胀到一个无法控制的规模。

还有一种技术债是因为开发团队的能力和水平有限,写出来的代码质量低下,从而引发在未来时刻爆发的技术问题。

大部分软件开发团队同时背负以上几种技术债。所有的技术债最终不得不被架构更加合理,接口更加清晰,效率更优,容量更大的方案取代,也就是所谓的还债。

注意技术债都是形容完成了工作但留下了遗憾或者隐患,完不成工作的那种叫技术败(tech failure),不在本文讨论当中。

The good

债务是人类经济活动中最伟大的发明之一,其重要性可以和纸币等量齐观。可以负责任地说,有经济的地方,就有债务。作为锄禾日当午的农民大军中的一员,我们码农最熟悉的债务就是房贷。房贷可以让我们在当下现金流短缺的情况下购买到我们将来才能负担得起的房子。如果没有房贷,我们是无法过上居者有其屋的生活的。

技术债既然被称之为一种债务,虽有债务的缺点,但也连带债务的好处。序员们并非活在乌托邦中,可以无所顾忌地追求极致的架构;我们生活在一个实际的,血淋淋的商业世界里。销售要某个产品和别人对标打单,市场要编制一个美丽的五彩缤纷的故事来应付发布,客户要求在限期之内完成某个他们自己也不知道什么时候才使用的功能(通常只是为了彰显甲方那种「我所说的,你都要照做」的气势),工程师就必须在限期之内完成。完不成,产品卖不出去,市场推广不开,客户和你一拍两散。还房贷?娃的奶粉钱?想都别想。所以这种时刻,引入技术债是不得已的聪明的做法。

这种做法在创业圈里有一个美丽冻人的名字:MVP(minimal viable product)。技术圈里已经快被人遗忘的,令人尊敬的老司机盖子同学就是个中好手,basic 解释器如此,DOS 系统也是如此。如果他老人家不付出技术债,恐怕还没拿到第一桶金就挂了,软件世界的版图也就可能没微软什么事了。

另一个经典的同时也令人扼腕的「举债经营」成功,不愿负债失败的例子是 mongodb / rethinkdb。做为一个 database,mongodb 在其前两个版本的打法是骇人听闻的:一个数据库竟然靠 mmap 来提高效率,通过 fsync 来保证持久化(如果你不打开 oplog 的话,就只能听天由命了),然后还好意思发布 1.0 一路到 2.x —— 有木有搞错!技术老司机们纷纷看不下去了,各种质疑雄文层出不穷,比如说经典的 call me maybe(请自行 google call me maybe mongodb)。可以看得出 mongodb 把技术债用到了极致,而且是用在了对于其他软件公司性命攸关的部分:数据库。而 rethinkdb 则走向了另一个极端,为了技术正确,一路闷头苦练内功。两个同样在2009年发布第一个版本的产品,2011年 mongodb 就已经有人在 production 里使用了,而直到 2015 年,rethinkdb 才遮遮掩掩地宣称它们 production ready。结果是 rethinkdb 错失了 NoSQL 的红利期,赚不到也筹不到足够的钱维持其运营,不得不解散团队。而 mongodb 在购买了 wiredtiger 引擎后,基本解决了最让人诟病的技术债,在软件服务的路上走得还算顺畅。

所以,不要怕引入技术债,相反,要敢于引入技术债,举债经营,尤其是新的产品和功能未能证实其价值的时候。通过引入技术债,我们以软件日后腾挪的空间换取了发展的时间。从这个意义上讲,MVP 不仅仅适用于创业公司,也适用于任何大小的公司。

The bad

债务的缺点自然是利息。房贷固然让你圆了一个安居乐业的梦,但经济不景气时可能导致的失业一下子会让你失去了偿债能力。银行会收了你的房子,残酷无情地敲碎你「人生蒸蒸日上」的黄粱美梦,让一切归零。所以有了房给贷这样的债务,一定要关注短期偿债能力,把流动比率或者速动比率控制在一个合理的范围内,同时要握有足够的自由现金流应对不时之需。

对于公司的运营来说,短期负债过多会影响正常运营甚至导致公司没有偿债能力从而不得不破产或者进行债务重组。对于软件开发而言,技术债所带来的利息是新功能更长的开发周期,或者老功能很快触碰天花板。这会影响公司接下来的发展。如果累积一段时间技术债的利息无法清偿,公司可能会技术破产,进而导致运营上的破产。twitter 在早期开发时,MVP 选用了 rails。快速的开发能力带来了快速的产品验证,然而 rails 的低效使得 twitter 很快在技术上触及了天花板(尽管 twitter 针对其做了无数优化):2007 - 2008 年,twitter 动不动就挂了,并且一度挂了三天( https://techcrunch.com/2008/04/22/twitter-may-not-have-to-care-about-uptime-any-longer/ )。后来在技术主管换血之后,它们痛定思痛,大刀阔斧做了债务重组,摒弃 rails,拥抱 java 生态圈,用 scala 重写很多核心服务,终于把服务稳定下来。

技术债一个很要命的问题是债务的叠加。生活中我们很少叠加债务,如果你有了房贷,你会减少各种开销,避免同时背上那些 APR 极高的信用卡债务。软件开发中我们却很容易叠加债务。这些叠加的技术债会使利息指数增长,从而反噬我们。

技术债还有一个很严重的问题是 backward compatibility。软件的有些接口设计上的缺陷被使用者当成了功能去使用,使用的范围之广以至于开发者在新版本中无法还债,只能被动地一路保有这样的债务。windows 系统在其演进过程中,有大量的缺陷和设计失误被开发者当成了功能,使得微软无法改变这些不合理的接口。这是历史的教训,我们需要小心这类技术债。一个很重要的原则是对外的接口(API,SDK等)一定要小心设计,如果要负债,让接口背后的实现去负债。

The tao

技术债并非洪水猛兽,我们要要合理控制,让其发挥债务的优势。处理之道:

1) 拥抱 MVP。先解决温饱问题,再考虑还债。

2) 把技术债外包出去,套句时髦的话,就是 tech debt as a service。infrastructure 交给 aws/azure,monitor 交给 datadog / newrelic,search 交给 elk / algolia,analytics 交给 mixpanel / 神策数据(好友的公司,免费硬广),支付交给 stripe / 微信 / 支付宝。。。将自己的非核心技术问题/难题转嫁给更合适的团队。不过这种债务除了需要支付月度的服务费作为利息外,还有另一种隐性的,代价不菲的利息:vendor lock-in。

3) 雇佣你所能获得的最优秀的人,给予她们你所能给予的,最能发挥她们能力的权限。由人引发的技术债是最让人痛心疾首无语凝噎的技术债。这条不解释。

4) 拥抱匡威定律。你的组织架构决定了你的代码结构。想要快速独立的功能交付能力,你要有包含所有角色,拥有直接决策权的端到端的功能团队,而不是开发,测试,运维等彼此独立,组织上汇报给不同 VP,优先级完全不在一个调子上,各自为战的团队;想要使用 micro service,组织结构上就要打造彼此平行的 service team。很多技术债的出现是由于技术方向和组织结构不协调导致的。

5) 在实现上可以多些负债,在接口上尽量减少负债。你有没有问过自己,除了从别人那里学习撰写代码的艺术和教育新人的目的外,为什么我们需要 code review?为什么我要关心别人的代码写成什么样子?我要关心 memcpy 的实现么?我要关心 twillio 究竟在代码级怎么把短信发给我的用户么?我不关心。我只关心 interface 和 SLA。而软件中重要的恰恰是 interface。为什么我们没有interface review?为什么如此重要的 interface review 要混在并不那么重要的 code review 中?code 可以被抛弃,被重写,但 interface 一旦确认,修改的代价会很大,尤其对外的 interface。

说句题外话。如果我们只做 interface review,不做 code review,有什么严重后果么? 貌似没有 —— 如果 code 写的不好,扔了重写就成。如果我们以这种思维打造软件,我们还需要整个开发团队使用一种或者少数集中语言么?我们还需要为每个工程师设置 peer backup 么?一旦一个工程师离职,他的工作在需要的时候能够被重写,是不是就足够了?对于这一点,不懂软件的 Bezos 有着远见卓识 —— 他在 02 年提出了一系列苛刻的关于 service instance 的构想,并强制研发团队照做。见我的文章:拥抱约束

6) 意识到软件是个有新陈代谢的有机体,并围绕这个思想来打造软件。代码是需要新陈代谢的,这意味着新的代码不断加入,旧的代码要不断删除。生物体里细胞坏了会重新造,组织死了会重新生成。生物体不会试图「修复」生老病死的细胞,只会将其杀死并代谢掉,同时重新生成功能一样的细胞。我们写代码时也要使每个部分保持独立(像一个个完整的细胞或者组织),留有日后自己或者别人将其完全删除重写的余地。这样,当我们主动或者被动引入技术债时,便心中不慌了 —— 因为我们知道,我们保留了删除和重写的权利。

为什么解决技术债时不是考虑重构,而是考虑重写?重构技术债太多的代码有重构的代价 —— 能重构代码,你得先把前任(或者几个月前的自己)的古怪逻辑搞清楚,还得小心不要让自己引入问题。很多时候,技术债累积过多的代码叠加了难以伸缩和扩充的问题,与其小心翼翼,瞻前顾后地重构不如重写来的干脆,也避免了半吊子工程。当然,如果能达到每个功能或者组建可以不费太大代价重写的理想,那么要选用以此为核心的架构,比如说 "micro service",并且制定相关的规则,比如说,一个 service 不超过一个程序员一周的工作量,以便于随时重写。

重写代码而非重构是足够疯狂的想法,但我们身边有个最好的例子:unix。有人反对 unix 是一系列 micro service 组成的系统么?估计没有。我们应该把我们打造的系统组织成围绕着一个核心的一个个 ls / grep / awk / vim...。vim 作为一个伟大的软件,其代码质量实在不敢让人恭维,所以有人干脆另起炉灶,照着 vim 的接口做了一套 neovim。

先写这么多。回头我就软件的新陈代谢,生老病死再写篇文章。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • The good
  • The bad
  • The tao
相关产品与服务
云数据库 MongoDB
腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档