现在的位置: 首页 > 读书笔记 > 正文
【读书笔记】“没有银弹”-有关软件工程的论断
2012年12月29日 读书笔记 ⁄ 共 6282字 【读书笔记】“没有银弹”-有关软件工程的论断已关闭评论 ⁄ 被围观 2,230 views+

初次听到《人月神话》的名字时,我以为是科幻小说,就像有人以为是爱情小说一样。只有当我看到它的英文书名:《The Mythical Man-Month》,才恍然大悟——此“月”(month)非彼“月”(moon)。这种理解障碍是语言差异引起的,几天来我都在想,翻译为什么不能更直白一点?《软件工程中的“人员-时间”问题》或其他什么。

事实上,这本关于软件工程和项目管理的著作,最早出版于1975年,中文版的序言作者将其作为“神品”收藏,书中的很多思想至今仍不过时,以至于不止一个读者说,“每隔一两年就会拿出来读一遍。”——也许他们只是想验证书中的预言是否仍然有效。

我所读的是32周年中文纪念版,除了经典而几乎没有争议的“人月”——衡量软件工程生产率的一个指标,作者还补充了更加深刻的“没有银弹”——一篇发表于1986年的论文(全书其实就是一本论文集),虽然“没有银弹”引起了很多争议,但是其中所探讨的问题充满魅力、思想洋溢,甚至我觉得这本书再版时应该改名“没有银弹”。

出于做笔记的习惯,我边读边想:有关软件工程和项目管理方面,全书提出了这么多经典的观点和方法,虽然几十年过去了,但是如今看来,对的当然令人兴奋,错的却也有错的理由。我是不是应该把这些内容都摘录下来,未来也许会用到?但是第18章改变了我的想法——作者亲自对各章的观点和方法进行了归纳和整理。所以,这本书的确很值得珍藏!而我,则根据自己印象最深的几点,完成了下面这则笔记……

一、“没有银弹”:

“软件的复杂度是根本属性,不是次要因素。因此,抽掉复杂度的软件实体描述常常也去掉了一些本质属性。数学和物理学在过去三个世纪取得了巨大的进步,数学家和物理学家们为复杂的现象建立了简化的模型,从模型中抽取出各种特性,并通过试验来验证这些特性。这些方法之所以可行,是因为模型中忽略的复杂度不是被研究现象的根本属性。当复杂度是本质特性时,这些方法就行不通了。”

“爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专横武断和反复无常的。软件工程师却无法从类似的信念中获得安慰,他必须掌握的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。它们随接口的不同而改变,随时间的推移而变化,并且,这些变化不是必需的,仅仅由于他们是不同的人——而非上帝——设计的结果。”

当我们试图用图形来描述软件结构时,发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。这些图形可能代表控制流程、数据流、依赖关系、时间序列和名字空间的相互关系等等。它们通常不是有较少层次的扁平结构。实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层次化一个或多个图形。”

原书中,这两段话分别针对软件四项内在特性中的两项:“复杂度”和“一致性”而论,这三段话让我有豁然开朗的感觉,所以相当喜欢!

  • 第一段加深了我对数学和物理的理解——我们的学习思路的确是这样的;

  • 第二段反映了一个心结:我们都想将精力放在解决问题本身,而不是因为人为设计不一致造成的无谓却必要的劳动上;

  • 第三段则介绍用图形描述软件结构的一种抽象方法——强制将关联分割。

“没有银弹”全篇(包括16、17章)则深化了我对软件的认识,这源自本篇的中心论点:软件存在根本的和次要的两种困难,为了提高软件开发的效率,人们同时在为解决这两种困难而努力着。如今,解决次要的困难已经取得了巨大进步,但根本的困难仍然困扰着人们——这也是作者“没有银弹”论断的来源。(将软件开发比作人狼——简单明了的东西可能变成落后进度、超出预算、存在大量缺陷的怪物,杀死人狼的武器是银弹,但是解决软件开发困难的银弹可能并不存在,因为人们面对的是软件的根本困难。)

由于作者所有的讨论都是针对软件的根本困难和次要困难,所以,理解这两种困难对于深化软件的认识至关重要,这也是我认为全书最精华的地方——作者讲得深入浅出,又发人深省。

