《人月神话》第十六章阅读笔记:没有银弹
1 摘要
软件活动的根本任务:打造由抽象软件实体构成的复杂概念结构
软件活动的次要任务:使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言
除非次要任务占了所有工作的 9/10,否则即使全部次要任务的时间缩减到零,也不会给生产率带来数量级上的提高。
ps:软件生产率在近年内取得的巨大进步来自对后天障碍的突破,来自于对次要任务的改进,但是当前已经到了需要关注解决根本任务的时候了。
作者的建议:
仔细地进行市场调研,避免开发已上市的产品。
在获取和制订软件需求时,将快速原型开发作为迭代计划的一部分。
有机地更新软件,随着系统的运行、使用和测试,逐渐添加越来越多的功能。
不断挑选和培养杰出的概念设计人员。
2 介绍
1,在所有恐怖民间传说的妖怪中,最可怕的是人狼,因为它们可以完全出乎意料地从熟悉的面孔变成可怕的怪物。为了对付人狼,我们在寻找可以消灭它们的银弹。
2,近十年来,没有银弹的踪迹。没有任何技术或管理上的进展, 能够独立地许诺在生产率、可靠性或简洁性上取得数量级的提高。
3,解决管理灾难的第一步是将大块的“巨无霸理论”替换成“微生物理论”,它的每一步 ——希望的诞生,本身就是对一蹴而就型解决方案的冲击。它告诉工作者进步是逐步取得的, 伴随着辛勤的劳动,对规范化过程应进行持续不懈的努力。由此,诞生了现在的软件工程。
3 是否一定那么困难?——根本困难
1,不仅仅是在目力所及的范围内,没有发现银弹,而且软件的特性本身也导致了不大可 能有任何的发明创新——能够像计算机硬件工业中的微电子器件、晶体管、大规模集成一样——提高软件的生产率、可靠性和简洁程度。我们甚至不能期望每两年有一倍的增长。
2,我认为软件开发中困难的部分是规格化、设计和测试这些概念上的结构,而不是对概 念进行表达和对实现逼真程度进行验证。当然,我们还是会犯一些语法错误,但是和绝大多数系统中的概念错误相比,它们是微不足道的。如果这是事实,那么软件开发总是非常困难的。天生就没有银弹。
3.1 复杂度
1,规模上,没有任何两个软件部分是相同的(至少是语句的级别上)。如果有相同的情况,我们会把它们合并成供调用的子函数。软件实体的扩展也不仅仅是相同元素重复添加,而必须是不同元素实体的添加。 大多数情况下,这些元素以非线性递增的方式交互,因此整个软件的复杂度以更大的非线性级数增长。
2,软件的复杂度是必要属性,不是次要因素。因此,抽掉复杂度的软件实体描述常常也 去掉了一些本质属性。数学和物理学在过去三个世纪取得了巨大的进步,数学家和物理学家 们建立模型以简化复杂的现象,从模型中抽取出各种特性,并通过试验来验证这些特性。这些方法之所以可行——是因为模型中忽略的复杂度不是被研究现象的必要属性。当复杂度是 本质特性时,这些方法就行不通了。
3,上述软件特有的复杂度问题造成了很多经典的软件产品开发问题。
由于复杂度,团队成员之间的沟通非常困难,导致了产品瑕疵、成本超支和进度延迟;
由于复杂度,列举和理解所有可能的状态十分困难,影响了产品的可靠性;
由于函数的复杂度,函数调用变得困难,导致程序难以使用;
由于结构性复杂度,程序难以在不产生副作用的情况下用新函数扩充;
由于结构性复杂度,造成很多安全机制状态上的不可见性。
4,复杂度不仅仅导致技术上的困难,还引发了很多管理上的问题。
它使全面理解问题变 得困难,从而妨碍了概念上的完整性;
它使所有离散出口难以寻找和控制;
它引起了大量学 习和理解上的负担,使开发慢慢演变成了一场灾难。
3.2 一致性
1,物理学家坚信必定存在着某种通用原理,或者在夸克中,或者在统一场论中。爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专 横武断或反复无常的。
2,软件工程师却无法从类似的信念中获得安慰,他必须控制的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。它们随接口的不同而改变,随时间的推移而变化,而且,这些变化不是必需的,仅仅由于它们是不同的人——而非上帝——设计的结果。
3,某些情况下,因为是开发最新的软件,所以它必须遵循各种接口。另一些情况下,软件的开发目标就是兼容性。
3.3 可变性
1,软件实体经常遭受到持续的变更压力。部分的原因是因为系统中的软件包含了很多功能,而功能是最容易感受变更压力 的部分。另外的原因是因为软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展。
2,所有成功的软件都会发生变更。简言之,软件产品扎根于文化的母体中,如各种应用、用户、自然及社会规律、计算机硬件等等。后者持续不断地变化着,这些变化无情地强迫着软件随之变化。
现实工作中,经常发生两种情况。当人们发现软件很 有用时,会在原有应用范围的边界,或者在超越边界的情况下使用软件。功能扩展的压力主 要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们。
其次,软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比当初的计 算机硬件平台要长。即使不是更换计算机,则有可能是换新型号的磁盘、显示器或者打印机。 软件必须与各种新生事物保持一致。
3.4 不可见性
软件是不可见的和无法可视化的。几何抽象让矛盾突出,遗漏的地方可以捕获。但是软件的客观存在不具有空间的形体特征,因此没有已有的几何表达方式。当我们试图用图形来描述软件结构时,我们发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。这些图形可能描绘控制流程、数据流、依赖关系、时间序列、名字空间的相互关系等等。它们通常不是有较少层次的扁平结构。实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层 次化一个或多个图形。这种缺憾不仅限制了个人的设计过程,也严重地阻碍了相互之间的交流。
4 以往解决次要困难的一些突破
4.1 高级语言
高级语言所达到的抽象程度包含了(抽象) 程序所需要的要素,避免了更低级的元素,它消除了并不是程序所固有的整个级别的复杂度。
4.2 分时
分时解决了完全不同的困难。分时保证了及时性,从而使我们能维持对复杂程度的一个总体把握。
4.3 统一编程环境
它们主要通过提供集成库、统一文件格式、管道和过滤器,解决了共同使用程序的次要困难。这样,概念性结构理论上的相互调用、提供输入和互相使用,在现实中可以非常容易地实现。
5 银弹的希望
5.1 Ada 和其他高级编程语言
1,由于 Ada 采用的是抽象数据类型、层次结构的模块化理念,所以 Ada 理念可能比语言本身更加先进。
2,Ada 仍然不是消灭软件生产率怪兽的银弹。毕竟,它只是另一种高级语言,这类语言出现最大的回报来自出现时的冲击,它通过使用更加抽象的语句来开发,降低了机器的次要复杂度。一旦这些难题被解决,就只剩下非常少的问题,解决剩余部分的获益必然也要少一些。
5.2 面向对象编程
1,达特茅斯的 Mark Sherman 提出,必须仔细地区别两个不同的概念:抽象数据类型和层次化类型,后者也被称为类(class)。抽象数据类型的概念是指对象类型应该通过一个名称、一系列合适的值和操作来定义,而不是理应被隐藏的存储结构。
2,层次化类型,如 Simula-67 的类,是允许定义可以被后续子类型精化的通用接口。这两个概念是互不相干的——可以只有层次化,没有数据隐藏;也可能是只有数据隐藏,而没有层次化。两种概念都体现了软件开发领域的进步。
3,它们的出现都消除了开发过程中的非本质困难,允许设计人员表达自己设计的内在特性,而不需要表达大量句法上的内容,这些内容并没有添加什么新的信息。对于抽象数据类型和层次化类型,它们都是解决了高级别的次要困难和允许采用较高层次的表现形式来表达设计。
5.3 人工智能
5.4 专家系统
1,构建专家系统的必要前提条件是拥有专家。
2,专家系统最强有力的贡献是给缺乏经验的开发人员提供服务,用最优秀开发者的经验和知识积累为他们提供了指导。这是非常大的贡献。最优秀和一般的软件工程实践之间的差距是非常大的,可能比其他工程领域中的差距都要大,一种传播优秀实践的工具特别重要。
5.5 “自动”编程
1,大多数情况下所给出的技术说明本质上是问题的解决方案,而不是问题自身。
2,这样的应用具有非常良好的特性:
问题通过相对较少的参数迅速地描述出特征。
存在很多已知的解决方案,提供了可供选择的库。
在给定问题参数的前提下,大量广泛的分析为选择具体的解决技术提供了清晰的规则。
5.6 图形化编程
1,首先,如同我先前所提出的,流程图是一种非常差劲软件结构表达方法
2,其次,现在的屏幕非常小,像素级别,无法同时表现软件图形的所有正式、详细的范围和细节。
3,更加基本的是,软件非常难以可视化。
芯片设计是对两维对象的层次设计,它的几何特性反映了它的本质特性,而软件系统不是这样
5.6 程序验证
完美的程序验证只能建立满足技术说明的程序。这时,软件工作中最困难的部分已经接近完成,形成了完整和一致的说明。开发程序的一些必要工作实际上已经变成对技术规格说明进行测试。
5.7 环境和工具
它能带来软件生产率和可靠性上的一些提高。但是,由于它自身的特性,目前它的回报很有限。
5.8 工作站
一旦机器运算速度提高十倍,那么程序开发人员的思考活动将成为日常工作的主要活动。实际上,这已经是现在的情况。
6 针对概念上根本问题的颇具前途的方法
1,虽然现在软件上没有技术上的突破能够预示我们可以取得像在硬件领域上一样的进 展,但在现实的软件领域中,既有大量优秀的工作,也有不引人注意的平稳进步。
2,所有针对软件开发过程中次要困难的技术工作基本上能表达成以下的生产率公式:
任务时间 = ∑(频率)i×(时间)i
如果和我所认为的一样,工作的创造性部分占据了大部分时间,那么那些仅仅是表达概念的活动并不能在很大程度上影响生产率。
6.1 购买和自行开发
软件成本一直是开发的成本,而不是复制的成本。所以,即使只在少数使用者之间实现共享,也能在很大程度上减少成本。另一种看法是使用软件系统的 n 个拷贝,将会使开发人员的生产率有效地提高 n 倍。这是一个领域和行业范围的提高。
当然,关键的问题是可用性。
我认为,对于现在的很多组织机构来说,最有效的软件生产率策略是在生产一线配备很多个人计算机,安装好通用的书写、作图、文件管理和电子表格程序,以及配备能熟练使 用它们的人员,并且把这些人员散布到各个岗位。类似的策略——通用的数学和统计软件包,以及一些简单的编程能力,同样地适用于很多实验室的科学工作者。
6.2 需求精炼和快速原型
1,开发软件系统的过程中,最困难的部分是确切地决定搭建什么样的系统。概念性工作中,没有其他任何一个部分比确定详细的技术需求更加困难,详细的需求包括了所有的人机界面、与机器和其他软件系统的接口。需求工作对系统的影响比其他任何一个部分的失误都大,当然纠正需求的困难也比其他任何一个部分要大。
2,软件开发人员为客户所承担的最重要的职能是不断重复地抽取和细化产品的需求。
3,客户不知道他们自己需要什么。
4,在计划任何软件活动时,要让客户和设计人员之间进行多次广泛的交流沟通,并将其作为系统定义的一部分。这是非常必要的。
4,现在的技术中最有希望的,并且解决了软件的根本而非次要问题的技术,是开发作为迭代需求过程的一部分——快速原型化系统的方法和工具。原型的目的是明确实际的概念结构,从而客户可以测试一致性和可用性。
6.3 增量开发——增长而非搭建系统
1,很多年前,Harlan Mill 建议所有软件系统都应该以增量的方式开发。即,首先系统应该能够运行,即使未完成任何有用功能,只能正确调 用一系列伪子系统。接着,系统一点一点被充实,子系统轮流被开发,或者是在更低的层次 调用程序、模块、子系统的占位符(伪程序)等。
2,这种方法迫切地要求自顶向下设计, 因为它本身是一种自顶向下增长的软件。
6.4 卓越的设计人员
1,关键的问题是如何提高软件行业的核心,一如既往的是——人员。
2,优秀的设计是可以传授的。程序员的周围往往是最出色的人员,因此他们可以学习到良好的实践。
3,低劣设计和良好设计之间的区别可能在于设计方法中的完善性,而良好设计和卓越设计之间的区别肯定不是如此。卓越设计来自卓越的设计人员。软件开发是一个创造性的过程。完备的方法学可以培养和释放创造性的思维,但它无法孕育或激发创造性的过程。
4,尽管我强烈地支持现在的技术转移和开发技能的传授,但我认为我们可以着手的最重要工作是寻求培养卓越设计人员的途径。
5,如何培养杰出的设计人员?有些步骤是显而易见的:
尽可能早地、有系统地识别顶级的设计人员。最好的通常不是那些最有经验的人员。
为设计人员指派一位职业导师,负责他们技术方面的成长,仔细地为他们规划职业生涯。
为每个方面制订和维护一份职业计划,包括与设计大师的、经过仔细挑选的学习过 程、正式的高级教育和以及短期的课程——所有这些都穿插在设计和技术领导能力的培养安排中。
为成长中的设计人员提供相互交流和学习的机会。
评论