软件开发的“偶然”困难和“本质”困难

No Silver Bullet

No Silver Bullet – Essence and Accident in Software Engineering 是著名计算机科学家 Fred Brooks 的一篇著名论文。Fred Brooks 被称为IBM大型机之父,是著名软件工程著作《人月神话》的作者,在1999年获得了图灵奖。《No Silver Bullet》一文发表于1986年,作者认为,由于软件开发本质上的困难,在当时的十年内不会有技术和方法可以让软件生产力有数量级上(也就是十倍以上)的提升。相比《人月神话》得到的广泛认同,这篇论文发表后引起了不小的争议。1995年《人月神话》再版时,Fred Brooks 增加了一章《No Silver Bullet Refired》,对这些争议分别作了回应。作者赞同了同时代的一些观念,如“基于组件的软件开发会是解决软件根本困难的一个方法”,但是他认为这并不会改变或减少软件开发本质上的困难。而且在当时的近十年里,也确实没有出现能让软件生产力提高十倍的技术和方法,Fred Brooks 的预言是准确的。

时间来到今天,相对于将近 40 年前,当代软件开发人员的生产力无疑是有巨大提升的。通过大量成熟的开发框架,开发人员分分钟就可以搭建出一个基础功能齐备的 Web 站点;即使完全不懂TCP/IP也能快速实现一个性能优异的、简单可用的网络服务器;通过并行计算,流式计算等手段,我们已经可以轻松处理 PB 级规模的海量数据,甚至可以是实时的。这些有没有达到一个或几个数量级的提升?我们不是大师,不敢妄议。但是云服务,服务化,各种开箱即用的开发框架,包管理器,组件库等等,组件化的软件工程确实大大帮助了我们软件开发人员。

软件开发似乎越来越简单。然而,软件开发人员的辛劳好像一点儿也没有改观,尤其是在中国,加班似乎是软件开发人员的必选项,他们总是有做不完的需求和改不完的 Bug。写代码是简单的,开发一个软件系统仍然是不容易的。一个软件被称为系统就不只是实现功能那么简单,需要考虑可靠性、性能、可维护性等等。尤其是可维护性,现代企业对信息化系统的依赖越来越重,企业的业务流程是不断变化的,信息化系统对“需求变化“的响应越快,企业的效益就越高。良好的可维护性,是信息化系统能快速响应”需求变化”的基础。虽然敏捷开发,DevOps 等理念已经越来越深入人心,但是说起来容易做起来难,大量企业的软件系统都处在补丁摞补丁,混乱生长的状态,开发一个高质量的软件系统无疑仍然是非常困难的。

在《No Silver Bullet》一文中, Fred Brooks 将软件开发中的困难分为本质的困难和偶然的困难。组件化的开发,更高级的开发语言,更先进的 IDE 只能帮我们解决偶然的困难。概念性的设计,这种本质的困难,仍然是困难的,换句话说实现一个组件是容易的,但是设计出良好的组件仍然是不容易的。

本质(Essence)与偶然(Accident)

据 Fred Brooks 介绍,Essence 和 Accident 这两个词来源于亚里士多德的古老用法,是两个哲学名词。根据维基百科,Essence 是一种或一组永远不变的属性,它们使一个实体或物质成为它的根本所在,并且它必然存在,如果没有它,物质就失去了它的身份。与之相对的是 Accident。Accident 是指实体或物质偶然具有的一种属性,没有这种属性,物质仍然可以保持其特性。比如一把椅子,可能是铁制的,也可能是木制的,可能是红色,也可能是黑色,颜色和材料都是它的偶然属性,这些偶然属性不能决定它就是一把椅子,椅子有它自己的本质属性让它成为一把椅子。

后来在《No Silver Bullet Refired》一文中,Fred Brooks 对 Essence 和 Accident 又作了补充解释,他认为 Accident 的意思更接近于“附带的”或者“从属的”。他借用一位英国剧作家关于创造性活动的观点进行解释,所有的创造性活动包括:(1)概念性结构的形式规格化;(2)使用现实的介质来实现;(3)在实际的使用中,与用户交互。在软件开发中,“本质”(Essence)的部分是构思这些概念上的结构;“偶然”(Accident)的部分是指它的实现过程。