··················································

作者认为,所有的软件活动包括两种任务:

  • 1.根本任务:打造构成抽象软件实体的复杂概念结构。

  • 2.次要任务:使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言。

相应的,软件就存在两种困难:

  • 1.根本的困难:软件特性中固有的困难。

  • 2.次要的困难:出现在目前生产中,但并非那些与生俱来的困难。

作者的观点是:

“我认为软件开发中困难的部分是规格说明、设计和测试这些概念上的结构,而不是对概念进行表达和对现实逼真程度进行验证。”

所以,软件无法规避的内在特性是(虽然书中没有明说,但我能感觉到,这些就是作者所说的根本困难):

  • 1.复杂度:没有任何两个软件部分是相同的(至少在语句的级别上)……数字计算机本身就比人类建造的大多数东西复杂……软件系统的状态又比计算机的状态多若干个数量级……软件的复杂度是根本属性,不是次要因素……

  • 2.一致性:许多情况下,因为是开发最新的软件,它必须遵循各种接口。另一些情况下,软件的开发目标就是兼容性……很多复杂性来自保持与其他接口的一致方面,对软件的任何再设计,都无法简化这些复杂特性……

  • 3.可变性:软件实体经常会遭受到持续的变更压力……系统中的软件包含了很多功能,而功能是最容易感受变更压力的部分……软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展……所有成功的软件都会发生变更……功能扩展的压力主要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们……软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比当初开发软件所用的计算机硬件平台要长……简言之,软件产品扎根于文化的母体中,如各种应用、用户、自然及社会规律、计算机硬件等等。后者持续不断地变化着,这些变化无情地强迫软件也随之变化……

  • 4.不可见性:软件是不可见的和无法可视化的……软件的客观存在不具有空间的形体特征……除去软件结构上的限制和简化方面的进展,软件仍然保持着无法可视化的固有特性,从而剥夺了一些具有强大功能的概念工具的构造创意。这种缺憾不仅限制了个人的设计过程,也严重阻碍了思路相互之间的交流。

*注:这里,未必说作者所说的都是对的,我们当然需要批判性思维,但是当这方面的知识远逊于作者时,将其作为知识吸收,从而促进自己的思考,显然是个不错的选择。

文章中剩余的部分则讨论了解决次要困难的突破(高级语言、分时、统一编程环境),以及解决根本困难的可能途径(Ada和其他高级编程语言、面向对象编程、人工智能、专家系统、“自动”编程、图形化编程、程序验证、环境和工具、工作站)——当然这些都被作者否定了。

最后,作者提出了自己所认为的一些可能解决根本困难的方法(参考如今的软件开发,作者30多年前的观点多么具有前瞻性啊!):

  • 1.购买和自行开发。

  • 2.需求精炼和快速原型。

  • 3.增量开发——增长,而非搭建系统。

  • 4.卓越的设计人员。

二、“人月神话”:

Brooks法则:向进度落后的项目中增加人手,只会使进度更加落后。

——出自原书第2章,这恐怕是全书最著名的论断。

“人月”是软件开发中,衡量生产率的一个指标(类似的还有“人天”、“人年”等,都是表示人员数量和开发时间)——也就是衡量软件开发规模的指标。

按照书中的观点:现实的项目开发过程中,由于人员之间需要沟通交流,所以,单纯的增加人员,并不一定能缩短整个项目的开发时间。

这是因为,新增加的人员,无论如何都要接受项目培训,原有的项目人员则要暂时放下手中的工作,为新人提供培训;同时,原有的项目分工需要重新分配,这些都属于额外的工作——如果没有新进人员,原有的工作结构和节奏就不会被破坏。所以,只有当新进人员的数量所最终提供的生产率,大于他们对原有项目结构和节奏所破坏的生产率的情况下,才能够缩短整个项目的开发时间——这从逻辑上是非常容易理解的,更何况作者还有图例说明。

因此,越是接近项目后期,增加人手对原有项目的“破坏性”就越大——现实偏偏是越到后期,才发现项目滞后,于是本能地增加人手。可见,如果一定要增加人员,选择在项目开发早期是最明智的做法。

