没有银弹:软件工程的本质和事故(1987年)

2021-01-29 03:53:30

在充斥着我们民俗噩梦的所有怪物中,没有一个比狼人更恐怖,因为它们意外地从熟悉的变成恐怖。为此,人们寻找可以神奇地安息的银子弹。至少从非技术经理那里可以看到,熟悉的软件项目具有这种特征。它通常是无辜的,直接的,但是却有可能成为时间表,预算和产品瑕疵的怪物。因此,我们听到了急需解决的银弹,这使软件成本下降的速度与计算机硬件成本一样快。

但是,在展望十年后,我们看不到任何灵丹妙药。无论是技术还是管理技术,都没有一个单独的发展可以使生产率,可靠性和简单性提高甚至一个数量级。在本文中,我将通过检查软件问题的性质和建议的项目符号的性质来说明原因。

但是,怀疑论并不是悲观论。尽管我们没有看到惊人的突破-实际上,我相信这与软件的本质不一致-许多令人鼓舞的创新正在进行中。开发,传播和利用这些创新的纪律一致的努力确实应该带来数量级的改进。没有皇家之路,但有一条路。

疾病控制的第一步是用细菌理论代替恶魔理论和幽默理论。那一步,就是希望的开始,本身使所有神奇解决方案的希望破灭了。它告诉工人,要付出很大的努力才能逐步取得进展,必须对清洁纪律坚持不懈地坚持不懈。今天的软件工程也是如此。

现在不仅没有万能的灵丹妙药,而且软件的本质使它几乎不可能出现,因为没有任何一项发明能够为软件生产率,可靠性和简单性提供电子,晶体管和大规模集成所做的贡献用于计算机硬件。我们不能期望每两年看到两倍的收益。首先,必须观察到异常不是软件进度太慢,而是计算机硬件进度太快。自文明开始以来,没有任何其他技术在30年中使性能价格上涨了六个数量级。在任何其他技术中,都无法选择以提高性能或降低成本来获得收益。这些收益源于计算机制造从组装工业向过程工业的转变。

其次,要了解人们在软件技术上可以期待的发展速度,让我们研究一下该技术的困难。继亚里斯多德之后,我将它们划分为本质,软件性质固有的困难和事故,这些困难如今已经出现在软件生产中,但并不是固有的。

软件实体的本质是互锁概念的构建:数据集,数据项之间的关系,算法和功能调用。这个本质是抽象的,因为在许多不同的表示形式下,这种概念构造都是相同的。尽管如此,它还是非常精确和详尽的。

我认为构建软件的难点在于对​​该概念结构的规范,设计和测试,而不是对其进行表示和测试表示形式的真实性的劳动。当然,我们仍然会犯语法错误。但是与大多数系统中的概念错误相比,它们是模糊的。

如果这是真的,那么构建软件将总是很困难。本质上没有银弹。

让我们考虑一下现代软件系统这种不可简化的本质的内在属性:复杂性,一致性,可变性和隐形性。

复杂。软件实体的大小可能比任何其他人工构造的都要复杂,因为没有两个部分是相同的(至少在声明级别之上)。如果是的话,我们将两个相似的部分放入一个子例程中-打开或关闭。在这方面,软件系统与计算机,建筑物或汽车有很大的不同,在计算机,建筑物或汽车中,重复的元素很多。

数字计算机本身比人们构建的大多数事物都要复杂:它们具有非常多的状态。这使得难以构思,描述和测试它们。软件系统的状态数量级比计算机多。

同样,软件实体的按比例放大不仅是相同元素的较大重复,而且必然会增加不同元素的数量。在大多数情况下,这些元素以某种非线性方式彼此交互,并且整体的复杂性远远超过线性地增加。

软件的复杂性是必不可少的,而不是偶然的。因此,对抽象出其复杂性的软件实体的描述通常会抽象出其本质。在三个世纪中,数学和物理科学取得了长足的进步,它们构建了复杂现象的简化模型,从模型中推导了属性,并通过实验验证了这些属性。该范例之所以有效,是因为模型中忽略的复杂性不是现象的本质属性。当复杂性是本质时,它不起作用。

开发软件产品的许多经典问题都源于这种基本的复杂性,并且非线性随着大小的增加而增加。由于复杂性,团队成员之间难以沟通,从而导致产品缺陷,成本超支,进度延迟。复杂性带来了难以枚举的困难,更难以理解程序的所有可能状态,由此带来了不可靠性。功能的复杂性带来了调用功能的困难,这使得程序难以使用。由于结构的复杂性,很难在不产生副作用的情况下将程序扩展到新功能。从结构的复杂性来看,构成安全陷阱门的不可见状态。

不仅技术问题,而且管理问题也来自复杂性。它使概览变得困难,从而妨碍了概念的完整性。这使得很难找到并控制所有松散的末端。它造成了巨大的学习和理解负担,使人员更替成为灾难。