软件的“本质”属性是我们对业务需求的抽象和设计,包含了一系列的流程,实体,规则,策略等等,反映了我们对业务需求的理解,是我们要实现出来的东西。软件的“偶然”属性是我们为了解决软件的“本质”问题,所“附带”要解决的问题,是实现过程中产生的问题。因此,我们可以把软件开发中的困难分为两类:本质上的困难是软件特性中固有的困难,偶然的困难是出现在目前生产中,但并非与生俱来的那些困难。解决软件生产力的问题,应该从这两个方面入手。更高级的编程语言,更方便的IDE,更丰富的组件库,这些都是在解决偶然困难。面向对象语言,SOA等内容涉及到了本质困难的解决,但仍然是困难的,因为要良好地实现它们本身就是有难度的。所以说,其实是没有银弹的,因为软件开发总是非常困难的。

软件开发的本质困难

Fred Brooks 认为软件开发的困难,来自于软件系统中无法规避的内在特性:复杂度、一致性、可变性和不可见性。

  • 复杂度。和很多现实中其它的设计工作(比如房子,汽车等)不同的是,软件是动态的,软件是状态与动作的结合,根据不同的状态要执行不同的动作,不同的动作又为导致更多不同的状态。如果说一辆汽车是三维的,那么软件系统可以认为是四维的,(当然,现在自动驾驶已经将汽车也变成了一个软件)。而且,随着系统中元素的增加,软件的复杂度的增加是非线性的。当系统大到需要多个团队协作时,信息的完整性,沟通和管理又成了新问题。

  • 一致性。即使是同一个开发者,间隔一段时间再看自己过去的代码,也经常一头雾水。更不用说那些运行了好几年,由多个业务系统组成的,换了好几拨开发人员的大型系统,要始终保持一致基本是不可能的,为了解决这些不一致,需要付出巨大的成本。

  • 可变性。业务流程本身是不断变化的,快速地响应变化,已经成为我们对现代软件的基本要求。相对于房子,汽车,人们普遍认为软件的变更是很容易的。但是,如果系统缺乏良好的可维护性,这个变化的成本就不可控了,你想象不到这里改一个枚举类型,可能关联多少个上下游的代码都需要改动。

  • 不可见性。软件是不可见的和无法可视化的,虽然我们现在已经有很多工具来表示软件的结构、流程、数据关系等内容。但是我们没有类似于建筑平面图一样直观的工具。

精准的预言

非常让人赞叹的是,Fred Brooks 在论文中指出了有些问题其实是可以被解决的,而很多方法正是我们现在的最佳实践,例如:

  • 购买而不是自行开发。构建软件最可能的彻底解决方案是不开发任何软件。这不就是我们现在的云计算模式?Iaas,Paas,Saas ,是自主开发,还是二次开发,还是拿来即用?

  • 需求精练和快速原型。通过原型让客户直观地看到软件,精确地沟通需求。这也已经是当近软件开发的基本环节之一了,我们还为此专门创建了一个产品经理的角色。

  • 增量开发。是的,三十多年前,Fred Brooks 已经在实践增量开发了。

  • 卓越的设计人员。无论什么样的开发模式,高素质的团队都是基础。当前开发工作越来越简单的背景下,高水平、深度的专业团队,似乎越来越难遇到。动辄微服务,敏捷开发。有些团队声称自己是敏捷开发,却默认任务是不可分解的,没有冲刺的概念,测试理念还停留在瀑布时代,也没有架构治理的理念,领域知识仅靠开发人员的悟性来保证。他们只是学会了每天开个早会、晚会,每周上线几个新需求。这比瀑布式或者迭代式还要危险,持续 1 年以上就会发现,团队的人都走光了,因为留下的这堆代码,他们已经没有信心再维护下去了。

我在2005年毕业成为了一名程序员,还记得 Visual Studio 2005 刚发布时,曾经激起了巨大波澜,自动化的报表,拉个控件就可以实现任意数据表的增删改查,大量的开发人员好像马上就会失业。那时侯 ASP.NET 似乎就是未来。然而今天搞 .net 的开发人员却成了小众。Web 2.0,移动互联网,大数据,人工智能,新的技术在不断地出现,新的软件需求也在不断涌现。软件渗透到了企业业务流程的方方面面,而不只是一个以CRUD为主的信息管理系统,好像每个公司都会变成软件公司。业务部门对软件的要求越来越高,必须能够快速地变化,因为客户随时会变,市场随时会变;必须足够可靠,系统停一分钟都可能造成巨大损失。软件还需要确保企业内的信息能够有效共享,数据的分析和挖掘,可以为业务发展提供重要方向。软件已经不是一个辅助工具,而是一个主导工具。软件的复杂度已经大大提升,软件开发复杂的本质所带来的挑战其实并没有改变。Fred Brooks 在将近50年前对软件复杂度的定义仍然有效。写代码是简单的,开发一个系统是不容易的。