2.3.1 软件退化的根源
我们正处于一个快速变化的时代,这种快速变化首先体现在最近10年的互联网发展上。从电子商务到移动互联,再到“互联网+”与传统行业的互联网转型,这是一个非常痛苦的过程,而近几年人工智能与5G技术的发展,又会带动整个产业向着大数据与物联网发展,另一轮的技术转型已经拉开帷幕。这个过程一方面会给我们带来很多的挑战,但另一方面又会给我们带来无尽的机会。它会带来更多的新兴市场、新兴产业与全新业务,给我们带来全新的发展机遇。
然而,在面对全新业务、全新增长点的时候,我们能不能把握住这样的机遇呢?我们期望我们能,但现实令人沮丧。软件总是经历着这样的轮回:软件设计质量最高的是第一次设计的那个版本,第一个版本上线后就开始应对各种需求变更,需求变更又常常会打乱原有的设计。因此,需求变更一次,软件就修改一次;软件修改一次,质量就下降一档。不论第一次的设计质量有多高,经历几次变更后软件都会进入一种低质量、难以维护的状态。然后,团队就不得不在这样的状态下以高成本的方式维护多年。维护好原有的业务都非常不易了,又如何再去期望未来更多的全新业务呢?
图2-4是一段电商网站支付功能的代码,最初的版本设计质量还是不错的。
图2-4 电商网站支付功能第一个版本的设计
第一个版本上线后,很快就迎来了第一次变更。第一次变更的需求是增加商品折扣功能,并且这个折扣功能还要分为限时折扣、限量折扣、某类商品的折扣、某个商品的折扣。拿到这个需求时该怎么做呢?很简单,增加一个if语句,如果是限时折扣就怎么样,如果是限量折扣就怎么样……代码开始膨胀。
接着,第二次变更需要增加VIP功能,除了增加金卡、银卡的折扣,还要为会员发放各种福利,让会员享受各种特权。为了实现这些需求,我们又需要在payoff()方法中加入更多的代码。
第三次变更增加的是支付方式,除了支付宝支付,还要增加微信支付、各种银行卡支付、各种支付平台支付,为此我们又要塞入一大堆代码。经过这三次变更,大家可以想象现在的payoff()方法是什么样子了吧,变更是不是可以结束了呢?其实不能,接着还要增加秒杀、预订、闪购、众筹、返券等。程序越来越乱,越来越难以阅读,每次变更也变得越来越困难,如图2-5所示。
图2-5 电商网站支付功能的变更过程
那么,为什么软件会退化,为什么软件设计质量会随着变更而下降呢?在这个问题上,我们必须寻找到问题的根源,才能对症下药、解决问题。
要探寻软件退化的根源,首先要从探寻软件的本质及其规律开始。软件的本质就是对真实世界的模拟,每个软件都能在真实世界中找到它的影子。因此,软件中业务逻辑正确与否的唯一标准就是是否与真实世界一致。如果一致,则软件就是正确的;如果不一致,则用户就会提Bug,提新需求。
软件做成什么样,既不由我们来决定,也不由用户来决定,而是由客观世界决定。而用户总在改需求,是因为他们也不确定客观世界的规则,只有遇到问题了才能想得起来。因此,对于我们来说,与其唯唯诺诺地按照用户的要求去做软件,不如主动地在理解业务的基础上去分析软件,而后者会更有利于我们降低变更的成本。
那么,真实世界是怎样,我们就怎样开发软件,不是就简单了吗?其实并非如此,因为真实世界非常复杂,要深刻理解真实世界中的这些业务逻辑是需要一定的时间的。因此,我们最开始只能认识真实世界中那些简单、清晰、易于理解的业务逻辑,把它们做到我们的软件里。所以,每个软件的第一个版本的需求总是那么清晰明了、易于设计。
然而,当我们把第一个版本的软件交付用户使用的时候,用户会发现还有很多不简单、不明了、不易于理解的业务逻辑,还有些需求没做到软件里。这使得用户在使用软件的过程中不方便、和真实业务不一致。因此,用户就会提Bug,提新需求。
在我们不断地修复Bug、实现新需求的过程中,软件的业务逻辑会越来越接近真实世界,使得我们的软件越来越专业,让用户感觉越来越好用。但是,在软件越来越接近真实世界的过程中,业务逻辑也会变得越来越复杂,软件规模越来越庞大。
大家一定有这样一个认识:简单软件有简单软件的设计,复杂软件有复杂软件的设计。如图2-6所示,现在的需求就是将用户订单按照“单价×数量”计算应付金额,那么在一个PaymentBus类中增加一个payoff()方法,这样的设计没有问题。然而,如果现在的需求是需要在付款的过程中计算各种折扣、优惠、返券,那么我们必然会做出一个复杂的程序结构。
图2-6 简单软件与复杂软件的设计
但是,真实情况却不是这么简单。真实情况是,起初我们拿到的需求是那个简单需求,因此我们就在简单需求的基础上进行了设计开发,但随着软件的不断变更,软件业务逻辑变得越来越复杂,软件规模不断扩大,逐渐由一个简单软件转变成一个复杂软件。这时,如果要保持软件设计质量不退化,就应当逐步调整软件的程序结构,逐渐由简单的程序结构转变为复杂的程序结构。如果我们总是这样做,就能始终保持软件的设计质量。然而,非常遗憾的是,我们以往在维护软件的过程中不是这样做的,而是不断地在原有的简单软件的程序结构下,向payoff()方法中塞代码,这样做就必然会造成软件的退化。
也就是说,软件变更不是软件退化的根源,而只是一个诱因。如果每次软件变更时适时地进行解耦和功能扩展,再实现新的功能,就能保持高质量的软件设计。如果在每次软件变更时没有调整程序结构,而是在原有的程序结构上不断地塞代码,软件就会退化。这就是软件发展的规律。