··················································

本章也留下了两个问题:

  • 1.既然按照作者的意思,“人月”作为衡量软件生产率的指标,有其不合理之处(通常情况下,人员和时间并不能完全置换,典型的情况就是Brooks法则),那么,更好的指标是什么呢?如果我们理解作者所说的是针对“进度落后的项目”这种极端情况,那么一般情况下,如今所采用的“人月”指标,就仍然是合理的吧?

  • 2.作者在第19章对一次性构建项目的“瀑布开发模型”表示了反对,而是支持渐进式精化的“增量开发模型”,这就涉及本章作者自己的软件开发进度经验法则:1/3计划,1/6编码,1/4构件测试和早期系统测试,1/4系统测试。为了这种测试仍然能在“增量开发模型”中适用,是不是可以理解为,在增量开发的每一个阶段,都可以继续应用这种经验法则呢?

三、“类比”——简单有效的研究方法:

“类比”——虽然看起来很普通,但这是全书给我最大的启发,因为这是一种适用于很多领域的研究方法,包括如今仿生学中的飞行器设计、电子商务中的购物车概念等。以下是这本书中有关“类比”的几个例子:

  • 1.“外科手术式的团队”——整个开发团队由首席程序员负责,其他人辅助其工作——这是对外科手术的类比:护士、麻醉师等都是辅助,主刀医生只有一位(如果不止一位,该多么可怕)。

  • 2.“图形化界面”——窗口、图标、菜单、指针选取的软件操作界面,这是对桌面工作的类比(当然,也做了必要的优化和调整)。

  • 3.“增量式开发”——先完成一个主体,然后逐步添加功能,这是对生长的类比(自然界的生物都是逐步生长和进化的,不会因为养分的增多就会加速生长)。

书中这些有关软件开发的重大变革,如今并不鲜见,但它们的出现都是从“类比”而来。研究者正是发现了某些潜在的联系和规律,将其应用到自己的领域,从而形成了变革。以上这些变革是重要的,但是造成这些变革的思想更重要的。一旦运用自如,“类比”就是一种简单有效的研究方法。

四、摘录一些观点和方法:

