为什么开发软件这么贵?为什么我的团队交付软件的速度这么慢?为什么我的软件发布赶不上计划?为什么开发一个软件要花这么长时间?
我们之所以一遍又一遍地听到上述问题,背后是有原因的。为了保持竞争力,企业每天都需要新的软件功能,但随着时间的流逝,我们交付软件的速度似乎停滞不前,或者更糟,变得更慢了。
我想解释为什么会这样。不过,为了探讨这个话题,需要先了解一个我最关心的话题:本质复杂性和偶发复杂性。
不同类型的复杂性
任何时候,当你在解决一个问题,不仅仅是软件问题,都有两种类型的复杂性:
本质复杂性——这是包含在问题中的复杂性。如果不解决这种复杂性,就无法解决问题。它也被称为内在复杂性。
偶发复杂性——这是用来解决问题的方法和工具所带来的复杂性。这种复杂性不是你要解决的问题的一部分,而是在解决方案中引入的复杂性。它也被称为偶然复杂性。
IBM 360 系统之父 Fred Brooks 在经典论文“没有银弹:软件工程的本质性与附属性工作”中提出了这个概念。可以这么想,如果你要解决一个数学问题,本质复杂性就是指对数学的了解,因为只有懂数学才能解题。如果你想解决这个问题,要么学习所需的数学知识,要么找个懂数学的人帮忙。如果你想解决这个问题,就无法逃过学习数学这一关。
偶发复杂性
我们假设,这是一个颇具挑战性的数学问题,完全用人脑来解决是徒劳的,所以需要使用计算器。这就是偶发复杂性。还记得第一次使用图形计算器的情形吗?在这个时候,偶发复杂性就是学习如何在计算器上输入所有复杂的数学信息来帮你解决问题。你不一定要使用计算器,但你知道它对你有用,而且不会太难学。
现在,我们假设你对 Mathematica 很熟悉。Mathematica 是一个非常强大和复杂的软件,不过既然你已经知道它了,就决定用它来解决问题。你在学习 Mathematica 上投入了很多,所以不需要很多额外的努力,只是增加了解决方案的偶发复杂性。
几周后,你的一位同事遇到类似情况,他记得你曾经解决过一个类似问题。他们来找你,看你是如何解决问题的,然后你把 Mathematica 发给他们。你认为这个时候会发生什么?你认为他们会去学习数学吗?不,他们会想出另一种解决问题的方法,或者试图让你替他们解决问题。
正如你所看到的,这两种复杂性来自不同的地方,但它们之间有着紧密的联系。如果不引入一些偶发复杂性,就无法解决问题。即使是一支铅笔和一张纸也会带来一些微不足道的偶发复杂性。
如果没有偶发复杂性,就无法解决问题。
它在软件领域是怎样体现的
这可能会让你感到惊讶,在过去 20 年中,软件领域本质复杂性与偶发复杂性比率在急剧下降。David Heinemeier Hansson(Ruby on Rails 之父)用“概念压缩(conceptual compression)”这个词来描述这种趋势以及它是如何让我们的行业变得更好的。在过去的 20 年中,开源框架和库的激增是减少软件系统偶发复杂性最强大的力量。
与 20 年前相比,解决业务问题所需的代码量已经减少了一个数量级,因此你可能会认为开发软件将比那时快一个数量级。但这种情况似乎并没有发生,为什么会这样?
软件变得越来越容易开发,但与此同时,其他现象也在发生:
我们对软件的要求越来越高
公司内部的软件数量呈爆炸式增长
采用新技术的步伐正在加快
我们对软件的要求越来越多
尽管我们正在利用越来越多的外部工具和库来开发软件,让软件开发变得越来越容易,但我们对软件的要求也越来越高,仅这一点就抵消了大量的收益。如果我们用现代工具来开发 2000 年代的 Web 应用程序,会看到软件开发的生产力有十倍 (或更多) 的提升。
但我们的世界并没有停滞不前,消费者和企业对软件的期望一直在迅速增长。我们期望软件能比 20 年前做得更多。当我们开发出这些更大型、功能更丰富的应用程序时,为了保持它们的可靠性、功能性和可理解性,不得不改变软件的开发方式。
以下是我们在过去 20 年里看到的几个行业变化:
源码控制——源码控制一直都存在,但并不像现在这么普遍。不认为这增加了偶发复杂性?那就去问问第一次使用 Git 的初级工程师,他们是怎么想的。
自动化测试——我们引入了很多测试类型和测试工具。我们需要进行验收测试、集成测试、单元测试等等……这给项目带来了大量的偶发复杂性,但有助于确保交付的软件是高质量的,且功能是符合预期的。
拆分系统——随着系统复杂性的增加,组件之间连接和交互的量会呈二次级数增长。也就是说,在某种程度上,如果软件设计得不好,交互量将会继续增长,直到因为自身的复杂性而垮掉。拆分系统,特别是进行分布式拆分,会带来大量的意外复杂性。
专门化——随着 Web 应用程序变得越来越复杂,出现了大量的专门化。在 2000 年,由软件工程师负责 UI 设计、UI 构建和应用程序后端构建都是很常见的事,而到了 2020 年,这些工作需要几个角色分别承担。开发 Web 应用程序的团队通常由 UI 设计师、UX 设计师、前端软件工程师、后端软件工程师和 DevOps 工程师组成。在较大的组织中,会有更加专门化的角色,如安全、架构、数据管理、数据科学,等等……所有这些额外的角色让我们能够开发更大规模的软件,但所需的工具和流程了引入大量的偶发复杂性。
基础设施自动化——为了构建更大型、更复杂的环境来运行越来越多的应用程序,我们已经开始自动化它们的构建和维护过程。这样我们就可以更容易地进行大规模的环境管理,但需要一整套工具和知识。这些工具带来的复杂性是巨大的,导致 DevOps 成为大多数大型团队的专门角色。
频繁部署——由于应用程序的大小和复杂性都在增长,为了降低风险,我们需要以较小的增量交付软件。为此,我们引入了持续集成和持续部署的概念。同样,这对于大规模交付软件来说是非常好的,但用于构建和操作这些管道所需的工具和技能引入了偶发复杂性。
多设备和形式因素——在以前,我们可以说,我们的软件运行在一个操作系统上,只有少数的几种分辨率。现在,我们的应用程序需要在台式机、笔记本电脑和跨平台的移动设备上运行。通常,我们会有原生移动应用程序和 Web 应用程序,或许还可以加入一些物联网应用程序和手表应用程序。我们在访问数据的位置和方式上有了巨大的灵活性,改变了我们的社会,但无疑增加了软件开发过程的复杂性。
企业内部的软件爆炸
在阅读上一个小节前,你可能已经非常清楚,人们对软件要求越来越多以及越来越多的软件开发形式会导致复杂性的增加。但是,从单个应用程序的角度来看,企业拥有更多的软件会增加开发单个应用程序的复杂性吗?
答案很简单:不会,除非你想让这个软件与其他软件发生交互。一家公司使用的软件越多,系统之间的重叠就越多,不同的系统需要访问相同的数据才能正常运行,这就需要为更多的系统保存共享数据,并将所有系统集成起来。
举个例子,假设在 2000 年,你是一家办公椅生产商,那个时候你还没有网络系统。你需要为公司建立一个库存系统,所以需要开发软件来完成这件事。库存系统的用户是仓库工作人员,你可以通过生成夜间报告来获得库存信息,这些报告也可以被发送给整个公司的人。这个系统相对独立,报告功能对于每一个人来说都没有什么问题。
时间快进到 2020 年,你的库存系统已经不是一个独立的应用程序。你的合作伙伴可以直接将订单推送到你的系统中,Web 页面可以获得实时的库存更新,并在下单时更新库存。你的库存系统与物流系统直接集成,这样就可以自动生成物流标签和取货时间表。你直接在亚马逊上销售你的产品,所以你的库存系统直接与第三方软件集成。仓库的工作人员使用移动设备定位、扫描、登记和挑选商品,所以你还有一个用来完成这些事情的移动解决方案。
随着系统的激增,并覆盖了业务的各个方面,开始出现越来越多的重叠,一直到如果不与其他系统集成就无法满足需求的地步。虽然这带来了前所未有的生产力和自动化程度,但也给数据移动和集成引入了大量的偶发复杂性。
Marc Andreesen 提出了“软件正在吞噬世界”的说法。这个过程正在加速,而且看不到尽头。
https://a16z.com/2011/08/20/why-software-is-eating-the-world/
采用新技术的步伐正在加快
在 2000 年,你通常会从单个厂商那里购买系统,如微软、Sun 或 Borland,你还可能还会购买一些组件,但不管怎样,整个生态系统都是由单个厂商提供的。此时,你所采用和集成的外部工具和技术相对较少,你所能完成的任务被限制在厂商所提供的功能上。
为了跟上快速变化的技术格局,公司开始采用更开放的技术。这带来了巨大的优势,因为你可以用这些工具完成以前只能在梦中想一想的壮举。但切换工具通常是有代价的,最终会在流程中引入很多偶发复杂性。
虽然使用前沿的技术可能会在某些方面获得好处,但技术越新,维护的难度就越大。而且,越早采用一项技术,当它演化成为一项对广大用户都有用的技术时,你所经历的痛苦就越多。如何平衡一项新技术所带来的收益以及使用它所带来的痛苦是技术专家们长期以来一直在努力解决的问题。
我们现在发现,挑选有用的工具、框架和技术是一项非常有价值的技能。如果不小心使用了未经验证的新工具或框架可能会产生有害的影响。它们可能会导致大量的偶发复杂性,更糟糕的是,如果框架在跨越鸿沟之前死亡,就会把你带入死胡同。
有希望吗
关于为什么开发软件需要的时间越来越长,原因还有很多,比如业务需要更快的迭代速度、企业架构标准或对安全性的重视程度,等等。但关键是,我们在 2020 年开发的软件与在 2010 年开发的软件几乎没有什么相似之处,更不用说在 2000 年了。在很大程度上,这是一件好事。
但也有不好的地方。我们似乎又回到了 2000 年到 2007 年,那时每个应用程序都是用同样的工具开发的,而其中有很多工具变得越来越复杂。现在,很多流行的工具和框架都来自于大型企业,但它们解决的很多问题是其他企业不会遇到的。
正因为如此,很多中小型企业,甚至是大型企业的一些部门都发现,他们运行软件的能力正在迅速下降,而且不知道如何扭转局面。为了加快开发速度,他们已经开始转向低代码和无代码,但在很多情况下,这也破坏了使用这些工具构建的系统的功能和寿命。