一致性。并非只有软件人才面临复杂性。即使在“基本原理”中,物理学也能处理极其复杂的对象。粒子水平。但是,物理学家坚定地相信,无论是在夸克中还是在统一场论中,都存在统一的原理。爱因斯坦认为,必须简化对自然的解释,因为上帝不是任性的或任意的。

没有这样的信念会让软件工程师感到安慰。他必须掌握的大部分复杂性是任意复杂性,是他的界面必须遵循的许多人类机构和系统所没有的押韵或推理。这些因接口而有所不同,并且有时因时而异,不是因为必要,而是因为它们是由不同的人而不是由上帝设计的。

在许多情况下,该软件必须符合要求,因为它是最新出现的产品。在其他情况下,它必须符合要求,因为它被认为是最符合要求的。但是在所有情况下,复杂性都来自于与其他接口的配置。仅通过重新设计软件就无法简化这种复杂性。

可变性。软件实体不断承受着变化的压力。当然,建筑物,汽车,计算机也是如此。但是,制造后的东西很少会改变。它们将由更高版本的模型取代,或者将实质性更改并入同一基本设计的最新序列号副本中。汽车的回叫确实很少。计算机的现场更改则少一些。与修改现场软件相比,两者的使用频率要低得多。

之所以如此,部分是因为系统的软件体现了其功能,而功能是最能感受到变化压力的部分。部分原因是软件可以更轻松地进行更改-它是纯思想的东西,可无限延展。实际上,建筑物确实会发生变化,但是所有人都知道,变化的高昂成本可以减轻变更者的异想天开。

所有成功的软件都会更改。有两个过程在起作用。首先,由于发现一种软件产品是有用的,因此人们在新的情况下在原始域的边缘或超出原始域的范围内尝试使用它。扩展功能的压力主要来自喜欢基本功能并为其发明新用途的用户。

其次,成功的软件可以保留其最初编写时所不能满足的正常使用寿命。如果不是新计算机,则至少要有新磁盘,新显示器,新打印机。并且软件必须符合其新的机遇。

简而言之,软件产品嵌入了应用程序,用户,法律和机器车辆的文化矩阵中。这些都在不断变化,它们的变化无情地迫使软件产品发生变化。

隐身软件是不可见的和不可见的。几何抽象是强大的工具。建筑物的平面图可帮助建筑师和客户评估空间,交通流,景观。矛盾和遗漏变得​​显而易见。机械零件的比例图和分子的条形图模型(尽管是抽象的)具有相同的目的。几何现实以几何抽象形式捕获。

软件的现实并非天生就嵌入在太空中。因此,它没有以土地具有地图,硅芯片具有图表,计算机具有连接原理图的方式进行几何图形表示的方式。尝试绘制软件结构图时,我们发现它并不构成一个,而是几个相互重叠的通用有向图。几个图形可以表示控制流,数据流,依赖性模式,时间顺序,名称空间关系。这些图通常甚至都不是平面的,更不用说分层了。实际上,建立对这种结构的概念控制的方法之一是强制执行链接剪切,直到一个或多个图形变为分层结构为止。 [1]

尽管在限制和简化软件结构方面取得了进步,但它们本质上仍然是看不见的,因此不允许头脑使用其某些最强大的概念工具。这种缺乏不仅阻碍了一个人内心的设计过程,而且严重阻碍了人心间的交流。

如果我们回顾过去在软件技术开发中最富有成果的三个步骤,就会发现每个步骤都攻击了构建软件的不同主要困难,但是这些困难是偶然的,不是必需的。我们还可以看到对每次此类攻击进行外推的自然限制。高级语言。当然,提高软件生产率,可靠性和简单性的最有力动力是逐步使用高级语言进行编程。大多数观察家认为,这种发展的生产率至少是其五分之一,并且在可靠性,简单性和可理解性方面也随之获得了进步。

高级语言有什么作用?它使程序摆脱了很多意外的复杂性。抽象程序由概念构造组成:操作,数据类型,序列和通信。具体的机器程序涉及位,寄存器,条件,分支,通道,磁盘等。在某种程度上,高级语言体现了抽象程序中人们想要的结构,并避免了所有较低层的结构,从而消除了程序中从未存在过的整体复杂性。

高级语言最多可以做的就是提供程序员在抽象程序中想象的所有构造。可以肯定的是,我们对数据结构,数据类型和操作的思考水平正在稳步上升,但一直在下降。语言开发越来越接近用户的成熟度。

此外,在某种程度上,高级语言的精心设计造成了工具掌握的负担,这种负担增加了而不是减少了很少使用深奥构造的用户的智力任务。

分时。分时带来了程序员生产力和产品质量的重大提高,尽管不如高级语言带来的那么大。

分时攻击的难度大不相同。分时保留了即时性,因此使人们可以保持对复杂性的了解。批处理编程的缓慢周转意味着,当人们停止编程并要求进行编译和执行时,不可避免地会忘记细节,即使不是很紧要。这种中断的时间成本很高,因为必须刷新一个内存。最严重的影响很可能是复杂系统中所有正在发生的事情的掌握力下降。

