comments:这个文章年代久远,1987年的文章,有些句子理解不了,但不妨碍该文成为软工领域的问题进行哲学思考的源头,Frederick P. Brooks, Jr. 对软工的问题给出了深刻的思考和建议,对现有软件工程领域的问题和解决方案进行了深入的探讨,至今各种问题和解决方案也是学术界热门的研究内容,因此用了几乎近一周的时间对其进行了全面的翻译,但有些问题理解不够,只能如此了。
在出现在我们梦魇中的那些民间的传说中的怪兽里,最让人害怕就是狼人,因为这些怪兽可以从你很熟悉的东西毫无征兆的变为一个恐怖的怪兽。因此,大家希望能够找到能魔法般的让这些怪兽安息的银弹。
我们熟悉的软件工程,至少从非技术经理看来具有狼人的某些特征,它经常是无辜且简单的,但能够变成一个超出进度、无边的预算,充满错误的产品怪兽。所以,我们听到人们对银弹的渴求:这个银弹能够让软件的成本像计算机硬件一样能快速下降。
但是,当我们回顾过去的十年,我们发现没有银弹。不管是开发技术还是管理技术,没有一个能够哪怕从一个维度能够提升软件的生产率、可靠性或者简化软件生成过程。在这个文章中,我将会从软件问题的本质和我们提出的银弹的属性两方面来说明为什么没有这样的银弹。
怀疑不是悲观,尽管我们在这方面还没有重大的突破,但实际上我认为这和软件的本质是不一致的,很多创新正在进行。持续不断对此进行研究、传播并利用这些创新必然会从对这个学科有某种程度上的提升。这里没有康庄大道,但还是有路的。
管理这个问题的第一步就是用基因理论替换恶魔理论和幽默理论。这一步,才是希望的开始,破除了所有魔法般的解决方案。它告诉人们,这个过程必须一步一步的,付出巨大的努力,需要对学科的纯洁性付出持续不懈的努力。现在软件工程也需要这种努力。
这个过程必须这么艰难么?——本质困难
不仅仅目前看来没有银弹,从软件的本质上来看好像不会有一种发明能够像电子,晶体管和大规模集成电路提高计算机硬件产生的影响一样提升软件的生产率,可靠性,简化生产过程。我们都无法期待每隔两年能够在这两方面同时的进展:
首先,我们必须看到这个奇怪的现象不是软件的发展过程太慢,而是计算机硬件的发展过程太快。自从人类文明开始以来,没有一个科技能在30年获得6个数量级的性价比提升。没有一个科技能够同时提高性能并降低成本,这种进步的获得从计算机制造商流向组装工业再流向加工工业。
其次,想要知道软件技术进步速度,首先我们需要看看软件技术里面存着的困难,按照亚里士多德的理论,我将这些困难分为:本质困难—这些困难是由软件本质所决定的固有困难,附属困难—这些困难伴随着产品,但不是本质问题。
软件实体的本质是数据集,数据项之间的关系,算法和函数的调用这些概念之间的构造的内锁关系,这个本质是抽象的,并且有多种不同的表述,但却是非常精确并且有着丰富的内涵。
我认为构建软件真正困难的部分在于:软件规格说明,设计和测试所构建的概念,不是展现这些概念所做的工作和测试这个展示的正确性。我们仍然会有一些语法错误,但对大多数系统来说,和概念错误相比这都不算什么。
如果这是正确的,构建软件总会是困难的,这里本质上就没有银弹。
现在我们考虑这个现代软件系统无法减少的本质所具有的内在属性:复杂性,一致性,易变性和不可视性。
复杂性:软件实体比人类构造的其他物品更为复杂,因为软件里没有任何两个部分是相似的(至少在语句级是这样),如果他们相似,我们可以制作两个相似的部分作为子程序,一个打开一个关闭。在这个方面,软件系统和计算机,建筑或者汽车这些存在大量重复元素的产品有着巨大的差别。
数字计算机比人类建造的大多数物品更为复杂:它有巨大的状态空间,这就对如何构想,描述和测试这些空间造成了困难,软件系统比计算机硬件多一个数量级的状态空间。
同样的,同比例放大的软件实体不仅仅是同样的元素进行堆砌成的更大的软件,它必然是增加了大量的不同的元素。在大多数情况下,元素之间的交互以一种非线性的方式进行,而其整体复杂的增加更是一种非线性的方式。
软件的复杂性是一种本质的特性,不是附属特性。因此,通过抽象去掉软件的复杂性来描述软件实体往往去掉了软件的本质。三个世纪以来,数学和物理学通过构建简单的模型来描述复杂现象取得了巨大的进步,将复杂现象的属性和模型分类,并通过实验来验证这些属性。这种方式能够成功因为模型所简化的这些复杂性不是现象的本质特性。如果这个复杂性是本质的话这种方法就不能成功。
很多在软件开发中的经典问题的根源都是其本质的复杂性和其复杂度与软件规模之间的非线性增长关系。这种复杂性导致了团队成员之间沟通的困难,导致了软件的缺陷,成本的超值,进度的延迟;这种复杂性导致了对程序所有状态空间计数和理解的困难。函数的复杂性导致了函数调用的困难,这就让软件难以理解。结构的复杂性导致了很难为已有的程序增加新功能而不增加副作用。结构的复杂性还导致了无法看到构成安全窗口的状态空间。
不仅仅是计数问题,复杂性还导致了管理问题。复杂性导致无法对软件有个整体的视角,这就阻碍了概念的完整性。这就让找到和控制所有的没有交代清楚的部分变得十分困难。复杂性造成了巨大的学习和理解的负担,这让个人转向了一场灾难。
一致性。在面对复杂性的时候,软件人不是一个在战斗。物理学家处理非常复杂的对象,甚至是出于“基础”粒子级别的对象。物理学家有着坚定的信仰,他们认为一定会发现统一的原理,不管是在夸克还是在统一理论。爱因斯坦认为自然一定有简单的解释,因为神不会是任性或者是随意的。
但对软件工程师来说却没有这样的信仰。他们必须处理的大多数复杂性都是随意的,他们的接口必须遵守那些没有节奏和推理的由各种人类习惯或者系统所随意制定的规则,这些接口互不相同,随着时间的变化而变化,并且并不是说这些变化是必须的,而仅仅是因为这是由不同的人所设计的,而不是神。
在很多情况下,软件必须一致,因为这些软件刚刚面世,另外一些情况是由于只有这样才是最符合要求的。但在大多数情况下,大多数复杂性来自于对其他接口的一致性;这种复杂性无法通过仅仅对软件进行重新设计而简化。
可变性。软件实体一直处于变更的压力下,当然,建筑,汽车,电脑都是。但是制造业的物体在生产出来之后再进行变更不是那么频繁,他们将会被后面的模型替代;或则重要的变更会放进后续的具有同样基础设计的产品序列中。汽车产品的召回实在是非常的偶然;电脑产品的变化更小。这两者的变化频率都远小于对软件产品的修改。
从某种意义上讲,这是由于系统的软件体现了其功能,而功能又是变更压力最大的部分。另一方面,软件也是最容易改变的,它是纯粹思维的产物,具有无限的可扩展性。建筑确实也在变化,但众所周知,其高昂的变更成本阻碍了那些突发奇想相应变更它的人。
所有成功的软件都发生了变化。两个过程都在发生。首先,一个软件产品如果很有用,人们就想在超过其应用领域或者其边界尝试新的案例。对已有软件扩展新功能的主要压力来自于那些喜欢这个软件的基本功能的人,并希望能发掘软件的新用途。
其次,成功的软件的存活期远超过它第一次所运行的机器设备的寿命,如果不是换一个完全的新电脑,至少会换一个新的磁盘,或者新的显示器、打印机。而软件必须和这些可能换上的新设备保持一致。
简单来说,软件产品嵌入在应用的文化模型里,用户、规则、机器设备。这些都在一直发生变化,而这些变化不可避免的给软件产品带来了变化的压力。
不可视。软件是不可见的,并且也是不能可视化的。地理信息抽象(Geometric abstractions)是一个功能强大的工具。其建筑的开发计划能够帮助架构师或者客户评估空间,交通流量,视野,这样存在的矛盾和忽视的问题变得显而易见。
画出机械构建的草图或者分子结构模型通常也是基于同样的目的。一个空间实体能够通过空间的抽象得到。
而软件的实体并不是嵌入在空间里,因此,它无法像建筑物那样出现在地图里,硅片那样有自己的电路图,计算机有连接电路图。一旦我们尝试去画出软件结构,我们就会发现,这不仅仅是一个,而是多个图,通常是一个图叠放在另一个图上。有些图可能会展现控制流程,数据流,依赖模式,时间序列,命名空间关系,这些图通常不是平的,也不是分层的。事实上,在这个结构上构建概念控制的方式往往是强行切断其中的连接直到这些图变成层次化的。
尽管我们一直在约束并简化软件的结构,但其本质上还是不能可视化的,因此思维无法利用强大的概念工具。这种缺陷不仅阻碍了在一种思维的了设计的过程,并且严重的阻碍了思维之间的交流
解决附属困难的一些突破
如果我们检查在过去非常成功的软件科技发展的三个步骤,我们会发现每个都解决了在构建软件过程中的不同的主要问题,但是这些问题是附属的,不是本质困难。我们可以通过分析看到对这些解决方法所存在的本质约束。
高层语言。采用高层语言极大的提升了软件的生产率,可靠性,简化了软件的生产过程,多数人认为采用高层语言至少提升了五倍的的软件生产率以及相应的在可靠性,简化软件生产过程和易理解性方面的提升。
高层语言究竟完成了什么事呢?它将程序从其自生所带来的大多数附属复杂性解放出来。一个抽象程序包含概念的构想:操作,数据类型,序列和通信,而实际的计算机程序表达的是比特,寄存器,条件,分支,通道,磁盘以及类似的东西。从某种程度上看,高层语言体现了抽象语言的概念,同时又避免了采用低层语言所涉及的结构,它消除了一个层次的复杂性(低层的复杂),使得这种复杂性永远不会在程序中出现。
高层语言能做的是完成程序员在抽象程序中所想象的所有构想,当然,我们对于数据结构,数据类型和操作的思考层次在稳步上升,但是处于一个不断下降的过程,并且对于用户来说,开发语言的复杂性在越来越复杂。
更多的,从某种意义上来讲,高层语言的发展造成了掌握工具的负担,这就增加了而不是减少了那些并不会用到复杂概念的用户的智力工作。
分时。分时大大的提升了程序人员的生产力,提升了产品的质量,但是没有高层语言所带来的提升大。
分时解决了一个非常不同的困难。分时保持了对事物的关注,因此允许人们保持对复杂性的全面掌握。批处理程序如此长的周转时间意味着:在编译和执行的时间里,如果一个人不是那么的投入的关注他之前考虑的内容,就不可避免的会遗忘细节。这种中断非常耗时间,对一个人来说,他必须刷新他的记忆。最严重的影响可能就是影响对复杂系统中正在进行的事件的跟踪。
慢周转,类似机器语言的复杂性,是软件过程的附属困难而不是本质困难。因此分时多带来的潜在贡献的限制也就出来了:分时的主要影响就是降低系统响应时间,随着这个时间不断趋于0,从某种意义上来讲,这超过了人们可以观察的时间的门槛,大概是100ms,超过这个门槛后,就没有什么益处了。
统一语言环境。Unix和Interlisp,第一个广泛使用的集成语言环境,看起来提升了一个层次(intregral factors)的生产率,为什么呢?
它通过提供整合的库,统一的文件格式,管道和过滤器,解决了由使用个人编程工具所带来的一系列困难,因此结果就是让所涉及的概念结构随时可以调用,并且其他人也可以很简单的用于实践。
这个突破促进了整个工具环境的开发,因此,每个新工具都能够应用到使用标准格式的程序中。因为这些成功,研发环境成为现今大多数软件工程研究领域的目标,我们将会在下一章中分析其解决的问题和限制。
银弹的希望。
现在让我们来考虑目前那些最有可能成为银弹的新技术,这些技术解决了哪些问题,是本质问题还是其他的附属问题?他们带来了革命性的进步还是增量的进步?
Ada和其他高级语言。目前讨论最多的就是Ada,一个在1980年代通用的高层语言。Ada不仅仅反映了在语言概念上的进化发展,实际上其体现了鼓励现代设计和模块化的特征,可能Ada哲学比Ada语言本身更为先进,Ada的哲学包括模块化,抽象数据类型,继承结构。Ada是丰富的,是其过程的自然结果,其需求基于设计。这个不是主要的,因为工作字典子集可以解决学习的问题,硬件的发展可以带来廉价的MIPS来支付编译的成本。先进的软件系统的结构事实上对我们所购买的MIPS的很好的利用。操作系统,1960年代因其内存和周期成本被广泛谴责,但是随着MIPS和内存价格的下降,这种形式已经被证明是相当不错的形式。
尽管如此,Ada也无法成为那颗能够让软件生产率怪兽倒下的银弹。毕竟它只是另外一个高层语言,其最大的结果来自第一次转化:将机器的附属复杂性转化为更抽象的一步一步的解决方案的语言,一旦这个附加属性被移除了,剩下的贡献就很小了,其收益也会更少。
我预言在从今而后的10年,在对Ada语言的效率进行评估后,Ada将会显出其明显的不同,不是因为其特殊的语言特征,也不是因为这些特征的组合,同样,不会有新的Ada环境会带来新的提升。Ada的最大贡献将会是其在不知不觉中训练程序员的现代软件设计技术。
面向对象编程。相对于现在很多技术潮流,学生们对面向对象编程技术抱有更多的希望,我也是其中之一。达特茅斯的Mark Sherman在CsNET新闻中强调,我们必须小心的区分两个概念:抽象数据类型和继承类型。抽象数据类型的概念是一个对象的类型应该由对象的名称,一系列相关的属性和相关的操作组成,而不是由其存储的结构组成,其存储的结构应当隐藏起来。Ada的包(包含私有类型)和Modula的模块就是这个概念。继承类型,如Simula-67的说明,允许定义通用的接口,该接口可以被子程序进一步定义。这两个概念是正交的,一个可以是继承但不隐藏,一个是隐藏但不继承。两个概念都代表了构建软件中的先进技术。
每个技术都消除了软件过程中的附属困难,允许设计人员在无需表达大量没有信息内容的语法材料的情况下表达设计的本质。不管是抽象类型还是继承类型,结果都是消除高层的附加属性并且允许对设计进行更高层次的表达。
尽管如此,这样的进步仅仅是消除了在进行设计表达时的附加困难。设计本身的复杂性才是本质,这些进步并没有能够解决这个本质问题,只有在我们设计一个产品时会由9/10的工作还在使用那些不必要的类型说明,面向对象编程才获得一个数量级的提升。
人工智能。很多人都期待人工智能技术的提升能够为软件生产率和生产质量的提升提供一个革命性的突破。我不这么认为。为什么呢,我们必须看看什么叫做“人工智能”。
D.L.Parnas澄清了关于术语定义的混乱:“现在对AI有两个不同的定义,AI-1:用计算机解决那些以前只能利用人类智慧才能解决的问题;AI-2:利用一种特殊的编程技术,例如启发式的或者基于规则的编程技术,在这种方式里,需要对人类专家在解决问题时使用了哪些启发或者规则进行研究,而程序则设计为像人类那样去解决问题。第一种定义有不确定的意义,现在有些东西可以满足AI-1的定义,但是一旦我们知道这些程序是如何理解并工作的,我们就再也不会认为这是AI了....但是不幸的是我没法给出这个领域独有的软件,大多数这些工作都是针对特定问题的,并且要求通过对问题的抽象和变化来看如何解决这一类问题。“
我完全同意这个观点,语音识别技术和图像识别技术看起来有很大不同,并且和专家系统中所用技术都不相同。我觉得我很难看到图像识别技术和通常的编程实践有多大的不同,这对于语音识别技术也是一样。构建软件最难的是决定想要表达的内容而不是表达本身。表达的方式只能给出最低限度的提升。专家系统技术,AI-2值得我们用一个章节来讨论。
专家系统。人工智能最先进的部分并且也是应用最广泛的部分是构建专家系统的技术。很多软件科学家都在努力将这种技术应用到软件构建环境中。那这是怎样的概念,他所倡导的是什么呢?
专家系统是一个包含了一个归纳推理引擎和一个规则库,通过获取输入数据和假设,通过基础规则给出推理,为用户提供结论和建议,并能够向用户解释其推理过程。相对于确定的逻辑,典型的推理引擎可以处理模糊或者概率数据和规则。
这样的系统为对同一个问题给出同一个解决方案的程序算法设计,给出了一些明显的优势:
- 推理引擎技术是独立于应用的情况下进行开发的,然后应用到多个用户,每个人都可以通过推理引擎验证很多事。因此,这种技术是非常先进的。
- 应用相关的易变部分的材料以一种统一的方式编进规则库里,并且为规则库提供开发,变更,测试和生成说明文档的工具,这种调整主要是由于应用的复杂性导致的
这个系统的强大不是由于其强大的推理机制,而是由于其非常丰富的规则库,这些规则能够更精确的反应真实世界。我相信这种技术最先进的是将应用的复杂性和程序中分离出来。
这种技术如何应用到软件工程的任务里呢?有很多种方式:这种系统能够提供接口规则,给出测试策略的建议,记住bug类型的频率,给出优化的提示。
比如,考虑一个设想的测试顾问,在最初级的形式,专家诊断系统类似飞行员的检查单,仅仅列举出对可能会导致麻烦的事情的建议。随着越来越多的系统结构纳入到规则库里,规则库对所报告问题特征的评估越来越精确,测试顾问对专家系统生成的假设和其推荐的测试越来越挑剔,这样的一种软件系统和传统的基于规则的专家系统有明显的不同,传统的专家系统的规则库可能是根据不同的软件产品进行设计,因此其规则库是继承和模块化的,随着产品模块的修改,规则库的模块也必须修改。
这种专家系统要求生成诊断规则,其实就是要求不管通过什么方式对模块还是系统生成一系列测试用例,如果这个要求能够通过适当统一的方式,为规则制定统一的结构,并有一个好的推理引擎,这就有可能实际降低生成测试用例的工作,帮助整个生命周期的维护和对修改的测试。同样的,也可以建设其他的顾问,在其他软件构建任务中。
在实现一个对程序开发人员有用的专家系统顾问的过程中存在很多困难。可以想象到的一种重要的场景就是我们如何简单的从程序结构说明自动或者半自动的生成诊断规则,更为困难并且重要的是关于知识获取的两个方面:(1)找到具有良好表达能力,且能够自我分析自己为何这么做事情的专家;(2)开发有效的技术来抽取这些专家所知道的内容并将其提炼到规则库中。构建一个专家系统的重要前提是首先得有个专家。
专家系统最重要的贡献当然是为没有经验的程序员服务并且帮助收集最好的程序员的智慧。这可不是小的贡献。最好的软件工程实践和普通的实践之间的差距是非常大的,可能比其他工程要大的多,如果有个工具能够传播好的实践将会是非常重要的。
“自动化”编程。近40年来,人们期待并讨论“自动化编程”或者说依据对问题的描述直接生成一个解决该问题的程序。有些时候他们期望这种技术能够提供下一个突破。
Parnas指出,这个词是用来诱惑人的,不具有语义上的内容,他认为:“简单来说,自动化编程通常是用高层语言进行编程的一个委婉的说法,而这种高层语言已经在用了”
他认为,本质上,在大多数情况下,这是个解决方案,而不是问题本身,因此需要给这种解决方案下个定义。
你可以发现例外,建筑自动生成技术非常强大,它对于程序化排序最为擅长。一些整合偏微分方程的系统允许对问题进行直接描述,系统会对参数进行评估,从方法库中选择解决方案,并且生成程序。
这些应用有很好的属性:
(1)这些问题已经能够通过相关的参数进行刻画;
(2)有许多著名的解决方案来提供这样的备选解决方案库;
(3)可以通过对给定的问题参数进行扩展分析来得到选择解决方案技术的明确规则
但是很难能够看到这样的技术能够应用到普通的软件系统上,那些有着明确属性的情况除外。很难想象这种生成技术的突破如何发生。
图形化编程。软件工程领域博士生的最爱的主题就是图形化,或者可视化编程,将应用计算机图形学应用在软件设计上。有时候,应用这种方式的前景通常和分析VLSI设计来进行对比,确实,在芯片设计领域计算机图形学起到了很大的作用。有时候理论学家认为流程图可以作为一个理想的程序设计工具并为此提供了强大的支持来构建这个工具。
但是这些工作没有什么说服力,也没什么令人激动的地方,我认为未来也不会有。
首先,我在其他场合也说过,流程图是对软件结构的一个非常简单的抽象,事实上,最好将其看做Burks,Von Neumann,和Goldstine在尝试为他们设想的计算机提供一个急需的高层控制语言。现在流程图已经通过多页,连接的盒子进行了细化,但对于程序设计人员仍然是一个无用的工具,程序人员在程序完成后画出流程图,而不是在写之前。
其次,现在的屏幕从像素上来说太小了(换个大屏很有必要!^_^),无法显示软件图中关于范围和解决方案的重要细节。现在工作站“桌面”的概念替换了“航空座椅”的概念。任何坐在两个发福的乘客中间,并将报纸摊在腿上的人可以看到这之间的区别:你每次只可以看到一少部分的内容。真正的桌面提供了一个对很多页的概述,并且可以随机看到。更多的是,当随着创造力的活动越来越强,越来越多的程序人员或者作家开始抛弃桌面转而投向更宽敞的地面。硬件技术应该快速发展以适应我们的可视范围能够满足软件设计的任务。
更重要的,像我之前说的,软件很难被可视化。不管是一个控制图,变量范围簇,变量交叉引用,数据流,集成数据结构以及其他类似的,都感觉对于软件这头大象来说只有一维的错综复杂的内锁关系。如果强制要求所有的图必须由各种相关的视图生成,那么就很难抽取出一个全局的视图。对VLSI的分析是一个根本上的误导—一个芯片的设计是在一个二维的空间中进行描述的,它的地理位置反应了它在三维空间的实现。但软件不是。
程序验证。现代开发活动中有很大一部分工作是在测试和修复bug。那有没有一颗银弹能够将错误消除在源头,消除在系统设计阶段?能不能通过遵守一个非常不同的策略,使得在投入巨大的精力进行实现和测试之前,首先验证设计正确性,从而极大的提升软件的生产率和可靠性?
我不认为我们在这里会发现生产率魔法,程序验证是一个很大的概念,对于类似安全操作系统内核的产品非常重要,但这种技术不能节省劳动力。验证更是这样一种活动,只有少部分重要的程序才进行验证。
程序验证不是错误证明过程。这里没有魔法,数学证明也可能是错的。所以,尽管验证可能会降低程序测试的负担,但不能消除测试。
更严重的是,即使完美的程序验证也只能证明系统符合他的规格说明。而软件任务最难的部分是获得一个完整,一致的规格说明,构建一个程序的绝大多数本质在于调试这个说明。
环境和工具。通过研究更好的开发环境能够获得多大的生产率提升?本能反应就是已经解决了的并产生最大成果的问题—文件系统架构,统一文件系统格式保证了程序接口的统一性,以及生成的相关工具是第一解决这个问题的。正在开发还没有广泛使用的针对特定语言的智能编辑器(很好使!)能够将程序人员从语法错误和简单的语义错误中解放出来。
可能从开发环境中获得的尚未被意识到的最大的成果就是使用了一体的数据库系统来追踪那些程序人员必须继续精确回忆的无数细节,并为一组协作人员对系统保持最新的了解。
当然,这个工作是值得的,并且在生产率和可靠性上会获得提升,但是从其本质上来讲,其回报必然是递减的。
工作站。通过增加个人工作站的计算和存储能够为软件开发带来什么提升呢?多少MIPS能够满足使用的要求?现在计算机的计算速度已经能够满足人们同时对程序和文档的要求。但在机器速度中的10个因素中只有有1个因素留给程序员们思考主要的活动。事实上现在已经是这样的。
我们当然欢迎更加强大的工作站,但无法期待魔法般的提升。
对本质问题的解决方案
即使没有一种技术能够给出类似硬件领域生产率那样魔法般的突破。但是这里还是有大量的工作正在进行,并且处在一个稳定的进展中。
所有的解决软件过程的附属问题的技术从根本上由生产率方程所约束:
time of task =sum(frequency[i]*time[i])
如果任务的概念组件占用了绝大多数的时间,这样不表达概念组件的其他活动不管有多少页不能获得较大的生产率的提升。
因此,我们必须考虑那些解决了试图解决软件问题本质的方法,并且确切的表达这些复杂概念的结构。幸运的是,有些方法非常有希望。
买和自己开发。构建软件最激进且可能的方法就是完全不去构建这个软件。这种事情随着越来越多的投资人提供更多且更好的令人眼花缭乱的应用,变得越来越简单。当软件工程师还在生产方法上进行努力的时候,个人计算机的革命已经创造了大量的软件市场。每个杂志铺放着由这机器类型,广告进行分类的月刊杂志,这些杂志上给出了这些从几美元到几百美元不等的软件产品的介绍。更专业的软件为工作站和Unix市场提供更强大的产品。但即使可以买到现成的软件工具和环境,我在其他地方也倡导建立一个为个人而建立的软件市场(APP STORE!视野多么开阔)。
所有这些软件都比重新开发一个便宜。即使价值100,000美元的软件,所购买的软件的部分的价格仅仅只有一个人年,并且交付是立即的。至少从是产品是实际存在的意义上来讲是立即的,产品的开发人员认为产品的用户是欢乐的。这种产品具有更好的文档并且比手工作坊生产出来的软件具有更好的可维护性。
建立大规模软件市场在软件工程领域我认为是一个重要的长期趋势,软件的成本一般都是开发成本,不是复制成本。将成本分摊到即使少部分用户也会极大的降低单用户成本。从开发人员的角度来看,一个软件的n个复制相当于提升了软件开发人员的n倍的生产率。这个对学科和国家来说都是一个重要的生产率提升。
问题的关键在于:是否可行。我能够用一个现成的软件包来完成我的工作?一个这里发生过一个令人惊奇的事情,在1950年代到1960年之间,很多研究指出,人们在工资,库存控制,应收账款管理等领域不会使用现成的软件。这些需求都是特定的,每个案例和每个案例之间的差异巨大。但在1980年,我们发现这种软件需求量非常大,并且被普遍使用。是什么发生了变化?
绝对不是软件包发生了变化,肯定是某些东西变得更为一般或者比之前更容易定制了,但也没有那么多。如果是什么的话,应该不是应用,相比20年前现在业务和科学变得越来越多样化并且复杂。
一个重要的变化是硬件软件价格比率。1960年,一个买了200万机器的人任务他们可以支付250,000美元的来定制一个工资程序,这个程序能够简单无缝的放入计算机环境里。现在用50,000美元买的办公室机器不能承担一个自定义工资程序的价格,因此他接受了采用已有的工资系统。计算机现在很普遍,因此自然就接受了这种方式。
对我关于软件在多年里并没用发生太大的变化这个论点,这里有些例外情况:电子程序表格和简单数据库系统。这些强大的工具,事后看来,很明显会面对无数的用户,一些还非常另类的用户。有很多文章和书都在讨论利如何用电子表格解决这些另类任务。之前大量的用Cobol或者Report Program Generator定制的程序现在都由这些工具来完成了。
每天,很多用户都在通过很多应用程序操作他的电脑,但却没用写一行代码。事实上,很多用户都不会写代码,但是他们却能很熟练的利用电脑解决他们的问题。
我认为对多数组织而言,能够极大的提高生产率的策略就是给完全不懂电脑的一线员工配备个人电脑以及优秀的写作,绘图,文件,电子表格程序,然后逐渐减少这些人员(turn them to loose)。同样的策略也可以通过给电脑配备数学计算、统计包和一些简单的编程能力,来适应几百个人的科学家组成的实验室。
需求细化和快速原型。构建一个软件系统最难的部分就是精确定义构建什么。没有什么工作比建立详细的技术需求更困难,包括系统与人之间的借口,系统与机器之间的接口,系统与系统之间的接口。没有什么工作如果发生错误能对目标系统产生如此大的影响。没有其他部分在后期修正时有如此困难。
因此,软件开发人员进行的最重要的工作就是为客户迭代的抽取和细化产品需求。事实上,客户并不知道需要回答什么问题,他几乎不会考虑规格说明所必须的细节问题。即使简单的回答如:“开发一个类似原来手工方式进行信息处理的新的软件系统”,事实上也确实太简单了。我们绝对不会想要这样的答案。复杂的软件系统更多的是物体的行为、移动和工作。其动态的行为是很难想象的。所以作为规划任何软件设计的活动,必须将允许在客户和设计者之间有可以扩展的迭代作为系统定义的一部分。
我会进一步指出对客户(甚至客户就是个软件工程师)来说在没有试用几个版本的产品之前不可能完全,精确,正确的描述出现代软件产品的准确细节。
因此,现在最有希望解决软件问题的本质的一个技术就是开发支持快速原型系统的方法和工具支持,这种方法是对需求进行迭代描述的一部分。
一个原型系统是模拟了重要的接口和目标系统的主要功能的系统,不需要由硬件速度,大小或者成本的约束。原型描述了应用的主线任务,但是对例外任务,无效输入,清理退出不做处理。原型的目的是构造概念结构说明,以让客户测试其一致性和可用性。
现在软件获取的过程大多数建立在客户可以提前描述一个满意的系统,然后投标,开发,安装。但我认为这个假设根本上就是错误的,这些软件获取问题都来自于这个谬误。因此,如果不做根本上的修订——提供对原型和产品的迭代开发和说明,问题无法被修正。
增量开发——增长,不是建造软件。我仍然记得我第一次听到我一个朋友在1958年谈论关于建造一个系统而不是写一个系统时的给我带来的震动。瞬间,他开阔了我在软件过程领域的整个视野。这种象征的转换是强大且精确的。现在我们明白了软件构造如何像其他建造过程一样的,我们也用其他类似的隐喻,如规格说明,组件装配,脚手架。
建造的隐喻现在也已经过时,是时候再次改变了。我认为,如果需要我们概念架构太复杂,导致无法提前进行精确的描述,也无法构建无错的系统,我们则必须要采用一个非常不同的方式。
让我们转向大自然并且学习生物的复杂性而不仅仅是人类所制造的死板东西。我们会发现这些东西构造的复杂性值得我们敬畏。仅就大脑而言就复杂的无法绘制,比仿真更强大,有丰富的多样性,自我保护和自我更新能力。其秘密就是他是长大的而不是建造的。
所以我们的软件系统也应该是这样,一些年以前Harlan Mills提出软件系统应该是通过增量开发逐渐成熟的。这就是说,系统首先应该能运行,即使只能调用一些特定的模拟子程序。然后一个比特一个比特的进行充实,一步一步的对子程序进行开发,将其转化为动作或者调用更低层次的空的存根程序。
自从我开始在我的软件工程实验室课上推进这种技术的时候,我看到了引入注目的结果。在过去的几十年里,没有什么这个更大的改变我的实践或者效率。这种方式自然要求自顶向下的设计,因为这是自顶向下的增长模式,它可以进行简单的回溯。它让自己成为早期的原型。每个增加的功能和新提供的复杂数据和环境在原型的基础上有机的增长。
这种方法所带来的士气的增长是惊人的,当其成为一个系统时,即使是个简单的系统,开发人员的热情快速提升,当一个图像软件系统的第一个图片显示在图片上时,即使只是个矩形,成员的投入的精力增加了两倍。在过程的每个阶段都有一个可用的系统。我发现团队可以在四个月内开发出比他们能够开发的更复杂的软件。
在实现大型项目时,也可以收获如我的小项目一样的效果
伟大的设计人员。
如何提升软件开发实践的中心问题始终集中在人上。我们可以可以通过遵守好的实践来获得好的设计,好的设计实践可以通过学习获得。程序员是人群中最聪明的一个群体,因此,他们可以学到好的实践。因此,一个目前在美国一个主要的问题在于宣传好的现代实践。新的课程新的文献、新的组织如软件工程研究所,所有这些都是为了将我们的实践从弱提升到好。这个是完全正确的。
尽管如此,我不相信我们能够以同样的方式再上一个台阶。尽管好的概念设计和坏的之间的区别在于对设计方法的了解,但伟大的设计和好的设计之间的肯定不是这样。伟大的设计来自于伟大的设计人员。软件构建是一个创造性的过程,合理的方法论可以解放并增强创造性思维,但不能启发做苦力的人。
这两者之间的区别不小,就像Slieri和Mozart之间的区别。各种研究表明,非常杰出的设计人员能够以更小的工作量完成更快,更小,更简单,更清晰的结构设计。伟大和平均的设计方式之间的差距有一个数量级。
一个简单的回顾就可以看出,虽然有很多好的,有用的软件系统是由某些委员会设计的,并且作为多个项目的一部分,但那些有着令人兴奋的热情的粉丝的产品却是由一个或者几个伟大设计师的设计理念得到的。考虑Unix,APL,Pascal,Modula,Smalltalk Interface,Fortran以及对应的Cobol,PL/I,Algol,MVS/370,和MS-DOC(前者是伟大的设计,后者是好用的产品)。
因此,虽然我强烈支持技术转移和课程开发的方式,我认为我们现在需要做的一个最重要的一个工作就是找到培养伟大设计人员的方式。
没有软件组织可以阻止这个变化。好的设计人员的稀缺远大于好的管理人员的缺口,当然好的管理人员也稀缺,好的设计人员和好的管理人员都非常稀少。大多数机构花费了大量的努力来找到并培养管理人员;但我没有看到有一家公司花了同样的精力来培养最终决定产品技术水平的软件设计人员。
我的第一个提议是每个软件组织必须明确伟大的设计人员和管理人员对其成功的作用是一样的,并且他们之间应该有相似的培养和奖励,不仅仅是薪水,包括认知的额外待遇—办公室大小,装修,个人技术装备,旅行基金,员工支持——必须完全相等。
如何培养伟大的设计人员?篇幅原因,不能展开讨论,但有几个步骤是显然的:
(1)尽早的识别顶尖设计人员,最好的不一定是最有经验的
(2)分配一个职业导师来负责其职业前景的发展并仔细的保持一个职业经历
(3)在每个方面设计和保持一个职业发展计划,包括精心选择向顶尖设计师学习,一段时间的高级正式学习,短时间的课程,不断的和独立设计师和技术领袖的共同完成的任务。
(4)为成长中的设计人员提供交流和促进的机会