书中还有一些令人印象深刻的观点和方法,我翻着书摘录了一些,作为笔记和参考:

  • 1.从程序变成产品,成本至少上升9倍,因为其中包括了大量的测试、文档、接口等。

  • 2.概念完整性是产品质量的核心。保证产品/项目的概念完整性至关重要,人数越少,越容易保证设计概念的完整性。产品概念的完整性可以使开发、使用变得容易,而且更不易产生bug。所以,产品的概念由一个人来控制,其他人提供向其各种工作支援的团队——“外科手术式”的团队是高效的。

  • 3.功能与理解上复杂程度的比值才是系统设计的最终测试标准,但是功能本身或简洁都无法成为一个好的评判标准。

  • 4.易用性需要设计的一致性和概念的完整性。

  • 5.不能与系统基本概念整合的良好想法和特色,最好放到一边。否则,就抛弃原来的设计。

  • 6.警惕过分地设计“第二个系统”,因为第一个系统的成功容易使设计师的信心膨胀,但先前的经验还不足以帮助其判断和验证。

  • 7.设计师和开发人员要尽早交流并持续保持沟通,实现是开发人员体现创造性的责任,设计师不应该越俎代庖。设计师负责面向用户的设计,而开发人员负责具体的实现。

  • 8.文档是必要的,规格说明的风格必须清晰、完整、准确,这会使说明手册读起来枯燥无味,但精确比生动更加重要。

  • 9.编制文档或手册时,采用形式化定义(精确)和叙述性定义(可以解释原因)相结合的方式,但两种定义必须分主次:一种作为标准,另一种作为辅助描述。

  • 10.项目经理需要成立一个独立的产品测试机构/小组。

  • 11.树状结构几乎不能用来描述交流沟通,因为交流是通过网状结构进行的。

  • 12.团队的搭建必须根据参与的人员来组织,而不是将人员纯粹地按照理论进行安排。

  • 13.开发人员必须设置规模的目标,控制规模,考虑减小规模的方法。

  • 14.设计人员必须决定用户可选项目的粗细程度。

  • 15.文档的跟踪维护是项目监督和预警的机制。文档本身可以作为检查列表、状态控制,也可以作为汇报的数据基础。

  • 16.项目经理的基本职责是使每个人都向着相同的方向前进,所以他的主要工作是沟通,而不是做出决定。

  • 17.项目经理的任务是制定计划,并实现计划。

  • 18.为舍弃而计划,必须构建一个用来抛弃的系统。

  • 19.版本控制、阶段(量子)化、定期变更:每个产品都应该有数字版本号,每个版本都应该有自己的日程表和冻结日期,在此之后的变更属于下一个版本的范畴。

  • 20.所有修改都倾向于破坏系统的架构,增加了系统的混乱程度(熵)。用在修复原有设计上瑕疵的工作量越来越少,而早期维护活动本身所引起的漏洞的修复工作越来越多。随之时间的推移,系统变得越来越无序,修复工作迟早会失去根基。

  • 21.系统软件开发是减少混乱度(减少熵)的过程,所以它本身是处于亚稳态的。软件维护是提高混乱度(增加熵)的过程,即使是最熟练的软件维护工作,也只是放缓了系统退化到亚稳态的进程。

  • 22.个性化的工具妨碍而不是促进沟通。项目经理应该制定一套策略,并为通用工具的开发分配资源。与此同时,他还必须意识到对专业工具的需求,对这类工具的开发不能吝啬人力和物力。

  • 23.计算资源匮乏时,把机器时间分割成不同的块,然后给不同的小组使用,是一种很好的方法。

  • 24.受控、使发布的进展变得正式:为每个编程人员分配一个空间来供其存放程序拷贝、测试用例等,每个人员可以自由处置自己的空间,但是当他向集成经理提交程序拷贝到系统集成子库后,没有集成经理的批准,原作者无法再修改代码。

  • 25.使用级别较高的表达方法来表现概念和隐藏细节,直到有必要进行进一步的细化。

  • 26.一些糟糕的系统往往就是试图挽救一个基础很差的设计,而对它添加了各种表面装饰般的补丁。

  • 27.关键的地方和构建无bug程序的核心,是把系统的结构作为控制结构来考虑,而不是独立的分支语句。

  • 28.对变更和差异进行记载。

  • 29.系统测试过程中,一次添加一个构件。

  • 30.有时必须回退,推翻顶层设计,重新开始。

  • 31.项目进度表中的里程碑必须是具体的、特定的、可度量的事件,能够清晰定义。

  • 32.项目进行中,必须关心每一天的滞后,它们是大灾祸(整个项目落后)的基本组成元素。

  • 33.自文档化代替软件流程图:通过向源程序中必须存在的语句附加“文档”信息;调整源程序的格式表现从属和嵌套关系;利用注释向源程序插入必要的记叙性文字。

  • 34.采用“快速原型+增量式开发”的软件开发方法。

  • 35.项目经理面临的中心问题是如何设计架构和流程,来提高而不是压制主动性和创造力。

  • 36.彻底提高软件健壮性和生产率的唯一途径,是提升一个级别,使用模块或者对象组合来进行程序的开发。

  • 37.不要盲目添加产品的功能。功能建议的吸引力在初期阶段是很明显的,性能代价在系统测试时才会出现。而随之功能一点一点地增加,手册慢慢地变厚,易用性损失以不易察觉的方式蔓延。

  • 38.定义用户群:他们是谁?他们需要什么?他们认为自己需要什么?他们想要的是什么?必要时,可以用合理的猜测(假设)来代替成本高昂的调查。

  • 39.将体现结构和设计实现、物理实现相分离:在大型系统开发中,由一位主结构师把系统分解成子系统,系统边界应该划分在使子系统间接口最小化(接口越少、越简单,bug就越少)和最容易严格定义的地方。

  • 40.把任何东西变成代码之前,可能要往复迭代两个或更多的体系结构-设计-实现循环。

抱歉!评论已关闭.

×