阅读:人月神话

本文最后更新于:2024-04-18

2 月份看完一遍《人月神话》,划了一堆笔记,时隔两个月,终于稍微整理了一下笔记,专门开一篇 blog,推荐这本书


2 月的书评

当时在 月记:2024-02 中的评价,原封不动搬过来(偷懒

2 月份没看多少书,只看完一本《人月神话》

这本书名声如此之大,以至于我在没接触编程之前就早有耳闻
在发展日新月异的 IT 领域,这本出版于 1975 年的书历经几十年仍然能被奉为经典,显然书籍质量绝对过硬

没看之前以为这本会是什么晦涩难懂的大部头专业书籍,但看了一点就发现本书更像是把一篇篇独立文章订在一起的散文集
作者根据自身经历,探讨软件工程领域的各种困难,以及解决/缓解的办法(或者是没有办法)

我目前经验只有一年多,阅读过程中有一些地方还不太能理解,但也有很多地方能产生共鸣
也许以后随着项目经验增加,才能对这本书产生更加深刻的理解吧

如果要做笔记的话,或许整本书直接搬上来就可以,没什么废话,全篇都是知识点(笑
所以这里只挑其中最出名的 关于人月的误导没有银弹(No silver bullet) 这两章,简单转述一下

  • 关于 人月 的误导
    作者强调 ,两个单位不能简单互换,一个人做六个月的工作换六个人做一个月大概率做不出来
    日本这里人月好像应用非常广泛的样子,对此我也没什么办法,毕竟我现在只是菜鸟,也想不出什么更好地估算工程量的办法
    只能记住作者说的,人月不能简单互换、向进度落后的项目里增派人手往往会更加拖慢项目进度

  • 没有银弹(No silver bullet)
    作者预言 在近十年内,没有任何单独的软件工程进展(银弹)可以使软件生产率有数量级的提高
    因为软件开发中的困难可以分为 根本困难(复杂度、一致性、可变性和不可见性)次要困难,目前的技术突破都是在解决软件开发中的 次要困难,仍然没有工具可以显著解决软件开发中的 根本困难

    说实话,我现在还看不太懂这部分,经验还太少了,而且对 1975 年的软件开发没什么了解,作者在书里提到的面向对象编程已经成为现在的默认实践
    但似乎也能稍微了解一点作者想表达的意思:软件开发中最困难的部分是设计而非编程(似乎是这个意思吧?
    等以后回头再来看这部分吧

其他还有诸如

  • 一个 程序 如果想变成 系统中的模块 需要至少付出 3 倍成本,如果想变成 可出售的产品 同样需要 3 倍成本,所以从 程序系统产品 需要至少付出 9 倍成本
  • 好程序员和差程序员之间可能会产生数量级的差距
  • 最好组建外科手术团队类似的小团队
  • 保持系统设计的概念一致性非常重要

知识点实在太多了,后续计划单开一篇博客,把阅读过程中划下来的笔记放上来,加上一点自己的感想
重要的是,这只是我第一遍阅读的感想,后续重读这本书时会计划继续更新读后感,也许可以记录下来随着经验增加对本书理解加深的过程

看完这本书,在网上查资料的时候,意外地发现 编程随想 在豆瓣上发表的书评:人月神话(不朽的软件工程名著)
想到 编程随想 的经历,有些唏嘘…


第一次阅读的笔记

放上我读这本书时划的笔记,内容太多,折叠一下

内容比较长,点击展开

职业的苦恼

Page 16 @ 04 February 2024

对于系统编程人员而言,对其他人的依赖是一件非常痛苦的事情。他依靠其他人的程 序,而往往这些程序设计得并不合理,实现拙劣,发布不完整(没有源代码或测试用例) , 或者文档记录得很糟。所以,系统编程人员不得不花费时间去研究和修改,而它们在理想情 况下本应该是可靠完整的

这就是常说的屎山吧(

Page 16 @ 04 February 2024

下一个烦恼——概念性设计是有趣的,但寻找琐碎的 bug 却只是一项重复性的活动。 伴随着创造性活动的,往往是枯燥沉闷的时间和艰苦的劳动。程序编制工作也不例外。

Page 17 @ 04 February 2024

诚然,产品开发所基于的技术在不断地进步。一旦设计被冻结,在概念上就已经开始 陈旧了。不过,实际产品需要一步一步按阶段实现。实现落后与否的判断应根据其它已有的 系统,而不是未实现的概念。因此,我们所面临的挑战和任务是在现有的时间和有效的资源 范围内,寻找解决实际问题的切实可行方案。

人月神话(The Mythical Man-Month)

Page 18 @ 04 February 2024

在众多软件项目中,缺乏合理的时间进度是造成项目滞后的最主要原因,它比其他所 有因素加起来的影响还大。导致这种普遍性灾难的原因是什么呢? 首先,我们对估算技术缺乏有效的研究,更加严肃地说,它反映了一种悄无声息,但 并不真实的假设——一切都将运作良好。 第二,我们采用的估算技术隐含地假设人和月可以互换,错误地将进度与工作量相互 混淆。

空泛的估算

Page 23 @ 04 February 2024

通过对传统项目进度安排的研究,我发现很少项目允许为测试分配一半的时间,但大 多数项目的测试实际上是花费了进度中一半的时间。 它们中的许多项目, 在系统测试之前还 能保持进度。或者说,除了系统测试,进度基本能保证。

Page 23 @ 04 February 2024

特别需要指出的是,不为系统测试安排足够的时间简直就是一场灾难。因为延迟发生 在项目快完成的时候。直到项目的发布日期,才有人发现进度上的问题。因此,坏消息没有 任何预兆,很晚才出现在客户和项目经理面前。

合理估算进度,分配时间确实需要经验,我现在连准确估计自己手头的工作都不太能做到
大部分时候我都过于乐观地估算了工作进度,后期容易火葬场(

重复产生的进度灾难

Page 27 @ 04 February 2024

向进度落后的项目中增加人手,只会使进度更加落后。(Adding manpower to a late software project makes it later)

Page 27 @ 04 February 2024

总之,在众多软件项目中,缺乏合理的时间进度是造成项目滞后的最 主要原因,它比其他所有因素加起来的影响还要大

贵族专制统治和民主政治

Page 36 @ 04 February 2024

由于目标是易用性,功能与理解上复杂程度的比值才是系统设计的最终测试标准。单 是功能本身或者易于使用都无法成为一个好的设计评判标准。

Page 37 @ 04 February 2024

现在让我们来处理具有浓厚感情色彩的问题——贵族统治和民主政治。结构师难道不 是新贵?他们一些智力精英, 专门来告诉可怜的实现人员如何工作?是否所有的创造性活动 被那些精英单独占有, 实现人员仅仅是机器中的齿轮?难道不能遵循民主的理论, 从所有的 员工中搜集好的创意,以得到更好的产品,而不是将技术说明工作仅限定于少数人?
最后一个问题是最简单的。我当然不认为只有结构师才有好的创意。新的概念经常来 自实现者或者用户。然而,我一直试图表达,并且我所有的经验使我确信,系统的概念完整 性决定了使用的容易程度。 不能与系统基本概念进行整合的良好想法和特色, 最好放到一边, 不予考虑。如果出现了很多非常重要但不兼容的构想,就应该抛弃原来的设计,对不同基本 概念进行合并,在合并后的系统上重新开始。

唯一不变的就是变化本身

Page 77 @ 06 February 2024

Cosgrove 很有洞察力地指出,开发人员交付的是用户满意 程度,而不仅仅是实际的产品。用户的实际需要和用户感觉会随着程序的构建、测试和使用 而变化 3。

前进一步,后退一步

Page 81 @ 06 February 2024

程序维护中的一个基本问题是——缺陷修复总会以(20-50)%的机率引入新的 bug。 所以整个过程是前进两步,后退一步。

著名梗 Changelog:修复一些 bug,并引入新 bug

Page 81 @ 06 February 2024

Lehman 和 Belady 研究了大型操作系统的一系列发布版本的历史 6。他们发现模块数量 随版本号的增加呈线性增长, 但是受到影响的模块以版本号指数的级别增长。 所有修改都倾 向于破坏系统的架构, 增加了系统的混乱程度。 用在修复原有设计上瑕疵的工作量越来越少, 而早期维护活动本身的漏洞所引起修复工作越来越多。 随着时间的推移, 系统变得越来越无 序,修复工作迟早会失去根基。每一步前进都伴随着一步后退。尽管理论上系统一直可用, 但实际上,整个系统已经面目全非,无法再成为下一步进展的基础。而且,机器在变化,配 置在变化,用户的需求在变化,所以现实系统不可能永远可用。崭新的、基于原有系统的重 新设计是完全必要的。

高级语言和交互式编程

Page 89 @ 08 February 2024

批处理系统

不太懂,什么叫批处理系统

构件单元调试

Page 92 @ 08 February 2024

关键的地方和构建无 bug 程序的核心,是把系统的结构作为控制结构来考虑,而不是 独立的跳转语句。这种思考方法是我们在程序设计发展史上向前迈出的一大步。

好像是说不用 goto,而是结构化编程的故事

系统集成调试

Page 96 @ 11 February 2024

一次添加一个构件。

Page 96 @ 11 February 2024

这样做的好处同样是显而易见的,但是乐观主义和惰性常常诱使 我们破坏这个规则。 因为离散构件的添加需要调试伪程序和其他测试平台, 有很多工作要做。 毕竟,可能我们不需要这些额外工作?可能不会出现什么 bug? 不!拒绝诱惑!这正是系统测试所关注的方面。我们必须假设系统中存在着许多错误, 并需要计划一个有序的过程把它们找出来。

祸起萧墙(Hatching a Catastrophe)

Page 97 @ 11 February 2024

当人们听到某个项目的进度发生了灾难性偏离时,可能会认为项目一定是遭受了一系 列重大灾难。然而,通常灾祸来自白蚁的肆虐,而不是龙卷风的侵袭。同样,项目进度经常 以一种难以察觉,但是残酷无情的方式慢慢落后。实际上,重大灾害是比较容易处理的,它 往往和重大的压力、彻底的重组、新技术的出现有关,整个项目组通常可以应付自如。
但是一天一天的进度落后是难以识别、不容易防范和难以弥补的。昨天,某个关键人 员生病了,无法召开某个会议。今天,由于雷击打坏了公司的供电变压器,所有机器无法启 动。明天,因为工厂磁盘供货延迟了一周,磁盘例程的测试无法进行。下雪、应急任务、私 人问题、同顾客的紧急会议、管理人员检查——这个列表可以不断地延长。每件事都只会将 某项活动延迟半天或者一天,但是整个进度开始落后了,尽管每次只有一点点。

“其他的部分反正会落后”

Page 98 @ 11 February 2024

进度落后了一天,那又怎么样呢?谁会关心一天的滞后?我们可以跟上进度。何况, 和我们有关的其他部分已经落后了。 棒球队队长知道,进取这种心理素质,是很多优秀队员和团队不可缺少的。它表现为 “要求跑得更快” , “要求移动得更加迅速” , “更加努力尝试” 。对软件开发队伍,进取同样 是非常必要的。进取提供了缓冲和储备,使开发队伍能够处理常规的异常事件,可以预计和 防止小的灾祸。

地毯的下面

Page 99 @ 11 February 2024

这时, 人们往往会比较乐观地放缓工作节奏。

Page 99 @ 11 February 2024

并不是每一天的滞后都等于灾难。尽管会如上文所述,事先估计会给工作进度的超前 带来影响,但对活动的一些计算和考虑还是必要的。那么,如何判断哪些偏离是关键的呢? 只有采用 PERT 或者关键路径技术才能判断。它显示谁需要什么样的东西,谁位于关键路径 上,他的工作滞后会影响最终的完成日期。另外,它还指出一个任务在成为关键路径时,可 以落后的时间。 严格地说,PERT 技术是关键路径计划的细化,如果使用 PERT 图,它需要对每个事件估 计三次, 每次对应于满足估计日期的不同可能性。 我觉得不值得为这样的精化产生额外的工 作量,但为了方便,我把任何关键路径法都称为 PERT 图。 PERT 的准备工作是 PERT 图使用中最有价值的部分。 它包括整个网状结构的展开、 任务 之间依赖关系的识别、各个任务链的估计。这些都要求在项目早期进行非常专业的计划。第 一份 PERT 图总是很恐怖的,不过人们总是不断地进行努力,运用才智制订下一份 PERT 图。 随着项目的推进,PERT 图为前面那个泄气的借口, “其他的部分反正会落后” ,提供了 答案。它展示某人为了使自己的工作远离关键路径,需要超前多少,也建议了补偿其他部分 失去的时间的方法

Page 99 @ 11 February 2024

当一线经理发现自己的队伍出现了计划偏离时,他肯定不会马上赶到老板那里去汇报 这个令人沮丧的消息。 团队可以弥补进度偏差, 他可以想出应对方法或者重新安排进度以解 决问题,为什么要去麻烦老板呢?从这个角度来看,好像还不错。解决这类问题的确是一线 经理的职责。 老板已经有很多需要处理的真正的烦心事了, 他不想被更多的问题打搅。 因此, 所有的污垢都被隐藏在地毯之下。 但是每个老板都需要两种信息:需要采取行动的计划方面的问题,用来进行分析的状 态数据 3。出于这个目的,他需要了解所有开发队伍的情况,但得到状态的真相是很困难的。 一线经理的利益和老板的利益是内在冲突的。一线经理担心如果汇报了问题,老板会 采取行动,这些行动会取代经理的作用,降低自己的威信,搞乱了其他计划。

Page 100 @ 11 February 2024

有两种掀开毯子把污垢展现在老板面前的方法,它们必须都被采用。一种是减少角色 冲突和鼓励状态共享,另一种是猛地拉开地毯。 减少角色的冲突。 减少角色的冲突。首先老板必须区别行动信息和状态信息。他必须规范自己,不对项 目经理可以解决的问题做出反应, 并且决不在检查状态报告的时候做安排。 我曾经认识一个 老板,他总是在状态报告的第一个段落结束之前,拿起电话发号施令。这样的反应肯定压制 信息的完全公开。 不过,当项目经理了解到老板收到项目报告之后不会惊慌,或者不会越俎代庖时,他 就逐渐会提交真实的评估结果。

Page 100 @ 11 February 2024

猛地拉开地毯。 猛地拉开地毯。不论协作与否,拥有能了解状态真相的评审机制是必要的。PERT 图以 及频繁的里程碑是这种评审的基础。大型项目中,可能需要每周对某些部分进行评审,大约 一个月左右进行整体评审。 有报告显示关键的文档是里程碑和实际的完成情况。 图 14.1 是上述报告中的一段摘录。 它显示了一些问题:手册(SLR)的批准时间有所冲突,其中一个的时间比独立产品测试 (Alpha)的开始时间还要迟。这样一份报告将作为 2 月 1 号会议的议程,使得每个人都知 道问题的所在,而产品构件经理应准备解释延迟的原因,什么时候结束,采取的步骤和需要 的任何帮助——老板提供的,或者是其他小组间接提供的。

Page 102 @ 11 February 2024

我发现在里程碑报告中很容易记录“计划”和“估计”的日期。计划日期是项目经理 的工作产物,代表了经协调后的项目整体工作计划,它是合理计划之前的判断。估计日期是 最基层经理的工作产物, 基层经理对所讨论的工作有着深刻的了解, 估计日期代表了在现有 资源和已得到了作为先决条件的必要输入(或得到了相应的承诺)的情况下,基层经理对实 际实现日期的最佳判断。 项目经理必须停止对这些日期的怀疑, 而将重点放在使其更加精确 上、以便得到没有偏见的估计,而不是那些合乎心意的乐观估计或者自我保护的保守估计。 一旦它们在每个人的脑海中形成了清晰的印象, 项目经理就可以预见到将来哪些地方如果他 不采取任何措施,就会出现问题 4。

Page 103 @ 11 February 2024

对计划和控制职能进行适度的技术人力投资是非常值得赞赏的。它对项目的贡献方式 和直接开发软件产品有很大的不同。 计划和控制小组作为监督人员, 明白地指出了不易察觉 的延迟,并强调关键的因素。他们是早期预警系统,防止项目以一次一天的方式落后一年。

另外一面(The other face)

Page 104 @ 11 February 2024

即使是完全 开发给自己使用的程序, 这种沟通仍然是必要的。 因为记忆衰退的规律会使用户-作者失去 对程序的了解,于是他不得不重拾自己劳动的各个细节。

Page 104 @ 11 February 2024

公共应用程序的用户在时间和空间上都远离它们的作者,因此对这类程序,文档的重 要性更是不言而喻! 对软件编程产品来说, 程序向用户所呈现的面貌和提供给机器识别的内 容同样重要。 面对那些文档“简约”的程序,我们中的大多数人都不免曾经暗骂那些远在他方的匿 名作者。因此,一些人试图向新人慢慢地灌输文档的重要性:旨在延长软件的生命期、克服 惰性和进度的压力。 但是, 很多次尝试都失败了, 我想很可能是由于我们使用了错误的方法。

需要什么样的文档

Page 105 @ 11 February 2024

我曾经非常勤奋地给我的软件工程师们举办了多年关于文档必要性以及优秀文档所应 具备特点方面的讲座,向他们讲述——甚至是热诚地向他们劝诫以上的观点。不过,这些都 行不通。我想他们知道如何正确地编写文档,却缺乏工作的热情。后来,我尝试了向马车上 搬一些收银机,以此演示如何完成这项工作。结果显示,这种方法的效果要好得多。所以, 文章剩余部分将对那些说教之辞一笔带过,而把重点放在“如何做(才能产生一篇优秀的文 档)上。

Page 105 @ 11 February 2024

需要什么样的文档 需要什么样的文档 不同用户需要不同级别的文档。某些用户仅仅偶尔使用程序,有些用户必须依赖程序, 还有一些用户必须根据环境和目的的变动对程序进行修改。 使用程序。 使用程序。每个用户都需要一段对程序进行描述的文字。可是大多数文档只提供了很 少的总结性内容,无法达到用户要求,就像是描绘了树木,形容了树叶,但却没有一副森林 的图案。为了得到一份有用的文字描述,就必须放慢脚步,稳妥地进行。 1. 目的。主要的功能是什么?开发程序的原因是什么? 2. 环境。程序运行在什么样的机器、硬件配置和操作系统上? 3. 范围。输入的有效范围是什么?允许显示的合法范围是什么? 4. 实现功能和使用的算法。精确地阐述它做了什么。 5. 输入-输出格式。必须是确切和完整的。 6. 操作指令。包括控制台及输出内容中正常和异常结束的行为。 7. 选项。用户的功能选项有哪些?如何在选项之间进行挑选? 8. 运行时间。在指定的配置下,解决特定规模问题所需要的时间 9. 精度和校验。期望结果的精确程度?如何进行精度的检测?
一般来说,三、四页纸常常就可以容纳以上所有的信息。不过往往需要特别注意的是 表达的简洁和精确。 由于它包含了和软件相关的基本决策, 所以这份文档的绝大部分需要在 程序编制之前书写。

Page 106 @ 11 February 2024

验证程序。 验证程序。除了程序的使用方法,还必须附带一些程序正确运行的证明,即测试用例。 每一份发布的程序拷贝应该包括一些可以例行运行的小测试用例,为用户提供信心— —他拥有了一份可信赖的拷贝,并且正确地安装到了机器上。

Page 106 @ 11 February 2024

然后,需要得到更加全面的测试用例,在程序修改之后,进行常规运行。这些用例可 以根据输入数据的范围划分成三个部分。 1. 针对遇到的大多数常规数据和程序主要功能进行测试的用例。 它们是测试用例的主 要组成部分。 2. 数量相对较少的合法数据测试用例, 对输入数据范围边界进行检查, 确保最大可能 值、最小可能值和其他有效特殊数据可以正常工作。 3. 数量相对较少的非法数据测试用例, 在边界外检查数据范围边界, 确保无效的输入 能有正确的数据诊断提示。

Page 106 @ 11 February 2024

修改程序。 修改程序。调整程序或者修复程序需要更多的信息。显然,这要求了解全部的细节, 并且这些细节已经记录在注释良好的列表中。 和一般用户一样, 修改者迫切需要一份清晰明 了的概述,不过这一次是关于系统的内部结构。那么这份概述的组成部分是什么呢? 1. 流程图或子系统的结构图,对此以下有更详细的论述。 2. 对所用算法的完整描述,或者是对文档中类似描述的引用。 3. 对所有文件规划的解释。 4. 数据流的概要描述——从磁盘或者磁带中, 获取数据或程序处理的序列——以及在 每个处理过程完成的操作。 5. 初始设计中,对已预见修改的讨论;特性、功能回调的位置以及出口;原作者对可 能会扩充的地方以及可能处理方案的一些意见。另外,对隐藏缺陷的观察也同样很有价值。

流程图

Page 107 @ 11 February 2024

流程图是被吹捧得最过分的一种程序文档。事实上,很多程序甚至不需要流程图,很 少有程序需要一页纸以上的流程图。 流程图显示了程序的流程判断结构,它仅仅是程序结构的一个方面。当流程图绘制在 一张图上时,它能非常优雅地显示程序的判断流向,但当它被分成几张时,也就是说需要采 用经过编号的出口和连接符来进行拼装时,整体结构的概观就严重地被破坏了。

自文档化(self-documenting)的程序

Page 108 @ 11 February 2024

现实中,流程图被鼓吹的程度远大于它们的实际作用。我从来没有看到过一个有经验 的编程人员,在开始编写程序之前,会例行公事地绘制详尽的流程图。在一些要求流程图的 组织中, 流程图总是事后才补上。 一些公司则很自豪地使用工具软件, 从代码中生成这个 “不 可缺少的设计工具” 。我认为这种普遍经验并不是令人尴尬和惋惜的对良好实践的偏离(似 乎大家只能对它露出窘迫的微笑) ,相反,它是对技术的良好评判,向我们传授了一些流程 图用途方面的知识。

Page 108 @ 11 February 2024

数据处理的基本原理告诉我们,试图把信息放在不同的文件中,并努力维持它们之间 的同步,是一种非常费力不讨好的事情。更合理的方法是:每个数据项包含两个文件都需要 的所有信息,采用指定的键值来区别,并把它们组合到一个文件中。

日本这边非常喜欢用 Excel,文档也基本都是 Excel,我愿意称之为 Excel 仙人
维持代码和文档的同步本来就不容易,用 Excel 感觉就更麻烦了

Page 108 @ 11 February 2024

不过,我们在程序文档编制的实践中却违反了我们自己的原则。典型的,我们试图维 护一份机器可读的程序,以及一系列包含记叙性文字和流程图的文档。 结果和我们自己的认识相吻合。不同文件的数据保存带来了不良的后果。程序文档质 量声名狼藉,文档的维护更是低劣:程序变动总是不能及时精确地反映在文档中。 我认为相应的解决方案是“合并文件” ,即把文档整合到源代码。这对正确维护是直接 有力的推动,保证编程用户能方便、即时地得到文档资料。这种程序被称为自文档化

Page 110 @ 11 February 2024

文档是我们以及前人都不曾成功背负的重担。作为基本目标,我们必须试图把它的负 担降到最小。
方法。第一个想法是借助那些出于语言的要求而必须存在的语句,来附加尽可能多的 “文档”信息。因此,标签、声明语句、符号名称均可以作为工具,用来向读者表达尽可能 多的意思。
第二个方法是尽可能地使用空格和一致的格式提高程序的可读性,表现从属和嵌套关系。
第三,以段落注释的形式,向程序中插入必要的记叙性文字。大多数文档一般都包括 足够多的逐行注释,特别是那些满足公司呆板的“良好文档”规范的程序,通常就包含了很 多注释。即使是这些程序,在段落注释方面也常常是不够的,而段落注释能提供总体把握和 真正加深读者对整件事情的理解。
因为文档是通过程序结构、命名和格式来实现的,所有这些必须在书写代码时完成。 不过,这也只是应该完成的时间。另外,由于自文档化的方法减少了很多附加工作,使这件 工作遇到的障碍会更少。

没有银弹-软件工程中的根本和次要问题(No Silver Bullet – Essence and Accident in Software Engineering)

Page 114 @ 11 February 2024

所有软件活动包括根本任务——打造由抽象软件实体构成的复杂概念结构,次要任务 ——使用编程语言表达这些抽象实体, 在空间和时间限制内将它们映射成机器语言。 软件生 产率在近年内取得的巨大进步来自对后天障碍的突破,例如硬件的限制、笨拙的编程语言、 机器时间的缺乏等等。这些障碍使次要任务实施起来异常艰难,相对必要任务而言,软件工 程师在次要任务上花费了多少时间和精力?除非它占了所有工作的 9/10,否则即使全部次 要任务的时间缩减到零,也不会给生产率带来数量级上的提高。
因此,现在是关注软件任务中的必要活动的时候了,也就是那些和构造异常复杂的抽 象概念结构有关的部分。我建议:
▪️ 仔细地进行市场调研,避免开发已上市的产品。
▪️ 在获取和制订软件需求时,将快速原型开发作为迭代计划的一部分。
▪️ 有机地更新软件,随着系统的运行、使用和测试,逐渐添加越来越多的功能。
▪️ 不断挑选和培养杰出的概念设计人员。

名气超级大的一章

介绍

Page 115 @ 11 February 2024

在所有恐怖民间传说的妖怪中,最可怕的是人狼,因为它们可以完全出乎意料地从熟 悉的面孔变成可怕的怪物。为了对付人狼,我们在寻找可以消灭它们的银弹。
大家熟悉的软件项目具有一些人狼的特性(至少在非技术经理看来) ,常常看似简单明 了的东西,却有可能变成一个落后进度、超出预算、存在大量缺陷的怪物。因此,我们听到 了近乎绝望的寻求银弹的呼唤, 寻求一种可以使软件成本像计算机硬件成本一样降低的尚方宝剑。
但是,我们看看近十年来的情况,没有银弹的踪迹。没有任何技术或管理上的进展, 能够独立地许诺在生产率、可靠性或简洁性上取得数量级的提高。本章中,我们试图通过分 析软件问题的本质和很多候选银弹的特征,来探索其原因

Page 115 @ 11 February 2024

不仅仅是在目力所及的范围内,没有发现银弹,而且软件的特性本身也导致了不大可 能有任何的发明创新——能够像计算机硬件工业中的微电子器件、 晶体管、 大规模集成一样 ——提高软件的生产率、可靠性和简洁程度。我们甚至不能期望每两年有一倍的增长。

是否一定那么困难呢?——根本困难

Page 116 @ 11 February 2024

其次,让我们通过观察预期的软件技术产业发展速度,来了解中间的困难。效仿亚里士多德, 我将它们分成根本的——软件特性中固有的困难, 次要的——出现在目前生产上的, 但并非那些与生俱来的困难

Page 116 @ 11 February 2024

一个相互牵制关联的概念结构,是软件实体必不可少的部分,它包括:数据集合、数据条目之间的关系、算法、功能调用等等。这些要素本身是抽象的,体现在相同的概念构架 中,可以存在不同的表现形式。尽管如此,它仍然是内容丰富和高度精确的。
我认为软件开发中困难的部分是规格化、设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程度进行验证。当然,我们还是会犯一些语法错误,但是和绝大多数系统中的概念错误相比,它们是微不足道的。
如果这是事实,那么软件开发总是非常困难的。天生就没有银弹

Page 116 @ 11 February 2024

让我们来考虑现代软件系统中这些无法规避的内在特性:复杂度、一致性、可变性和不可见性。

Page 116 @ 11 February 2024

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

Page 117 @ 11 February 2024

上述软件特有的复杂度问题造成了很多经典的软件产品开发问题。由于复杂度,团队成员之间的沟通非常困难,导致了产品瑕疵、成本超支和进度延迟;由于复杂度,列举和理解所有可能的状态十分困难,影响了产品的可靠性;由于函数的复杂度,函数调用变得困难,导致程序难以使用;由于结构性复杂度,程序难以在不产生副作用的情况下用新函数扩充;由于结构性复杂度,造成很多安全机制状态上的不可见性。
复杂度不仅仅导致技术上的困难,还引发了很多管理上的问题。它使全面理解问题变得困难,从而妨碍了概念上的完整性;它使所有离散出口难以寻找和控制;它引起了大量学习和理解上的负担,使开发慢慢演变成了一场灾难。

Page 117 @ 11 February 2024

可变性。软件实体经常会遭受到持续的变更压力。当然,建筑、汽车、计算机也是如此。不过,工业制造的产品在出厂之后不会经常地发生修改,它们会被后续模型所取代,或者必要更改会被整合到具有相同基本设计的后续产品系列。汽车的更改十分罕见,计算机的现场调整时有发生。然而,它们和软件的现场修改比起来,都要少很多。

Page 117 @ 11 February 2024

另外的原因是因为软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展。日常生活中,建筑有可能发生变化,但众所周知,建筑修改的成本很高,从而打消了那些想提出修改的人的念头。

以往解决次要困难的一些突破

Page 118 @ 11 February 2024

软件的客观存在不具有空间的形体特征。因此,没有已有的表达方式,就像陆地海洋有地图、硅片有膜片图、计算机有电路图一样。当我们试图用图形来描述软件结构时,我们发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。这些图形可能描绘控制流程、数据流、依赖关系、时间序列、名字空间的相互关系等等。它们通常不是有较少层次的扁平结构。实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层次化一个或多个图形 2。
除去软件结构上的限制和简化方面的进展,软件仍然保持着无法可视化的固有特性,从而剥夺了一些具有强大功能的概念工具的构造思路。这种缺憾不仅限制了个人的设计过程,也严重地阻碍了相互之间的交流

银弹的希望

Page 123 @ 11 February 2024

专家系统最强有力的贡献是给缺乏经验的开发人员提供服务,用最优秀开发者的经验和知识积累为他们提供了指导。这是非常大的贡献。最优秀和一般的软件工程实践之间的差距是非常大的,可能比其他工程领域中的差距都要大,一种传播优秀实践的工具特别重要。

针对概念上根本问题的颇具前途的方法

Page 127 @ 12 February 2024

需求精炼和快速原型。需求精炼和快速原型。开发软件系统的过程中,最困难的部分是确切地决定搭建什么样的系统。概念性工作中,没有其他任何一个部分比确定详细的技术需求更加困难,详细的需求包括了所有的人机界面、与机器和其他软件系统的接口。需求工作对系统的影响比其他任何一个部分的失误都大,当然纠正需求的困难也比其他任何一个部分要大。

Page 127 @ 12 February 2024

因此,软件开发人员为客户所承担的最重要的职能是不断重复地抽取和细化产品的需求。事实上,客户不知道他们自己需要什么。他们通常不知道哪些问题是必须回答的。并且,连必须确定的问题细节常常根本不予考虑,甚至只是简单地回答——“开发一个类似于我们已有的手工处理过程的新软件系统”——实际上都过于简单。客户决不会仅仅要求这些。复杂的软件系统往往是活动的、变化的系统。活动的动态部分是很难想象的。所以,在计划任何软件活动时,要让客户和设计人员之间进行多次广泛的交流沟通,并将其作为系统定义的一部分。这是非常必要的。

连客户自己都不知道他们自己需要什么,真相了

Page 128 @ 12 February 2024

这里,我将向前多走一步,下一个定论。在尝试和开发一些客户定制的系统之前,即使他们和软件工程师一起工作,想要完整、精确、正确地抽取现代软件产品的需求——这,实际上也是不可能的。

Page 128 @ 12 February 2024

因此,现在的技术中最有希望的,并且解决了软件的根本而非次要问题的技术,是开发作为迭代需求过程的一部分——快速原型化系统的方法和工具。
软件系统的快速原型对重要的系统界面进行模拟,并演示待开发系统的主要功能。原型不必受到相同硬件速度、规模或者成本约束的限制。原型通常展示了应用程序的功能主线,但不处理任何如无效输入、退出清除等异常情况。原型的目的是明确实际的概念结构,从而客户可以测试一致性和可用性。
现在的软件开发流程基于如下的假设——事先明确地阐述系统,为系统开发竞标,实际进行开发,最后安装。我认为这种假设根本上就是不正确的,很多软件问题就来自这种谬误。因此,如果不进行彻底地调整,就无法消除那些软件问题。其中,一种改进是对产品和原型不断往复地开发和规格化。

Page 128 @ 12 February 2024

增量开发——增长,而非搭建系统。我现在还记得在 1958 年,当听到一个朋友提及搭建(building),而不是编写(writing)系统时,我所感受到的震动。一瞬间,我的整个软件开发流程的视野开阔了。这种暗喻是非常有力和精确的。现在,我们已经理解软件开发是如何类似于其他的建造过程,并开始随意地使用其他的暗喻,如规格说明、构件装备、脚手架(测试平台) (specifications,assemblyofcomponents, and scaffolding) 。

Page 129 @ 12 February 2024

很多年前,Harlan Mill 建议所有软件系统都应该以增量的方式开发 11。即,首先系统应该能够运行,即使未完成任何有用功能,只能正确调用一系列伪子系统。接着,系统一点一点被充实,子系统轮流被开发,或者是在更低的层次调用程序、模块、子系统的占位符(伪程序)等。

似乎和现在的敏捷开发有一些共同点

Page 129 @ 12 February 2024

在过去几十年中, 没有任何方法和技术能如此彻底地改变我自己的实践。这种方法迫切地要求自顶向下设计, 因为它本身是一种自顶向下增长的软件。 增量化开发使逆向跟踪很方便, 并非常容易进行原 型开发。每一项新增功能,以及针对更加复杂数据或情况的新模块,从已经规划的系统中有 机地增长。 这种开发模式对士气的推动是令人震惊的。当一个可运行系统——即使是非常简单的 系统出现时, 开发人员的热情就迸发了出来。 当一个新图形软件系统的第一副图案出现在屏 幕上时, 即使是一个简单的长方形, 工作的动力也会成倍地增长。 在开发过程中的每个阶段, 总有可运行的系统。我发现开发团队可以在四个月内,培育(grow)出比搭建(building) 复杂得多的系统。

Page 129 @ 12 February 2024

低劣设计和良好设计之间的区别可能在于设计方法中的完善性,而良好设计和卓越设计之间的区别肯定不是如此。卓越设计来自卓越的设计人员。软件开发是一个创造性的过程。完备的方法学可以培养和释放创造性的思维,但它无法孕育或激发创造性的过程。其中的差异并不小——就象萨列里和莫扎特。一个接一个的研究显示,非常卓越的设计者产生的成果更快、更小、更简单、更优雅,实现的代价更少。卓越和一般之间的差异接近于一个数量级。

卓越和一般之间的差异接近于一个数量级。

NO

Page 130 @ 12 February 2024

我的第一项建议是每个软件机构必须决定和表明,杰出的设计人员和卓越的管理人员一样重要,他们应该得到相同的培养和回报。不仅仅是薪资,还包括各个方面的认可——办公室规模、安排、个人的设备、差旅费用、人员支持等——必须完全一致。
如何培养杰出的设计人员?限于篇幅,不允许进行较长的介绍,但有些步骤是显而易见的。
▪️ 尽可能早地、有系统地识别顶级的设计人员。最好的通常不是那些最有经验的人员。
▪️ 为设计人员指派一位职业导师,负责他们技术方面的成长,仔细地为他们规划职业生涯。
▪️ 为每个方面制订和维护一份职业计划,包括与设计大师的、经过仔细挑选的学习过程、正式的高级教育和以及短期的课程——所有这些都穿插在设计和技术领导能力的培养安排中。
▪️ 为成长中的设计人员提供相互交流和学习的机会

存在着银弹-就在这里!

Page 133 @ 12 February 2024

次要(Accident) 。 次要(Accident) 。在第 16 章的摘要中,我已经尽我所能地清晰表达了《没有银弹》 一文的主要观点。然而,仍有些观点由于术语“accident(偶然) ”和“accidental(次要) ” 而被混淆,这些术语来自亚里斯多德的古老用法 4。术语“accidental” ,我不是指“偶然发 生” ,也不是指“不幸的” ,而是更接近于“附带的”或者“从属的” 。

含糊的表达将会导致误解

Page 134 @ 12 February 2024

复杂性是层次化的。 复杂性是层次化的。例如,复杂性是最严重的内在困难,并不是所有的复杂性都是不 可避免的。我们的很多软件,但不是全部,来自应用本身随意的复杂特性。

Page 135 @ 12 February 2024

就我的经验而言,在系统工作中所遇到的大多数困难是组织结构上的一些失误征兆。 试图为这些现实建模,建立同等复杂的程序,实际上是隐藏,而不是解决这些杂乱无章的情 况。

Page 135 @ 12 February 2024

不过,很多复杂性并不完全是因为和外部世界保持一致,而是因为实现的本身,例如 数据结构、算法、互联性等。而在更高的级别开发(发展)软件,使用其他人的成果,或者 重用自己的程序——都能避免面对整个层次的复杂性。 《没有银弹》提出了全力解决复杂性 问题的方法, 这种方法可以在现实中取得十分乐观的进展。 它倡导向软件系统增加必要的复 杂性: ▪️ 层次化,通过分层的模块或者对象。 ▪️ 增量化,从而系统可以持续地运行。

Harel的分析

Page 136 @ 12 February 2024

在所有被误导的科学探索中,最悲惨的莫过于对一种能够将一般金属变成金子的物质, 即点金石的研究。这个由统治者不断地投入金钱,被一代代的研究者不懈追求的、炼金术中 至高无上的法宝, 是一种从理想化想象和普遍假设中——以为事情会像我们所认为的那样— —提取出的精华。 它是人类纯粹信仰的体现, 人们花费了大量的时间和精力来认可和接受这 个无法解决的问题。即使被证明是不存在,那种寻找出路和希望能一劳永逸的愿望,依然十 分的强烈。 而我们中的绝大多数总是很同情这些明知不可为而为之的人, 因此它们总是得以 延续。所以,将圆形变方的论文被发表,恢复脱发的洗液被研制和出售,提高软件生产率的 方法被提出并成功地推销。我们太过倾向于遵循我们自己的乐观主义(或者是发掘我们出资人的乐观主义)。我们太喜欢忽视真理的声音,而去听从万灵药贩卖者的诱惑 11。

Jone的观点——质量带来生产率

Page 139 @ 12 February 2024

Coqui 也提出相似的主张: 系统化软件开发方法的发展是为了解决质量问题 (特别是避 免大型的灾难) ,而不是出于生产率方面的考虑。 但是注意:70年代,在软件生产上应用工程原理的目标是提高软件产品的质量、可测 试性、稳定性以及可预见性——而不是软件产品的开发效率。 在软件生产上应用工程原理的驱动力是担心拥有无法控制的“艺术家们”而可能导致 的巨大灾难,他们往往对异常复杂系统开发承担责任 20。

面向对象编程——这颗铜质子弹可以吗?

Page 141 @ 12 February 2024

使用更大的零件来构建。 使用更大的零件来构建。面向对象编程的第一个特征是,它强制的模块化和清晰的接 口。其次,它强调了封装,即外界无法看到组件的内部结构;它还强调了继承和层次化类结 构以及虚函数。 面向对象还强调了强抽象数据类型化, 它确保某种特定的数据类型只能由它 自身的相应函数来操作。

重用的情况怎样?

Page 142 @ 12 February 2024

面向对象应用在整个开发周期 中,但是真正的获益只有在后续开发、扩展和维护活动中才能体现出来。Coggin 说: “面向 对象技术不会加快首次或第二次的开发,产品族中第五个项目的开发才会异乎寻常的迅速。 22”

学习大量的词汇——对软件重用的一个可预见,但还没有被预言的问题

Page 144 @ 12 February 2024

作为一个科目,我们并没有就程序重用的实际情况,仔细考虑它蕴涵的意义。为了提 高质量和生产率,我们需要通过经过调试的大型要素来构建系统,在编程语言中,这些函数 的级别远远高于语句。所以,无论采用对象类库还是函数库的方式,我们必须面对我们编程 词汇规模彻底扩大的事实。对于重用,词汇学习并不是思维障碍中的一小部分。 现在人们拥有成员超过 3000 个的类库。很多对象需要 10 到 20 个参数和可选变量的说 明。如果想获得所有潜在的重用,任何使用类库编程的人员必须学习其成员的语法(外部接 口)和语义(详细的功能行为) 。

《人月神话》的观点:是或非?(Propositions of the Mythical Man-Month: True or False?)

Page 146 @ 12 February 2024

1.1 编程系统产品(Programming Systems Product)开发的工作量是供个人使用的、独立开发的构件程序的九倍。我估计软件构件产品化引起了 3 倍工作量,将软件构件整合成完整系统所需要的设计、集成和测试又强加了 3 倍的工作量,这些高成本的构件在根本上是相互独立的。

第2章 人月神话

Page 147 @ 12 February 2024

人们通常期望项目在接近结束时, (bug、工作时间)能收敛得快一些,然而软件 项目的情况却是越接近完成,收敛得越慢

Page 147 @ 12 February 2024

2.1 缺乏合理的时间进度是造成项目滞后的最主要原因, 它比其他所有因素加起来影响 还大。

Page 147 @ 12 February 2024

2.3 所有的编程人员都是乐观主义者: “一切都将运作良好” 。

第3章 外科手术队伍

Page 148 @ 12 February 2024

2.6 我们围绕成本核算的估计技术,混淆了工作量和项目进展。人月是危险和带有欺骗性的神话,因为它暗示人员数量和时间是可以相互替换的。
2.7 在若干人员中分解任务会引发额外的沟通工作量——培训和相互沟通。
2.8 关于进度安排,我的经验是为 1/3 计划、1/6 编码、1/4 构件测试以及 1/4 系统测试。

Page 148 @ 12 February 2024

2.12 向软件项目中增派人手从三个方面增加了项目必要的总体工作量: 任务重新分 配本身和所造成的工作中断;培训新人员;额外的相互沟通。

Page 148 @ 12 February 2024

3.1 同样有两年经验而且在受到同样的培训的情况下, 优秀的专业程序员的工作效率是 较差程序员的十倍。 (Sackman、Erikson 和 Grand)

Page 148 @ 12 February 2024

3.3 小型、精干队伍是最好的——尽可能的少。

Page 148 @ 12 February 2024

3.5 对于真正意义上的大型系统,小型精干的队伍太慢了。

第4章 贵族专制、民主政治和系统设计

Page 149 @ 12 February 2024

3.7 一位首席程序员、 类似于外科手术队伍的团队架构提供了一种方法——既能获得由 少数头脑产生的产品完整性,又能得到多位协助人员的总体生产率,还彻底地减少了沟通的工作量。

Page 149 @ 12 February 2024

第 4 章 贵族专制、民主政治和系统设计 贵族专制、民主政治和系统设计
4.1 “概念完整性是系统设计中最重要的考虑因素” 。
4.2 “功能与理解上的复杂程度的比值才是系统设计的最终测试标准”,而不仅仅是丰富的功能。[该比值是对易用性的一种测量,由简单和复杂应用共同验证。]
4.3 为了获得概念完整性,设计必须由一个人或者具有共识的小型团队来完成。
4.4 “对于非常大型的项目,将设计方法、体系结构方面的工作与具体实现相分离是获得概念完整性的强有力方法。”[同样适用于小型项目。]
4.5 “如果要得到系统概念上的完整性,那么必须控制这些概念。这实际上是一种无需任何歉意的贵族专制统治。”
4.6 纪律、规则对行业是有益的。外部的体系结构规定实际上是增强,而不是限制实现小组的创造性。
4.7 概念上统一的系统能更快地开发和测试。
4.8 体系结构 (architecture)、设计实现 (implementation)、物理实现 (realization)的许多工作可以并发进行。[软件和硬件设计同样可以并行。]

第7章 为什么巴比伦塔会失败?

Page 152 @ 12 February 2024

7.16 团队组织的目标是为了减少必要的交流和协作量。

第9章 削足适履

Page 154 @ 12 February 2024

9.6 在大型的团队中,各个小组倾向于不断地局部优化,以满足自己的目标,而较少考虑队用户的整体影响。这种方向性的问题是大型项目的主要危险。

Page 154 @ 12 February 2024

9.8 培养开发人员从系统整体出发、面向用户的态度是软件编程管理人员最重要的职能。

Page 154 @ 12 February 2024

9.14 精炼、充分和快速的程序。往往是战略性突破的结果,而不仅仅技巧上的提高。
9.15 这种突破常常是一种新型算法。
9.16 更普遍的是,战略上突破常来自于数据或表的重新表达。数据的表现形式是编程的根本。

第11章 未雨绸缪

Page 156 @ 12 February 2024

11.7 “开发人员交付的是用户满意程度,而不仅仅是实际的产品。 ” (Cosgrove)

能满足用户需求才是最重要的

Page 156 @ 12 February 2024

11.8 用户的实际需要和用户感觉会随着程序的构建、测试和使用而变化。
11.9 软件产品易于掌握的特性和不可见性,导致了它的构建人员(特别容易)面临着永恒的需求变更。
11.10 目标上(和开发策略上)的一些正常变化无可避免,事先为它们做准备总比假设它们不会出现要好得多。
11.11 为变更计划软件产品的技术,特别是细致的模块接口文档——非常地广为人知,但并没有相同规模的实践。尽可能地使用表驱动技术同样是有所帮助的。[现在内存的 成本和规模使这项技术越来越出众。]
11.29 所有修改都倾向于破坏系统的架构,增加了系统的混乱程度。即使是最熟练的软件维护工作,也只是放缓了系统退化到不可修复混乱的进程,从中必须要重新进行设计。[许多程序升级的真正需要,如性能等,尤其会冲击它的内部结构边界。原有边界引发的不足常常在日后才会出现。]

第13章 整体部分

Page 160 @ 12 February 2024

13.13 系统调试仅仅应该在所有部件能够运作之后开始。 (这既不同于为了查出接口 bug 所采取 “合在一起尝试” 的方法;也不同于在所有构件单元的 bug 已知,但未修复的 情况下,即开始系统调试的做法。 )[对于多个团队尤其如此。]

第15章 另外一面

Page 162 @ 12 February 2024

14.13 每个老板同时需要采取行动的异常信息以及用来进行分析和早期预警的状态数据。
14.14 状态的获取是困难的,因为下属经理有充分的理由不提供信息共享。
14.15 老板的不良反应肯定会对信息的完全公开造成压制;相反,仔细区分状态报告、毫无惊慌地接收报告、决不越俎代庖,将能鼓励诚实的汇报。
14.16 必须有评审的机制,从而所有成员可以通过它了解真正的状态。出于这个目的,里程碑的计划和完成文档是关键。

Page 163 @ 12 February 2024

15.4 这样的失败并不都是因为缺乏热情或者说服力,而是没能正确地展示如何有效和经济地编制文档。
15.5 大多数文档只提供了很少的总结性内容。必须放慢脚步,稳妥地进行。
15.6 由于关键的用户文档包含了跟软件相关的基本决策,所以它的绝大部分需要在程序编制之前书写,它包括了 9 项内容(参见相应章节) 。
15.7 每一份发布的程序拷贝应该包括一些测试用例,其中一部分用于校验输入数据,一部分用于边界输入数据,另一部分用于无效的输入数据。
15.8 对于必须修改程序的人而言,他们所需要程序内部结构文档,同样要求一份清晰明了的概述,它包括了 5 项内容(参见相应章节) 。
15.9 流程图是被吹捧得最过分的一种程序文档。详细逐一记录的流程图是一件令人生厌的事情,而且高级语言的出现使它显得陈旧过时。(流程图是图形化的高级语言。)
15.10 如果这样,很少有程序需要一页纸以上的流程图。[在这一点上,MILSPEC 军用标准实在错得很厉害。]
15.11 即使的确需要一张程序结构图,也并不需要遵照 ANSI 的流程图标准。
15.12 为了使文档易于维护,将它们合并至源程序是至关重要的,而不是作为独立文档进行保存。
15.13 最小化文档负担的 3 个关键思路:
▪️ 借助那些必须存在的语句,如名称和声明等,来附加尽可能多的“文档”信息。
▪️ 使用空格和格式来表现从属和嵌套关系,提高程序的可读性。
▪️ 以段落注释,特别是模块标题的形式,向程序中插入必要的记叙性文字。
15.14 程序修改人员所使用的文档中,除了描述事情如何以外,还应阐述它为什么那样。对于加深理解,目的是非常关键的,但即使是高级语言的语法,也不能表达目的。
15.15 在线系统的高级语言(应该使用的工具)中,自文档化技术发现了它的绝佳应用和强大功能。

核心观点:概念完整性和结构师

Page 166 @ 12 February 2024

第二个经常提及的解释——《人月神话》仅仅是顺便提及了软件,而主要针对团队中的成员如何创建事物。这种说法的确有些道理,1975 年版本的前言中提到,软件项目管理并不像大多数程序员起初所认为的那样,而更加类似于其他类型的管理。现在,我依然认为这是正确的。人类历史是一个舞台,总是上演着相同的故事。随着文化的发展,这些故事的剧本变化非常缓慢,而舞台的布局却在随时改变。正是如此,我们发现二十世纪本身会反映在莎士比亚、荷马的作品和圣经中。因此,某种程度上,《人月神话》是关于人与团队的书,所以它的淘汰过程会是缓慢的。

Page 167 @ 12 February 2024

结构师。 结构师。从第 4 到第 7 章,我一直不断地在表达一个观点——委派一名产品结构师是最重要的行动。结构师负责产品所有方面的概念完整性,这些是用户能实际感受到的。结构师开发用于向用户解释使用的产品概念模型,概念模型包括所有功能的详细说明以及调用和控制的方法。结构师是这些模型的所有者,同时也是用户的代理。在不可避免地对功能、性能、规模、成本和进度进行平衡时,卓有成效地体现用户的利益。这个角色是全职工作,只有在最小的团队中,才能和团队经理的角色合并。结构师就像电影的导演,而经理类似于制片人。

开发第二个系统所引起的后果:盲目的功能和频率猜测

Page 168 @ 12 February 2024

盲目的功能(Featuritis) 。 盲目的功能(Featuritis) 。对于如电子表格或字处理等通用工具的结构师,一个不断困扰他们的诱惑是以性能甚至是可用性的代价,过多地向产品添加边界实用功能。功能建议的吸引力在初期阶段是很明显的,性能代价在系统测试时才会出现。而随着功能一点一点地添加,手册慢慢地增厚,易用性损失以不易察觉的方式蔓延。

Page 168 @ 12 February 2024

定义用户群。用户群越大和越不确定,就越有必要明确地定义用户群,以获得概念完整性。设计队伍中的每个成员对用户都有一幅假想的图像,并且每个设计者的图像都是不同的。结构师的用户图像会有意或者无意地影响每个结构决策,因此有必要使设计队伍共享一幅相同的用户图像。这需要把用户群的属性记录下来,包括:
▪️ 他们是谁
▪️ 他们需要(need)什么
▪️ 他们认为自己需要(need)什么
▪️ 他们想要(want)的是什么

Page 169 @ 12 February 2024

频率。对于软件产品,任何用户群属性实际上都是一种概率分布,每个属性具有若干可能的值,每个值有自己的发生频率。结构师如何成功地得到这些发生频率?对并未清晰定义的对象进行调查是一种不确定和成本高昂的做法 3。经过很多年,我现在确信,为了得到完整、明确和共有的用户群描述,结构师应该猜测(guess),或者假设(postulate)完整的一系列属性和频率值。这种不是很可靠的过程有很多好处。首先,仔细猜测频率的过程会使结构师非常细致地考虑对象用户群。其次,把它们写下来一般会引发讨论,这能起到解释的作用,以及澄清不同设计人员对用户图像认识上的差异。
另外,明确地列举频率能帮助大家认识到哪些决策依赖于哪些用户群属性。这种非正式的敏感性分析也是颇有价值的。当某些非常重要的决策需要取决于一些特殊的猜测时,很值得为那些数值花费精力来取得更好的估计。( Jeff Conklin 开发的 gIBIS 提供了一种工具,能精确和正式地跟踪设计决策和文档化每个决策的原因 4。我还没有机会使用它,但是我认为它应该非常有帮助。)
总结:为用户群的属性明确地记载各种猜测。清晰和错误都比模糊不清好得多。

图形(WIMP)界面的成功

Page 170 @ 12 February 2024

通过类比获得的概念完整性。WIMP 是一个充分体现了概念完整性的用户界面例子,完整性的获得是通过采用大家非常熟悉的概念模型——对桌面的比喻,以及一致、细致的扩展,后者充分发挥了计算机的图形化实现能力。例如,窗口采用覆盖,而不是排列的方式,这直接来自类比。尽管这种方法成本很高,但却是正确的决定。计算机图形介质提供了对窗口尺寸的调整,这是一种保持一致概念的延伸,给用户提供了新的处理能力,桌面上的文件是无法轻易地调整大小和改变形状的;拖放功能则直接出自模仿,使用指针来选择图标是对人用手拾起东西的直接模拟;图标和嵌套文件夹源于桌面的文档,回收站也是如此;剪切、复制和粘贴则完全反映了我们使用文档的一些习惯;我们甚至可以通过向回收站拖拽磁盘的图标来弹出磁盘——象征手法是如此的贴切,扩展是如此的连贯一致,以致于新用户常常会被它所体现出的理念打动。

Page 171 @ 12 February 2024

软件结构师所面临的最困难问题是如何确切地平衡用户功能和易用性。是为初学者或 偶尔使用的用户设计简单操作, 还是为专业用户设计强大的功能?理想的答案是通过概念一 致的方式把两者都提供给用户——这正是 WIMP 界面所达到的目标。每个频繁使用的菜单动 词(命令)都有一个快捷键,因此可以作为组合通过左手一次性地输入。例如,在 Mac 机器 上,命令键(♂[jypan2])正好在 Z 和 X 键的下方,因此使用最频繁的操作被编码成♂z、♂ x、♂c、♂v、♂s。

没有构建舍弃原型——瀑布模型是错误的!

Page 173 @ 12 February 2024

瀑布模型的基本谬误是它假设项目只经历一次过程,而且体系结构出色并易于使用,设计是合理可靠的,随着测试的进行,编码实现是可以修改和调整的。换句话说,瀑布模型假设所有错误发生在编码实现阶段,因此它们的修复可以很顺畅地穿插在单元和系统测试中。
实际上,《未雨绸缪》并没有迎面痛击这个错误。它不是对错误的诊断,而是补救措施。现在,我建议应该一块块地丢弃和重新设计系统,而不是一次性地完成替换。就目前的情况而论,这没有问题,但它并没有触及问题的根本。瀑布模型把系统测试,以及潜在地把用户测试放在构件过程的末尾。因此,只有在投入了全部开发投资之后,才能发现无法接受的性能问题、笨拙功能以及察觉用户的错误或不当企图。不错,Alpha 测试对规格说明的详细检查是为了尽早地发现这些缺陷,但是对于实际参与的用户却没有对应的措施。

增量开发模型更佳——渐进地精化

Page 174 @ 12 February 2024

必须存在逆向移动。就像本章开始图片中精力充沛的大马哈鱼一样,在开发过程“下游”的经验和想法必须跃行而上,有时会超过一个阶段,来影响“上游”的活动。例如,设计实现会发觉有些体系结构的功能定义会削弱性能,从而体系结构必须重新调整。编码实现会发现一些功能会使空间剧增,超过要求,因此必须更改体系结构定义和设计实现。所以,在把任何东西实现成代码之前,可能要往复迭代两个或更多的体系结构-设计-实现循环。

Page 174 @ 12 February 2024

从事实时系统开发的 Harlan Mills,早期曾提倡我们首先应该构建实时系统的基本轮询回路,为每个功能都提供子函数调用(占位符),但仅仅是空的子函数(图 19.2) 。对它进行编译、测试,使它可以不断运行。它不直接完成任何事情,但至少是正常运行的 10。
接着,我们添加(可能是基本的)输入模块和输出模块。瞧,一个可运行的系统出现了,尽管只是一个框架。然后,一个功能接一个功能,我们逐渐开发和增加相应模块。在每个阶段,我们都拥有一个可运行的系统。如果我们非常勤勉,那么每个阶段都会有一个经过调试和测试的系统。(随着系统的增长,使用所有先前的测试用例对每个新模块进行的回归测试也采用这种方式进行。)
在每个功能基本可以运行之后,我们一个接一个地精化或者重写每个模块——增量地开发(growing)整个系统。不过,我们有时的确需要修改原有的驱动回路,或者甚至是回路的模块接口。因为我们在所有时刻都拥有一个可运行的系统,所以
▪️ 我们可以很早开始用户测试,以及
▪️ 我们可以采用按预算开发的策略,来彻底保证不会出现进度或者预算的超支(以允许的功能牺牲作为代价)

Page 175 @ 12 February 2024

在这整个 20 年的时间里,David Parnas 曾是软件工程思潮的带头人。每个人对他的信息隐藏概念都很熟悉,但对他另一个非常重要的概念——将软件作为一系列相关的产品族来设计11——相对了解较少。 Parnas 力劝设计人员对产品的后期扩展和后续版本进行预测,定义它们在功能或者平台上的差异,从而搭建一棵相关产品的家族树(图 19.3) 。
设计类似一棵树的技巧是将那些变化可能性较小的设计决策放置在树的根部。
这样的设计使得模块的重用最大化。更重要的是,可以延伸相同的策略,使它不但可以包括发布产品,而且还包括以增量开发策略创建的后续中间版本。这样,产品可以通过它的中间阶段,以最低限度的回溯代价增长。

关于信息隐藏,Parnas是正确的,我是错误的

Page 177 @ 12 February 2024

我在第 7 章中并不认同 Parnas 的概念是“灾难的处方” 。但是,Parnas 是正确的,我是错误的。现在,我确信信息隐藏——现在常常内建于面向对象的编程中——是唯一提高软件设计水平的途径。

Page 178 @ 12 February 2024

实际上,任何技术的使用都可能演变成灾难。Mill 的技术是通过了解接口另一侧的情况,使编程人员能理解他们所工作接口的详细语义。这些接口的隐藏会导致系统的 bug。 Parnas 的技术在面对变更时是很健壮的,更加适合为变更设计的理念。
第 16 章指出了下列情况:
▪️ 过去在软件生产率上取得的进展大多数来自消除非内在的困难,如笨拙的编程语言、漫长的批处理周转时间等。
▪️ 像这些比较容易解决的困难已经不多了。
▪️ 彻底的进展将来自对根本困难的处理——打造和组装复杂概念性结构要素。
最明显的实现这些的方法是,认为程序由比独立的高级语言语句、函数、模块或类等更大的概念结构要素组成。如果能对设计和开发进行限制,我们仅仅需要从已建成的集合中参数化这些结构要素,并把它们组装在一起,那么我们就能大幅度提高概念的级别,消除很多无谓的工作和大量语句级别的错误可能性。

Page 178 @ 12 February 2024

Parnas 的模块信息隐藏定义是研究项目中的第一步, 它是面向对象编程的鼻祖。 Parnas 把模块定义成拥有自身数据模型和自身操作集的软件实体。 它的数据仅仅能通过它自己的操 作来访问。第二步是若干思想家的贡献:把 Parnas 模块提升到抽象数据类型,从中可以派 生出很多对象。 抽象数据类型提供了一种思考和指明模块接口的统一方式, 以及容易保证实 施的类型规范化访问方法。 第三步,面向对象编程引入了一个强有力的概念——继承,即类(数据)默认获得类 继承层次中祖先的属性 14。我们希望从面向对象编程中得到的最大收获实际上来自第一步, 模块隐藏,以及预先建成的、为了重用而设计和测试的模块或者类库。

人就是一切(或者说,几乎是一切)

Page 180 @ 12 February 2024

人就是一切(或者说,几乎是一切)人就是一切(或者说,几乎是一切)很多读者发现很有趣的是,《人月神话》的大部分文章在讲述软件工程管理方面的事情,较少涉及到技术问题。这种倾向部分因为我在 IBM 360 操作系统(现在是 MVS/370)项目中角色的性质。更基本的是,这来自一个信念,即对于项目的成功而言,项目人员的素质、人员的组织管理是比使用的工具或采用的技术方法更重要的因素。

放弃权力的力量

Page 182 @ 12 February 2024

附属职能行使原理告诉我们——如果较低级别组织的自由和责任得以保留,中心权威 实际上是得到了加强;其结果是,从整体而言,组织机构实际上将“更加融洽和繁荣” 。 如何才能获得上述的架构?⋯⋯大型组织机构由很多准自治单元构成,我们称之为准 公司。它们中的每一个都拥有大量的自由,来为创造性和企业家职能提供最大的可能机 会⋯⋯。每个准公司同时具备盈亏帐目和资产负债表 20。 软件工程中最激动人心的进展是将上述组织理念付诸实践的早期阶段。首先,微型计 算机革命创造了新型的软件工业, 出现了成百上千的新兴公司。 所有这些小规模的公司热情、 自由和富有创造性。随着很多小型公司被大公司收购,这个产业正在发生着变化,而那些大 公司是否理解和保留小规模的创造性尚待分晓。 更不寻常的是,一些大型公司的高层管理已经开始着手将一些权力下放到软件项目团 队,使它们在结构和责任上接近于 Schumacher 的准公司。其运作的结果是令人欣喜和吃惊 的。 微软的 Jim AcCarthy 向我描述了他在解放团队上的经验: 每个队伍(30至 40人)拥有自己的任务、进度,甚至如何定义、构建、发布的过程。 团队由 4或 5个专家组成,包括开发、测试和书写文档等。由团队而不是老板对争论进行仲 裁。我无法形容授权和由团队自行负责项目的成功与否的重要性。

软件工程的状态和未来

Page 188 @ 12 February 2024

如同 Ferrell 故事所展现的,在几乎 50 年后,我仍被化学工程和软件工程之间的很多相似之处所震动。Parans 对我写的关于软件工程(softwareengineering)的文章提出了批评。他对比了电气工程和软件领域,觉得把我们所做的称为“工程”十分冒昧。他可能是正确的,这个领域可能永远不会发展成像电气工程那样的工程化领域,拥有精确的数学基础。毕竟,软件工程就像化学工程一样,与如何扩展到工业级别处理过程的非线性问题有关。而且,和工业工程类似,它总是被人类行为的复杂性所困扰。

Page 188 @ 12 February 2024

今天,软件工程的一些特殊问题正如第 1 章中所提出的:
▪️ 如何把一系列程序设计和构建成系统
▪️ 如何把程序或者系统设计成健壮的、经过测试的、文档化的、可支持的产品
▪️ 如何维持对大量的复杂性的控制
软件工程的焦油坑在将来很长一段时间内会继续地使人们举步维艰,无法自拔。软件系统可能是人类创造中最错综复杂的事物,只能期待人们在力所能及的或者刚刚超越力所能及的范围内进行探索和尝试。这个复杂的行业需要:进行持续的发展;学习使用更大的要素来开发;新工具的最佳使用;经论证的管理方法的最佳应用;良好判断的自由发挥;以及能够使我们认识到自己不足和容易犯错的——上帝所赐予的谦卑。


本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0协议 。转载请注明出处~