PS: 所有没给原文链接的精读都是原创,本篇也是原创。
前端精读之前写了 23 篇设计模式总结文,再加上 6 种设计原则,开闭、单一职责、依赖倒置、接口分离、迪米特法则、里氏替换原则,基本上对代码的可维护性有了全面深刻的理解。
但你我在工作中都会不断遇到烂代码,快要无法维护的大型项目,想一想,仅凭设计模式就能解决这些问题吗?为什么不断膨胀的大型项目总是变得越来越难以维护,而复杂度更高的真实世界,但没有人觉得快要崩塌了呢?
设计模式考虑的是代码之间的关系,设计原则考虑的是模块以及项目间的关系,那是否存在更上层的思考,解决大型项目越来越难维护的问题?
先考虑一下,为什么真实世界没有可维护性问题?
这个问题看起来有点傻,因为从来没有人会发出这样的抱怨 “我们的产品、科技、概念太多了,多到我觉得无法在这个世界活下去了”。但是在代码世界,程序员经常会抱怨,项目的概念太多、设计过于复杂,以至于他无法继续再维护下去了,是时候寻找下一份工作了。
一种显而易见的解释是,生活中,我们都是小角色,活在自己的天空下并不需要触及那么多概念,而程序员在项目中基本扮演了上帝的角色,必须为每一个细节操心。
但这并不完全解释得通。我们以为自己接触的东西不多,但实际上日常生活的知识太多了,就拿家电来说,每个人都会同时接触几十种家电,大到空调冰箱洗衣机,小到手机牙刷充电器,即便这些产品被大量标准化,但每个产品用起来都有大量细节的区别,但没有一个人觉得学习使用一个新剃须刀是一种负担,也并不觉得一款设计得不好的牙刷,会对整个牙刷行业造成怎样负面的冲击。
这背后的原因是:拷贝。正因为我们用的每一件东西都是拷贝,所以即使用坏了也不会对其它相同物品产生任何影响。但代码世界则不同,因为代码调用关系的存在,复用的越优雅,破坏力也就越大。一栋大楼断了几块钢筋尚可支撑,但换在代码世界,只要断了一块钢筋,就意味着这栋大楼所有钢筋都断了。这就是程序员最痛恨的问题之一,就是为什么改了一处看似人畜无害的代码,却导致一场故障。
从这个角度来说,代码世界是无法吸取真实世界经验的。而且代码世界的这种副作用,在商业上是有巨大正向价值的,即软件的边际成本几乎为零,这是实体产品做不到的,因此软件需要付出可维护性代价,似乎是这种极低边际成本的代价。
虽然通过借鉴真实世界的经验,使自己维护成本变成零时不可能的,但真实世界对软件世界确实有可借鉴之处,下面我们就来探讨几个有意思的点。
不知道你会不会有过这样的思考:面试官总是问原理,就是担心我只会用框架,而缺乏基础。但基础是什么呢?懂得 js,java 算是基础吗?也可以说不算,因为这些语言背后的编译原理好像才是基础,编译原理背后还有操作系统,操作系统运行在硬件上,而硬件的原理呢?从 CPU 设计到背后的硅是如何制作的,等等,这样下去,似乎永远也无法掌握原理。
但当我们从软件推导到硬件时,可以很自然的发现,没有人觉得掌握硅胶的制作过程是一件必须的事,我们可以一直使用硅胶制作的产品,但却可以不用了解硅胶制作的原理。
真实世界总是不断屏蔽复杂度,作为消费者时,我们面对的商品总是经过精心包装,简单易用的,只有我们工作时,才需要对某个专业领域的原理有所了解。
这个道理可以迁移到代码世界,即对于一个庞大而复杂的项目,不能指望每位开发者都了解全部原理后才能工作,我们需要在大多数时候把开发者当作消费者来看待,提供精美而稳定的接口。要做到这一点,需要一个类似下图的架构设计:
从图中可以看出,即便是业务层代码,我也不需要关心过于底层的实现,底层的代码就像脚下被压实了的土地,只需要在上面走就行了。
然而最让人崩溃的是下面的设计:
为了解决一个问题,需要面对无穷无尽的上下文,这就是维护成本高的最主要原因。
作为开发者,已经习惯了评价代码维护成本高还是低,今天我们换个视角,想一想为什么你会觉得维护成本高?
对维护成本的感受不完全是客观的,我画了一个四象限图:
左边是和人相关部分,包括你对代码的理解能力,以及对项目的熟悉度。
理解能力越强,越不容易觉得维护成本高;对项目越熟悉,哪怕是屎山代码,也会觉得重构后可维护性并不会提高,因为自己对项目会变得不熟悉。
右边是和项目相关部分,包括业务本身的复杂度,以及这背后的技术抽象实现的质量。
业务本身越复杂,维护成本就会越高,因为信息量不可避免的增大了,我们永远不能只盯着 Hello World 的 Demo 研究框架;代码质量体现了技术对业务的抽象,抽象的好,复杂度曲线就会比较贴合业务真实复杂度,抽象的不好,Hello World Demo 也能够新人进来喝一壶。
在这四个关键词中,业务复杂度是几乎无法改变的,对项目熟悉也需要一个过程,所以重点应该放在理解能力与代码质量两部分。
无论是个人理解能力,还是代码质量,目标都是帮助我们快速理解项目,也就是说,只要能快速理解技术项目在做什么,我如何快速融入,就会觉得可维护性高,反之则觉得不好维护。
所以一个简单的项目,或者一个分层合理,文档清晰的大型项目都会让人觉得可维护性好。在这一点上,需要向真实世界学习的经验就是,即便在软件世界,也并不是了解所有原理,所有犄角旮旯的逻辑才表明技高一筹,带着这种思想工作只会让大家陷入无尽的内卷和理解焦虑。我们要给大家思想减负,不需要理解的模块、代码设计,就不要轻易展示出来,将每个模块开发所需了解的最小知识设定好,最大程度减少开发者的理解负担。
当然要补充一句,这并不意味着局限开发者的成长和学习空间,其它知识随时敞开大门,只是理解它们并不是日常开发所必要的,这些知识形成文档可以用完即弃,不用成为长期记忆。说到这,就引出了真实世界第二个有趣的地方,就是说明书。
我回头想想也挺不可思议的,无论快递买来任何需要组装的东西,按照说明书的指引最终都可以组装好,而且装好之后就可以把说明书扔了,完全没有认知负担。
与其说快递包裹的说明书太完善了,不如说说明不完善,不好用的商品根本卖不出去。我们早已习惯极度易用的商品,及其详尽的说明书了,这是商业社会持续发展,长期博弈后的结果,而且会稳定持续下去。试想一下,如果我们参与维护的项目也有精巧的设计,完善的文档,那维护就不是什么问题,按照文档说的一步步来就行了。
那为什么大部分情况,我们接手的项目就像一个没有说明书的乐高呢?这应该是商品与代码的本质区别了,即商品质量好不好,是由买家用钞票投票的,做得好用,说明书完善的商品才能存活下来,但这背后的技术实现是看不到的,也没有人可以投票,即便技术人员吐槽代码无法维护,但如果项目取得了商业上的成功,也只会越做越大,技术债越滚越多。
技术项目的买家是程序员,但程序员没有拒签的办法,导致无论项目质量如何都要接受,没有市场机制的作用,就导致了烂代码随处可见。
要解决这个问题,首先要意识到这个问题,即技术项目质量本质上是无人长期、持续关心的,你可能会说,技术 Leader 会关心呀?但这和业务驱动相比实在是太弱了。产品有用户侧钞票的投票,无论管理者换多少人,还是会从源头持续提供动力,但项目质量总是要反复强调,间歇性整治,并且不同的 Leader 关心程度也不同,因为这背后没有源动力,除非项目质量影响到用户那头的现金供给了,但这种情况发生时,说明项目早已烂透了。
正是因为技术质量缺乏源动力,或者说源动力传导链路太长,我们才要人为的不断加强重视,重视文档、重视使用体验、重视是否符合设计模式。只有长期主义者才能坚持做代码质量治理,因为坚信总有一天,代码质量会影响到业务发展。
这次从真实世界借鉴了一些经验到软件世界,我们从借鉴真实世界的屏蔽复杂度,谈到了为什么真实世界的说明书这么好用,但技术项目文档却总是缺胳膊少腿的问题。
我们总结出的经验是,设计原则与设计模式固然可以提升可维护性,但归根结底还是动力的问题,提升代码质量本身就是一件缺乏动力去做的事,或者长期被认为是重要不紧急的事,往往很难找出理由现在就去做,但没有人觉得不应该做。
所以想要提升可维护性,找到为什么现在,立刻,马上就要做技术优化的原因,并立即开始优化才是最重要的。
讨论地址是:精读《可维护性思考》· Issue #359 · dt-fe/weekly
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)