为什么说软件工程“没有银弹”?_风闻
code2Real-有人就有江湖,有code就有bug2022-01-14 10:06
《没有银弹:软件工程的本质性与附属性工作》(英语:No Silver Bullet—Essence and Accidents of Software Engineering)是IBM大型机之父佛瑞德·布鲁克斯所发表一篇关于软件工程的经典论文,原先是在1986年都柏林IFIP研讨会的一篇受邀论文,隔年电机电子工程师学会《Computer》也转载了这篇文章,他们用了几张《伦敦狼人》之类的电影剧照来当作说明,还加上了一段〈终结狼人〉的附注,用来引出非银弹则不能成功的(现代)传说。
该论述中强调由于软件的复杂性本质,而使真正的银弹并不存在;所谓的没有银弹是指没有任何一项技术或方法可使软件工程的生产力在十年内提高十倍。
所有软件创作都包括了本质性工作(essential task)和附属性工作(accidental task)。
前者是去创造出一种由抽象的软件实体所组成的复杂概念结构,后者则是用编程语言来表现这些抽象的实体,并在某些空间和速度的限制之下,将程序对应至机器语言。
若跟本质性的工作相比,软件工程人员所做的事,还有多少算是花在附属性的工作上呢?
除非附属性工作要耗费的心力超过全部工作的9/10,否则就算是将所有的附属性工作降至零,也无法将整个开发工作的轻松程度提升一个数量级。(零代码也不可能将附属性工作降低到零)
以往,软件生产力的重要进展绝大部分是来自于人为障碍的排除,像是严苛的硬件限制、难用的编程语言、上机时间(machine time)的不足等等,这些都是造成附属性工作益发困难的原因。
在该论述当中,讨论到了次要和必要复杂度的差异。
所谓次要复杂度是指由人们本身所产生的问题,而这类型的问题是可以被解决的。
譬如说,撰写和最佳化组合语言的复杂度就是属于次要的,它可以借由高阶程序语言来取代。
必要复杂度则是从软件本身要解决的问题衍生而来,并无法被移除。
如果软件需要提供三十个不同的功能,那么这三十个功能都是必要的,这些功能都必须被实作出来。
软件工程面临的问题在于我们已经清除了大部分的次要复杂度,而剩余的(主要复杂度)都无法改变。
在移除次要复杂度中最大的进展也许要算是高阶语言的诞生,像是Fortran和Java。
在欧洲中世纪的传说中,有一种叫“人狼”的妖怪,就是人面狼身。它们会讲人话,专在月圆之夜去袭击人类。
而且传说中对“人狼”用一般的枪弹是不起作用的,普通子弹都伤不到也打不死它,只有一种用银子作成的特殊子弹才能把它杀死。
Brooks在他最著名的随笔文章《No Silver Bullet》里引用这个典故,说明在软件开发过程里是没有万能的终杀性武器的,只有各种方法综合运用,才是解决之道。
(注:兵无常势,但是能运用一心的将军屈指可数,Brooks显然把多种方法的综合运用想得太简单了,一般人一种都用不好,更不用说多种方法综合使用。)
而各种声称如何如何神奇的理论或方法,都不是能杀死“软件危机”这头人狼的银弹。
他当时大胆声称并预言方法学家们10年之内(自1986年)绝找不到什么好的的神奇银弹(后来有人把时间延长到50年)。
他的文章发表后,被广泛引用,后来他的随笔结集成书《人月神话》。
从此,在软件界,银弹(Silver Bullet)成了一个通用的比拟流行开来。1975年所出版的《人月神话》—被称为软件工程圣经。
在Brooks看来,软件工程要面临的固有的不可避免的问题,主要是复杂性(complexity),软件整合(conformity),可变性(changeability)和不可见性(invisibility):
(1)复杂性(complexity)。软件要增加规模不仅仅是简单地增加相同内容的规模,还要增加新的内容,这就使得随着软件规模的增加其复杂度的增加是非线性的,整体复杂性的增加可能比线性增加要大得多。软件的复杂性的这个特征给软件的开发带来了不少的困难,它会给软件项目组里的组员之间交流带来困难,从而导致产品的瑕疵、开支过多和时间耽搁;这样的复杂性给我们穷举所有软件可能的状态带来了不少的困难,进而导致软件最后的不可靠性;各个功能模块之间的调用关系也会随着软件的复杂度的增加而变得复杂,这使得项目变得很难去使用;复杂的软件结构同样会使得软件的扩展性和安全性受到影响。
(2)软件整合(conformity)。一个软件往往是由不同的人一起开发出来的,因此软件开发者不得不设计出符合各种接口的软件来,这个困难往往随着时间变化,甚至随着不同的应用场合而变化。总的就是说软件整合是个不好解决的问题。
(3)可变性(changeability)。软件相对于其它的传统产品,要更容易修改,而且修改的频率要比传统的产品大;一个成功的软件必须要能够适应用户的各种需求,比如,有些用户就是喜欢去试探那些超出软件工作域的情形,软件开发者设计的软件必须要能够面对这些挑战而不崩溃,但这是一个不可能在一开始全部知道的,所以软件更新时软件发布后的“家常饭”。
(4)不可见性(invisibility)。软件不同于其它设计,软件没有空间性,所以具有不可见性的特点;有向图帮了软件工作者一个大忙,软件工作者可以用有向图来表示出控制流,数据流,模式的依赖,时间序列,命名空间的关系,但是尽管是这样软件开发还是存在内在的不可见性,这都将成为阻碍软件设计和各个软件工作者交流的“拦路虎”。
紧接着Brooks介绍了过去软件开发解决事故性困难的方法,
首先是高级语言(High-level languages)带来的方便性,高级语言给软件开发者带来的便捷式显而易见的,软件开发者不用再去关心低层的实现而只要在一个抽象的(相对与计算机)但更接近人类思维的层面上去设计软件;
其次就是分时(Time-sharing),只要分时的频率足够大,在人类分辨能力之上,那么就能取到很好的效果;
最后是统一的编程环境(Unified programming environments),统一的编程环境是的软件开发者都使用统一格式,这样便于软件工作者之间的交流。
那能否找到解决软件生产率“怪物”的银弹呢,Brooks就此展开了讨论。
(1)Ada和其它高级程序设计语言的发展。Ada只是另外一种高级程序设计语言,它不可能成为解决软件生产率“怪物”的银弹,只能是带来一种新的软件设计的技术。(Ada生不缝时,当时人们认为计算机性能紧缺,安全性可以放一放,现在Rust正在完成Ada未完成的事业)
(2)面向对象编程的方法。作者认为这也不可能成为解决软件成产率“怪物”的银弹,面向对象编程方法只能让软件开发者在定义接口时方便许多,而具体软件开发需要的复杂度并没有的到有效地降低。
(3)人工智能和专家系统。能够解决涉及相关领域的问题,但是在处理实际问题时,软件的规模是决定复杂度的主要因素。
(4)自动编程。这是一个诱人的技术,它从已有的解决问题的方法里找出解决要解决的问题的方法,然后生成代码;但是作者觉得这个扩展性不好,并且不能预见它在泛化方面的突破。
(5)图形编程。在上面作者已经提到了软件的固有属性里就有不可见性这么一条,因此很难用流程图去表达出所有的软件项目,况且到了软件的一定规模的话,这个图肯定会比较大,就现在的计算机屏幕大小来说,能显示出来的图形是有限的,因此图形编程也不可能解决软件生产率怪物。(现在零/低代码本质上回避了软件固有的复杂性)
(6)程序验证。首先程序验证并不能完全消除测试程序这一部分的工作,;其次,要建立一个程序规格(最难的部分软件的任务是到达一个完整和一致的规格),必须要求程序验证的完美性。这似乎不太可能。(哥德尔定理证明不可能有完美的验证理论)
(7)环境和工具。只能说方便了软件开发者,但不能实际的减小软件开发的复杂度。
(8)工作站。不能期待一个大的工作站就能简化软件开发的复杂度。
最后Brooks讲了下寻找银弹的前途。
主要有
* Buy versus build的讨论,其中成本是主要考虑的因素(转嫁问题给第三方);
* 要求完善和快速成型(Requirements refinement and rapid prototyping)是软件设计的基础工作,一开始就要设计好;
* 人是软件开发中最重要的决定因素,拥有强大的设计者(Great designers)将使软件开发效率提高好多,作者给出了培养Great designers的明显的步骤。
由于Brooks深厚的行业背景,即使说得如此浅白,还是有很多人看不懂。为此需要进一步分析:
Brooks从通过分析软件技术的困难,来推测软件技术到底能进步得多快。技术的难点分为两类:本质(困难);意外/伴随(困难)。
是软件内在的特性带来的困难,而不是那些不是本质困难、却伴随着软件生产过程中的困难。
然后我们来欣赏文章的浓缩摘要:
Fashioning complex conceptual constructs is the essence; accidental tasks arise in representing the constructs in language. Past progress has so reduced the accidental tasks that future progress now depends upon addressing the essence.
摘要中第一句话Fashion和constructs是重点
什么是fashion(verb):(尤指用手工)制作,使成形,塑造
什么是constructs:an idea or a belief that is based on various pieces of evidence which are not always true (根据不总是真实的各种证据得出的)构想,观念,概念
首先constructs指软件的设计构想,作者认为生成复杂的软件概念设计构想是一个创造的、艺术的过程,因此作者使用了这个动词。
这个动词很有灵气,因为它本意是用手工塑造,这是艺术创造的一个动作。
简单地说,复杂的软件的设计概念是(软件工程)的本质。
其次的第二句话,意外伴随的任务在用语言表示软件构想的过程中出现。
作者认为,设计软件的本质问题根本不是停留在各种编程语言上,在使用编程语言实现软件构想的过程中所绕的弯路(例如语法错误等等)都属于“意外伴随的任务”,这些任务并不是软件工程的本质。
本质是设计软件啊(当我们在谈软件时,我们在谈什么)!
数学家和物理学家通过简单的模型来解释复杂的现象,但是作者说软件具有内在的复杂性,软件如何通过简单的模型来解释?
当我们想表示软件时,我们需要一系列又臭又长的图表来表示软件:控制流,数据流,依赖,时序,名字空间的关系等等。对于软件,我们根本找不到大一统的原则(unifying principles),精简的解释(simplified explanation)。
因此作者人为软件工程难题最难的部分应该在于“specification, design, and testing of this conceptual construct, not the labor of representing it and testing the fidelity of the representation.”
详细阐述、设计、测试这个概念构想是软工的本质难点,并不是看你用语言对软件文档的完成度和准确度有多高(如果有人问你编了多少行代码,那他的水平一定高不到哪里去)。
我们过去所做的努力,都在减少意外的任务的发生;未来,我们应该着眼于解决软件工程的本质问题,由于近代以来的科学范式,人们总是寻找捷径,把低垂的果实先摘下,而没有“退而结网”思考如何摘更高的果实,因此科学陷入大停滞。
上面的解释仍然太专业了,下面换一个说法:
1. Complexity :软件有两重含义。软件被使用和软件被开发。假设需求全停了,软件的修改被静止下来。单纯去理解我们开发出来的软件是怎么工作的,是怎么给用户提供服务的,已经是非常复杂了。原因是 活动件(moving piece) 太多了,有太多的全局变量(数据库就是一个超级全局变量)在同时变化。
例如一个活动件有3个变化可能性,10个活动件则有10^3个变化可能性。100万行代码,除去注释和变量定义初始化部分,剩下的代码假设有50%,至少有500000^3个变化的可能性。
一个建筑系统经常需要建立在非活动部件之上,而非流沙之上,但软件就是构建在流沙之上。
2.Conformity :科学家相信这个世界是简单的。爱因斯坦的名言叫 God is not capricious or arbitrary。但是软件的本质复杂度就来自于所谓的业务逻辑。又有一句名言叫所谓的业务逻辑就是没有逻辑。
与没有逻辑的人和业务打交道真是虐了狗,由此我能体会社会系统的改造是多么艰巨。
3.Changeability :Successful software gets changed。只有无用的软件才会静止不变。现有的软件会不断被驱使去解决之前没有想到要解决的关联问题。
只要生活还在继续,变化就不会停止,问题就层出不穷,历史就不会终结(福山,你一定不是生活在地球上)。
4. Invisibility :最大的问题是不可见性。你无法给投资人讲解复杂度。也没无法证明有更简单的解决方案,也无法证明其他解决方案的商业价值。
软件的最大困难来自人类自己。
所有人都可以理解为什么不能把狗窝放大之后成为国家歌剧院。因为材料,因为力学。制约建筑结构的约束是可见的。而软件复杂度的约束是人的理解力。
取决于你可以一次读多少行代码,并记住。取决于昨天你写的东西,今天你有多少能记得。人的能力是复杂性扩展最大的约束。
问题是投资人无法理解整个结构的复杂,也无法理解人的理解能力其实就是软件的钢筋和混凝土。
在软件工程之外,还存在一系列困难,例如巴比伦塔问题。
巴比伦塔是一个任务很明确,工程量一定,没有技术难度,资源没有限制的工程。
但是它无法完成,有人认为原因是交流,我认为原因是熵。
熵是能量中不能做功的那部分。
任何概念要落实,必须涉及到具体技术,工程方法,组织架构。
每多增加一层要素,熵就增加一部分。
软件开发的熵,可以归纳为工程熵和组织熵。
采用不合适的技术方案,会增加熵,采用不合适的人员和组织,也会增加熵。
熵极大的结果是没有动能用来做功,所有的动能都做熵了。
比如大公司组织结构和流程复杂冗长,高P的大部分时间都用来开会——这种不直接做功的工作。
再严重一点,连会都开不完(美国国会不仅会议冗长,而且经常filibuster)。
就更没真实效能输出了——那会也白开了。
大部分大企业病的根源是这个——复杂、高耦合,高熵。
解决工程熵和组织熵的办法是
1,尽量用简单直接的技术方案,
2,用牛人,用简,少的组织结构。
分工明确人月神话中讲的解决办法,交流,用成本低的交流方式,比如开小会,尽量开面会……等都是这个原则的应用。
当然,最彻底的,最高效的方式就是“不交流”或者接近零交流,书中讲的例子是“手术刀团队“。类似的好像还有康威定律——交流会损害企业的效率。道理是一样的。