像机器语言的复杂性一样,缓慢的周转是偶然的,而不是软件过程的基本困难。分时的潜在贡献的限制是直接得出的。分时的主要作用是缩短系统响应时间。当此响应时间变为零时,在某个点它超过了人为注意的阈值(约100毫秒)。超过该阈值,将不会带来任何好处。

统一编程环境。 Unix和Interlisp,第一个被广泛使用的集成编程环境,似乎通过综合因素提高了生产率。为什么?

通过提供集成的库,统一的文件格式以及管道和过滤器,它们可以解决由于一起使用单个程序而导致的意外困难。结果,原则上原则上总是可以相互调用,提供和使用的概念结构实际上可以很容易地做到这一点。

这一突破反过来刺激了整个工具平台的发展,因为每个新工具都可以应用于使用标准格式的任何程序。

由于取得了这些成功,因此环境成为当今许多软件工程研究的主题。我们将在下一部分中讨论它们的承诺和局限性。

现在,让我们考虑最经常作为潜在子弹发展的技术发展。他们要解决什么问题-本质问题还是剩余的偶然困难?他们提供革命性的进步还是进步的进步? Ada和其他高级语言进步。最受吹捧的最新发展之一是Ada,它是1980年代的通用高级语言。 Ada不仅反映了语言概念的进化改进,而且确实体现了鼓励现代设计和模块化的功能。也许Ada哲学比Ada语言更先进,因为它是模块化,抽象数据类型和层次结构的哲学。 Ada过于丰富,这是对其设计提出要求的过程的自然结果。这不是致命的,因为子集化的工作词汇可以解决学习问题,而硬件的进步将使我们能够以便宜的MIPS来支付编译成本。推进软件系统的结构的确对于增加我们将要购买的MIPS确实是一个很好的用途。事实证明,在1960年代大声疾呼其内存和周期成本的操作系统是一种使用过去的硬件浪潮中的某些MIPS和廉价内存字节的绝佳形式。

尽管如此,Ada不会成为杀害软件生产力怪物的灵丹妙药。毕竟,它只是另一种高级语言,而从这些语言中获得的最大收益来自于第一次过渡-从机器的偶然复杂性过渡到更抽象的逐步解决方案陈述。一旦消除了这些事故,其余的事故将较小,而清除这些事故所带来的收益肯定会更少。

我预测从现在开始十年后,当评估Ada的有效性时,将会看到它产生了巨大的变化,但这并不是因为任何特定的语言功能,也不是因为所有这些因素的结合。新的Ada环境也不会成为改进的原因。 Ada的最大贡献将是切换到该应用程序时,可以培训程序员现代软件设计技术。

面向对象的编程。与当今的任何其他技术潮流相比,许多本领域的学生对面向对象的编程抱有更大的希望。 [2]我在其中。达特茅斯(Dartmouth)的马克·谢尔曼(Mark Sherman)在《 CSnet新闻》上指出,必须谨慎区分以该名称命名的两个独立的思想:抽象数据类型和层次类型。抽象数据类型的概念是,对象的类型应由名称,一组适当的值和一组适当的操作定义,而不是由其隐藏的存储结构定义。示例是Ada程序包(具有私有类型)和Modula的模块。

诸如Simula-67的类之类的分层类型允许人们定义通用接口,可以通过提供从属类型来进一步完善这些通用接口。这两个概念是troponline_one可能具有层次结构而没有隐藏和隐藏而没有层次结构。这两个概念代表了构建软件领域的真正进步。

每一个都消除了过程中的另一个偶然的困难,使设计人员可以表达设计的本质,而不必表达大量不增加任何信息内容的句法材料。对于抽象类型和层次类型,结果都是消除了更高级别的偶然困难,并允许了更高级别的设计表达。

然而,这些进步无非是消除了设计表达中所有偶然的困难。设计本身的复杂性是必不可少的,而且这种攻击不会改变任何东西。仅当仍然在我们的编程语言中使用的不必要的类型规范画笔本身只是设计程序产品所涉及工作的十分之一时,才能通过面向对象的编程获得数量级的增益。我对此表示怀疑。

人工智能。许多人期望人工智能的进步提供革命性的突破,这将使软件的生产率和质量得到数量级的提升。 [3]我没有。要了解原因,我们必须剖析“人工智能”的含义。

如今,有两种非常不同的AI定义被普遍使用。 AI-1:使用计算机解决以前只能通过应用人类智能才能解决的问题。 Al-2:使用一组特定的编程技术,即启发式或基于规则的编程。用这种方法研究人类专家,以确定他们用于解决问题的启发式方法或经验法则....该程序旨在解决人类似乎解决问题的方式。

第一个定义具有滑动性含义。...某些东西可以满足今天Al-1的定义,但是一旦我们看到程序的工作原理并理解了问题,我们就不会再将其视为Al ....不幸的是我无法确定该领域独有的技术体系。...大部分工作是针对特定问题的,需要一些抽象或创造力才能看到​​如何转让它。

我完全同意这种批评。用于语音识别的技术似乎与用于图像识别的技术几乎没有共通之处,并且两者均与这些使用有所不同。

......