摘抄
第一章 - 焦油坑
- 卷首语:前车之覆,后车之鉴。
- 这,就是编程,一个让许多人痛苦挣扎的焦油坑,以及一项乐趣和苦恼共存的创造性活动。对许多人而言,编程所带来的快乐远远大于苦恼。本书的以下章节将试图在焦油上铺设一些木板路。
第二章 - 人月神话
- 卷首语:美食的烹调需要时间:片刻等待,更多美味,更多享受。-- 新奥尔良市安托万餐厅的菜单
- 在许多创造性活动中,往往很难掌握活动实施的介质,例如木头切割、油漆涂抹、电气接线等。这些介质的物理特性限制了思路的表达,它们同样对构思的实现带来了许多预料之外的困难。由于物理介质和思路中隐含不完善性,构思实现起来需要付出时间和精力。对遇到的大部分困难,我们总是倾向于责怪那些物理介质,因为物理介质不是“我们的”,而思路是“我们的”我们的自尊心使判断带上了主观色彩。然而,计算机编程往往基于十分容易掌握的介质,编程人员只需要通过纯粹的思维活动及灵活的表现形式来构建。正是由于介质易于驾驭,我们通常期待在实现过程中不会碰到困难,因此造成了乐观主义的弥漫。而我们的思路是有缺陷的,因此总会发现bug。也就是说,我们的乐观主义并不应该是理所应当的。
- 第二个存在谬误的思考方式是在时间估算和进度安排中使用的工作量单位:人月。成本的确随人数和月数的乘积而变化,进度却不是如此。因此我认为,用人月衡量一项工作规模的大小是一个危险和带有欺骗性的神话。它暗示着人员数量和时间是可以相互替换的。人员数量和时间的互换仅仅适用于以下情况:某个任务可以分解完全给参与人员,并且他们之间不需要相互的交流。这在收割小麦或采摘棉花的工作中是可行的,而在系统编程中近乎不可能。当任务由于次序上的约束而不能分解时,人手的添加对进度没有帮助。无论分配给多少女人,孕育一个生命都需要九个月。由于除错存在次序特性,因此许多软件任务都具有这种特征。
- 就像承诺在两分钟内微好一个煎蛋饼,看上去可能进行得非常好,但当它无法在两分钟内完成时,顾客只有两个选择:等待或者生吃。软件顾客也面临同样的选择。厨师还有另一个选择:他可以把火开大,不过结果常常是得到无法“挽救”的煎蛋饼--一面已经焦了,而另一面还是生的。现在,我并不认为软件经理内在的勇气和信心不如厨师,或者不如其他工程经理。但为了满足顾客期望的日期而造成的不合理进度安排,在软件领域却比其他任何工程领域要普遍得多。
- 向进度落后的软件项目增加人手,会使进度更加落后。(Adding manpower to a late software project makes it later.)
第三章 - 外科手术团队
- 卷首语:这些研究揭示,效率高和效率低的实施者之间个体差异非常大,经常能够达到数量级的水平。-- Sackman、Erikson、Grant
- Mills建议大型项目的每一部分由一个团队解决,但是该团队以类似外科手术团队的方式组建,而不是杀猪团队。也就是说,不是每个成员都拿刀乱砍,而是只有一个人操刀,其他人给予他各种支持,以提高效率和生产力。
- 10人程序开发团队的沟通模式:
- 首席程序员:团队里的外科主刀医生,他亲自定义功能和性能规约、设计程序、编制源代码、测试,以及编写文档。
- 副手:他是外科医生的后备,能做任何一部分工作,但是拥有的经验相对较少。
- 管理员:管理金钱、人员、空间和机器的专业人员,充当塑织中其他行政机构的接口。
- 编辑:编辑根据外科医生的草稿或者口述,进行分析和重新组织,提供各种参考信息和书目,对多个版本进行维护,并监督文档生成的机制。
- 文秘:管理员和编辑每个人需要一个文秘。管理员的文秘负责非产品文件和使项目协作一致。
- 程序文员:他负责维护编程产品库中团队的所有技术记录。该职员接受文秘性质的培训,承担机器可读以及人类可读文件的相关管理责任。
- 工具维护人员:保证他的外科医生得到所需要的工具。工具建造人员常常要构造专用的实用程序,并为过程以及宏库编目。
- 测试人员:测试人员既是从功能规约设计系统测试用例的敌手,也是为外科医生的日常调试设计测试数据的助手。他还负责计划测试序列和为组件测试搭建脚手架。
- 语言律师:掌握语言的复杂使用技巧,为团队寻找一种简有效的语言使用方法来解决困难、晦涩或者棘手的问题。
第四章 - 贵族制、民主制和系统设计
- 卷首语:大教堂是艺术史上无与伦比的成就。它所宣扬的理念既不乏未也不混乱……它是一种风格的巅峰之作。要完成这样一件艺术品,艺术家们要首尾融会贯通前辈的成果,同时也完全掌握他们那个时代的技术,并在运用这些技术时做到恰如其分,避免轻浮的炫耀,也绝不花哨。-- 《兰斯大教堂指南》
- 只有当这些功能规约节约下来的时间,比花费在学习、记忆和搜索手册上的时间要多时,易用性才会得到提高。对于现代编程系统来说,收益确实超过成本,但近年来随着越来越多的复杂功能被添加,收益和成本的比值似乎已经下降。由于目标是易用性(simplicity),功能与概念的复杂程度的比值才是系统设计的最终测试标准。单是功能本身或者简洁性都无法成为一个好的设计评判标准。然而这一点被广泛地误解了。
- 现在我们可以处理具有浓厚感情色彩的问题--贵族制对民主制。架构师难道不是新的贵族?他们是一些智力精英,专门指导可怜又愚蠢的实现人员要做什么?是否所有的创造性活动都被这些精英单独占有,实现人员仅仅是机器中的齿轮?难道不能遵循民主哲学,从整个团队中搜集好的创意,以得到更好的产品,不是将规约的开发工作仅限定于少教人?
- 第一个问题的答案是肯定的,若要得到系统概念的完整性必须有人控制这些概念。无须为这样的贵族制道歉。
- 第二个问题的答案是否定的,因为外部规约的编制工作并不比实现的设计工作更富有创造性,它只是一项性质不同的创造工作而已。
- 类似地,我观察到外部的架构规定实际上是增强,而不是限制实现小组的创造性。一旦他们将注意力集中在没有人解决过的问题上,创意就开始奔涌而出。在毫无限制的实现小组中,在进行架构上的决策时,会出现大量的想法和争议,对具体实现的关注反而会比较少。
- “最终,我们的编译器决定支持不经过改进和增强的语言,因为关于语言的争议已经耗费了我们所有的精力。”
第五章 - 第二系统效应
- 卷首语:聚沙成塔,集腋成裘。
- 在开发第一个作品时,架构师倾向于精炼和简洁。他知道自己对正在进行的任务不够了解,所以会谨慎、仔细地工作。在设计第一个作品时,他会面对不断产生的装饰和润色功能。这些功能都被留存在一边,以备“下次”使用。第一个系统迟会结束,而此时的架构师对这类系统充满了十足的信心,认为自已精通这一类别的系统,并且时刻准备开发第二个系统。
- 第二个系统是一个人所设计过的最危险的系统。而当他着手第三个及以后的系统时,先前的经验会相互验证,得到对此类系统通用特性的判断,而且系统之间的差异会帮助他识别出经验中不够通用的部分。
- 我对Stretch系统的印象是,从某种角度而言,它是一个产品线的终结。如同早期的计算机程序一样,它非常富有创造性,设计非常复杂,却非常高效。但不知为什么,我同时感觉到,它粗糙、浪费、缺乏优雅,并让人觉得必定存在某种更好的方法可以代替它。
- 架构师如何避免开发第二系统效应?显然,他无法跳过第二个系统,但他可以有意识地关注这个系统的特殊危险,并加以额外的自我约束,来避免那些对功能的过多修饰,并避免延伸出会因假设和目的的变化而废除的功能。
第六章 - 传递消息
- 卷首语:他只是坐在那里,嘴里说:“做这个!做那个!”当然,什么都不会发生,光说不做是没有用的。-- 哈里杜鲁门,《总统的权力》
- 手册不仅要描述包括所有界面在内的用户可见的一切,还要避免描述用户看不见的事物。后者是实现人员的工作范畴,他的设计自由必须不受约束。架构师必须随时准备展示他描述的任何特性的实现,但是他不应该试图指定实现方式。
- 规约的风格必须精确、充实和详细。用户常常会单独参考某个定义,所以每个定义必须重复所有的基本要素,但所有文字都要相互一致。这往往使手册读起来枯燥乏味,但是精确比生动更加重要。
- 一句古老的格言警告说:“不要携带两个时钟出海,而是带一个或三个。”同样的原则也适用于形式化和记叙性定义。
- 手册的编写者必须努力让自己的思路和语言达到所需要的精确程度。一种颇具吸引力的做法是对上述定义使用形式化标记方法。毕竟,精确度是我们需要的东西,这也正是形式化标记方法存在的理由。
- 对软件系统的架构师而言,存在一种美妙的方法来传播和推行定义。对于建立模块间接口语法,而非语义时,它特别有用这项技术是设计被传递参数或共享存储的声明,并要求实现通过编译时操作(PL/I的宏或%INCLUDE)来包含这些声明。
- 无须多说,会议是必要的。然而,数百人在场的大型磋商会议往往需要大规模和非常正式地召集。我们发现把会议分成两个级别很管用。第一个是每周半天的会议,所有架构师、硬件和软件实现人员的官方代表以及市场规划人员都参加。首席系统架构师主持。
- 然而,随着时间的推移,一些决定不再适用,一些小事情并没有被某个参与者真正地接受,其他决定造成了预想不到的问题。对于这些问题,有时每周会议并不赞同重新考虑。慢慢地,很多小要求、未解决的问题或者不愉快会堆积起来。为解决这些堆积起来的问题,我们会举行年度大会,典型的年度大会会持续两周。
- 归根结底,顾客才是独立的审计员。在残酷的现实使用环境中,每个细微缺陷都将无处遁形。产品测试小组则是顾客的代理人,专门寻找缺陷。不时地,细心的产品测试人员总会发现一些地方,在那里信息没有被传递、设计决策没有被正确理解或准确实现。因此,这样的测试小组是设计信息传递链中必要的一环,需要与设计一样,在早期同时运作。
第七章 - 为什么巴别塔会失败
- 卷首语:现在整个大地都采用一种语言,只包括为数不多的单词。在一次从东方往西方迁徙的过程中,人们发现了苏美尔地区的一处平原,并在那里定居下来。接着他们奔走相告说:“来,让我们制造砖块,并把它们烧好。”于是,他们用砖块代替石头,用沥青代替灰泥(建造房屋)。然后,他们又说:“来,让我们建造一座带有高塔的城市,这个塔将高耸入云,也将让我们声名远扬;同时,有了这个城市,我们就可以聚居在这里,再也不会分散在广阔的大地上了。”于是上帝决定下来看看人们建造的城市和高塔。看了以后,他说:“他们只是一个种族,使用一种语言,如果他们一开始就能建造城市和高塔,那么以后就没有什么能难得倒他们了。来,让我们下去,在他们的语言里制造一些混乱,让他们相互之间不能听懂。”这样,上帝把人们分散到世界各地,于是他们不得不停止建造那座城市。-- 创世纪,11:1-8
- 同时具有管理技能和技术技能的人很难找到。思考者很少,实干家更少,既是思考者又是实干家的人最罕见。
- 既然他们具备了所有的这些条件,为什么项目还会失败呢?他们还缺乏什么?缺乏两个方面:其一是交流;其二是交流的结果--组织。他们无法相互交谈,从而无法合作。当合作无法进行时,工作陷入了停顿。通过史书,我们推测出来,交流的缺乏导致争辩、沮丧和群体猜忌。很快,部落开始分裂--大家宁愿孤立也不愿互相争吵。
- 巴别塔可能是第一个工程灾难,但它不是最后一个。交流和交流的结果--组织,是成功的关键。交流和组织的技能需要管理者付出大量思考,并具备与软件技术本身同等丰富的经验能力。
第八章 - 胸有成竹
- 卷首语:经验是昂贵的老师,但愚人只能从经验学习。-- 穷理查年鉴
- 因此,对这样的短跑数据做线性外推是没有意义的。就好像把百码短跑时间外推,得出人类可以在3分钟之内跑完1英里的结论一样。
- 在将上述观点抛开之前,尽管不是为了进行严格的比较,我们仍然可以留意到一些事情。即使在不考虑交流、沟通,开发人员仅仅回顾自己以前工作的情况下,这些数字仍然显示出工作量是程序规模的幂函数。
第九章 - 削足适履
- 卷首语:作者应该看看诺亚,向他学习,像他在方舟中所做的那样,把大量的东西挤到一个非常小的容器内。-- 西德尼·史密斯,《爱丁堡评论》
- 没有人可以在自始至终提倡更紧密的软硬件设计集成的同时,又仅仅就规模本身对软件系统提出批评。
- 由于规模占编程系统产品用户成本中如此大的一个部分,建造者必须设置规模的目标,控制规模,考虑减小规模的方法,就像硬件建造者会设立元器件数量目标,控制元器件的数量,想出一些减少零件的方法。同任何开销一样,规模本身不是坏事,但不必要的规模是不可取的。
- 超越技艺的是创新,正是在这里诞生了精简、简洁、快速的程序。所有这些几乎都是战略突破的结果,而不是战术上的聪明。这种战略上的突破有时是一种新的算法,如Cooley-Tukey快速傅里叶变换,或者是将比较算法的复杂度从n降低到nlog"。更普遍的是,战略上的突破常来自数据或表的重新表达--这是程序的核心所在。给我看你的流程图,不给看你的表,我仍然会很迷惑。而如果给我看表,往往就不再需要流程图,程序结构是非常清晰的。
- 当程序员因空间不足而束手无策时,通常最好的办法是从自己的代码中挣脱出来,放松一下,思考自己的数据。表达是编程的本质。
第十章 - 提纲挈领
- 卷首语:前提:在堆积如山的文件资料中,少数文档是关键枢纽,每一件项目管理的工作都围绕着它们运转。这些文档是项目经理最重要的个人工具。
- 组织图与接口规约交织在一起,如同康威定律所预测的:“设计系统的组织受到限制以生产系统,而系统则是组织的通信结构的副本。”
- 经理的任务是制订计划,并实现计划。但是只有书面计划是精确和可以沟通的。计划中包括了时间、地点、人员、项目内容和资金。这些少量的关键文档封装了经理的大部分工作。如果一开始就认识到它们的普遍性和重要性,那么经理可以将它们视为友好的工具,而不是繁琐的琐事。通过开展文档工作,经理能更清晰和更快速地设定自己的方向。
第十一章 - 未雨绸缪
- 卷首语:不变只是愿望,变化才是永恒。-- 斯威夫特
- 卷首语:普遍的做法是,选择一种方法,试试看;如果失败了,没关系,再试试别的方法。不管怎么样,重要的是先去尝试。-- 富兰克林·罗斯福
- 因此,为舍弃而计划,无论如何,你一定要这样做。
- 然而,目标上的一些变化不可避免,事先为它们做准备总比假设它们不会出现要好得多。不但目标上的变化不可避免,而且设计策略和技术上的变化也不可避免。抛弃原型概念本身就是对事实的接受--随着学习的过程更改设计。
- Cosgrove主张把所有计划、里程碑和日程安排都当作尝试性的,以方便进行变化。这似乎有些走极端--现在软件编程小组失败的主要原因是管理控制得太少,而不是太多。他观察到不愿意为设计书写文档的原因,不仅仅是由于惰性或者时间压力。相反,设计人员通常不愿意提交尝试性的设计决策,再为它们进行辩解。
- 程序维护中的一个基本问题是,缺陷修复总会以固定(20%~50%)的概率引入新的bug。所以,整个过程是前进两步,后退一步。
- 为什么缺陷不能更彻底地被修复?首先,看上去很微小的错误,似乎仅仅是局部操作上的失败,实际上却是系统级别的同题,通常这不是很明显。修复局部问题的工作量很清晰,并且往往不大。但是,更大范围的修复工作常常会被忽视,除非软件结构很简单,或者文档书写得非常详细。其次,维护人员常常不是编写代码的开发人员,而是一些初级程序员或者新手。
- 作为引入新bug的一个后果,程序每条语句的维护需要的系统测试比其他编程要多。理论上,在每次修复之后,必须重新运行先前所有的测试用例,从而确保系统不会以更隐蔽的方式被破坏。实际情况中,回归测试必须接近上述理想状况,所以它的成本非常高。显然,使用能消除或至少能指明副作用的程序设计方法,会在维护成本上有很大的回报。同样,设计实现的人员越少、接口越少,产生的错误也就越少。
- 这正是历史的关键。使用卓越的能源,构建文明,成立杰出的机构,但是每次总会出现问题。自私和残酷的人类升到塔尖后,总有一些致命的缺陷,使得一切开始滑落,回到痛苦和废墟之中。实际上,机器失灵了。看上去,就好像是机器启动时一样正常,跑了几步,然后垮掉了。
- 系统软件开发是减少混乱度(减少熵)的过程,所以它本身是处于亚稳态的。软件维护是提高混乱度(增加熵)的过程,即使是最练的软件维护工作,也只是放缓了系统退化到非稳态的进程。
第十二章 - 干将莫邪
- 卷首语:巧匠因为他的工具而出名。--谚语
- 化学工程师很早就认识到,在实验室中可以进行的反应过程,并不能在工厂中一步实现。一个被称为“试验性工厂”(pilotplant)的中间步骤是非常必要的,它会为提高产量和在缺乏保护的环境下运作提供宝贵经验。例如,海水淡化的过程会先在产量为10000加仑/天的试验场所测试,然后再使用2000000加仑/天的净化系统投入生产。
- 上述方法尽管没有在任何理论中被提及,在实际情况中却一直如此。另外,同天文工作者一样,系统调试总是夜班性质的工作。20年前,当所有机房负责人在家中安睡时,我却不愿意严格遵守作息时间,黎明之前仍辛勤地工作。三代机器过去了,技术完全改变了,操作系统出现了,大家喜好的工作方式并没有改变。这种工作方式得以延续,是因为它的生产率最高。现在,人们已开始认识到它的生产力,并且敞开地接受这种富有成效的实践。
- 这里有两个重要的理念。首先是受控,即程序的备份由经理负责,他可以独立地授权程序的变更。其次是使发布的进展变得正式,以及开发库与集成、发布的正式分离。
- 在所有的工具中,最能节省劳动力的,可能是运行在可靠辅助平台上的、计算机化的文本编辑系统。
- 然而,目前还没有非常明显的证据来证明这些功能强大的工具的效力。正如人们所普遍认识的那样,调试是系统编程中较慢和较困难的部分,而漫长的调试周转时间是调试的祸根。就这一点而言,交互式编程的逻辑合理性是毋庸置疑的。
第十三章 - 整体部分
- 卷首语:我可以召唤地下的幽魂。这我也会,什么人都会,可是当您召唤它们的时候,它们会应召而来吗?-- 莎士比亚,《亨利四世》,第一部
- 和古老的神话一样,现代社会也总有一些爱吹嘘的人:“我可以编写控制航空运输、拦截导弹、管理银行账户、控制生产线的系统。”对这些人,回答很简单,“我也可以,任何人都可以,但是当你真的写了,系统就能成功运行吗?”如何开发一个可以运行的系统?如何测试系统?如何将经过测试的一系列构件集成到已测试过、可以依赖的系统?对这些问题,我们以前或多或少地提到了一些解决方法,现在就来更加系统地考虑一下。
- “关键的工作是产品定义。许多的失败完全是由那些产品未精确定义的地方而导致的。”-- V.A.Vyssotsky
- “他们不会告诉你他们不懂。相反,他们乐于自己摸索出解决问题和澄清疑惑的办法。”-- V.A.Vyssotsky
- 好的自上而下的设计从几个方面避免了bug。第一,清晰的结构和表达方式更容易对需求和模块功能进行精确描述。第二,模块分割和模块独立性避免了系统级的bug。第三,细节的抑制使结构上的缺陷更加容易识别。第四,设计在每个精化步骤上都是可以测试的,所以测试可以尽早开始,并且每个步骤的重点可以放在合适的级别上。
第十四章 - 祸起萧墙
- 卷首语:带来坏消息的人不受欢迎。--索福克勒斯
- 卷首语:项目怎么会被延迟了整整一年的时间?……延迟的时间是一天天积累下来的。
- 当人们听到某个项目的进度发生了灾难性偏离时,可能会认为项目一定遭受了一系列重大灾难。然而,通常灾祸来自白蚁的肆虐,而不是龙卷风的侵袭。同样,项目进度经常以一种难以察觉,但是残酷无情的方式慢慢落后。实际上,重大灾害是比较容易处理的,它往往和重大的压力、彻底的重组、新技术的出现有关,整个项目组通常可以应付自如。但是每天的进度落后是难以识别、不容易防范和难以弥补的。
- 如何根据一个严格的进度表来控制大型项目?第一步是制定进度表。进度表上的每一件事被称为“里程碑”,它们都有一个计划完成日期。确定日期是一个估计技术上的问题,在前面已经讨论过,它在很大程度上依赖以往的经验。里程碑的选择只有一个原则,那就是里程碑必须是具体的、特定的、可度量的事件,能够进行清晰定义。
- 里程碑边界明显并且没有歧义,比容易被老板核实更为重要如果里程碑定义得非常明确,无法自欺欺人时,很少有人会就里程碑的进展弄虚作假。但是如果里程碑很模糊,老板就常常会德到一份与实际情况不符的报告。毕竟,没有人愿意承受坏消息。这种做法只是为了起到缓和的作用,并没有任何蓄意的欺骗。
- 不过,当项目经理了解到老板收到状态报告之后不会惊慌,或者不会越俎代庖时,他会逐渐提交真实的结果。如果老板把会见、评审、会议明显标记为状态检查(status-review)和“问题-行动”(problem-action)会议,并且相应控制自己的行为,这对整个过程会很有帮助。
- 对计划和控制职能进行适度的技术人力投资是非常值得费赏的。它在项目的贡献方式和直接开发软件产品方面差异较大。计划和控制小组作为监督人员,明确地指出了不易察觉的延迟,并强调关键的因素。他们是早期的预警系统,防止项目以一次一天的方式落后一年。
第十五章 - 另外一面
- 卷首语:不了解,就无法真正拥有。-- 歌德
- 卷首语:噢,赐予我朴素的评论者吧,他们不会因为过于深奥而让人困惑不解。-- 克雷布
- 不同用户需要不同级别的文档。某些用户仅仅偶尔使用程序,有些用户必须依赖程序,还有一些用户必须根据环境和目的的变动对程序进行修改。每个用户都需要一段对程序进行描述的文字。可是大多数文档只提供了很少的总结性内容,无法达到用户要求,就像描绘了树木,形容了树皮和树叶,但却没有一幅森林的图案。为了得到一份有用的文字描述,必须放慢脚步,稳妥地进行。
- 现实中、流程图被鼓吹的程度远大于它们的实际作用。我从没有看到过一个有经验的编程人员,在开始编写程序之前,会例行公事地绘制详尽的流程图。
- 数据处理的基本原理告诉我们,试图努力维持不同文件之间的同步关系,是一件非常费力不讨好的事情。更合理的方法是:每个数据项包含两个文件都需要的所有信息,采用指定的键值来区别,并把它们组合到一个文件中。
- 自文档化,即把文档整合到源程序。这对正确维护是直接、有力地推动,保证编程用户能方便、及时地得到文档资料。
- 因为是机器为人服务的,而不是人为机器服务的。
第十六章 - 没有银弹--软件工程中的根本和次要问题
- 卷首语:在未来的十年内,无论是在技术上还是在管理方法上,都看不出有任何突破性的进步,能够保证在十年内大幅度地提高软件的生产率、可靠性和简洁性。
- 不过,怀疑论者并不是悲观主义者。尽管我们没有看见令人惊异的突破,并认为这种银弹实际上是与软件的内在特性相悖的,不过还是出现了一些令人振奋的革新。这些方法的规范化、持续地开拓、发展和传播确实是可以在将来使生产率产生数量级的高。虽然没有通天大道,但是路就在脚下。
- 解决管理灾难的第一步是将大块的“巨无霸理论”替换成“微生物理论”,它的每一步--希望的诞生,本身就是对一蹴而就型解决方案的冲击。它告诉工作者进步是逐步取得的,要伴随着辛勤的劳动,对规范化过程进行持续不懈的努力。由此,诞生了现在的软件工程。
- 不仅仅是在目力所及的范围内没有发现银弹,软件的特性本身也导致不大可能有任何的发明创新--能够像计算机硬件工业中的电子器件、晶体管、大规模集成一样--提高软件的生产率可靠性和简洁程度。我们甚至不能期望每两年有两倍的增长。首先,我们必须看到这样的畸形并不是由于软件发展得太慢,而是因为计算机硬件发展得太快。从人类文明开始,没有任何其他产业的的性价比,能在30年之内取得6个数量级的提高,也没有任何一个产业可以在性能提高或者成本降低方面取得如此的进步。
- 我认为软件开发中困难的部分是规格说明、设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程度进行验证。当然,我们还会犯一些语法错误,但是与绝大多数系统中的概念错误相比,它们是微不足道的。如果这是事实,软件开发总是非常困难的,天生就没有银弹。
- 爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专横武断或反复无常的。软件工程师却无法从类似的信念中获得安慰,他必须掌握的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。它们随接口的不同而改变,随时间的推移而变化,而且,这些变化不是必需的,仅仅由于它们是不同的人--而非上帝设计的结果。
- 软件是不可见的和无法可视化的。软件的客观存在不具有空间的形体特征。因此,没有已有的几何表达方式,就像陆地海洋有地图,硅片有膜片图,计算机有电路图一样。当我们试图用图形来描述软件结构时,发现它是很多相互关联、重叠在一起的图形。这些图形可能代表控制流程、数据流、依赖关系、时间序列和名字空间的相互关系等。它们通常不是有较少层次的扁平结构。
- Parnas澄清了术语上的混乱:现在有两种差异非常大的AI定义被广泛使用。AI-1:使用计算机来解决以前只能通过人类智慧解决的问题。AI-2:使用启发式或基于规则的特定编程技术。在这种方法中,对人类专家进行了研究,判断他们解决方法的启发性思维或者经验法则……程序被设计成以人类解决问题的方式来运作。第一种定义的意义容易发生变化……今天可能适合A-1定义的程序,一旦我们了解了它的运行方式,理解了问题,就不再认为它是人工智能……遗憾的是,我无法识别这个领域的特定知识体系…绝大多数工作是针对问题域的,我们需要一些抽象或者创造性来解决上述问题。
- 我完全同意这种批评意见。语音识别技术与图像识别技术的共同点非常少,它们又都与专家系统中应用的技术不同。例如,我觉得很难去发现图像识别技术能给编程开发实践带来什么样的差异。同样,语音识别也差不多--软件开发上的困难是决定说什么,而不是如何说。表达的简化仅仅能提供少量的促进作用。
- 专家系统最强有力的贡献是给缺乏经验的开发人员提供服务,用最优秀的开发者的经验和知识积累为他们提供指导,这是非常大的贡献。最优秀和一般的软件工程实践之间的差距是非常大的,可能比其他工程领域中的差距都要大,一种传播优秀实践的工具特别重要。
- 一句话,自动编程总是成为一种热情,使用现在并不可用的更高级语言编程的热情。
- 如果和我所认为的一样,工作的创造性部分占据了大部分时间,那些仅仅是表达概念的活动并不能在很大程度上影响生产率。
- 现在很多用户天天操作计算机,使用着各种各样的应用程序,但并不编写代码。事实上,他们中很多人无法为自己的计算机编写任何程序,不过他们非常熟练地使用计算机来解决新问题。
- 事实上,客户不知道自己需要什么。他们通常不知道哪些问题是必须回答的。并且,连必须确定的问题细节常常根本不予考虑,甚至只是简单地回答--“开发一个类似于我们已有的手工信息处理过程的新软件系统”。
- 让我们转向自然界,研究一下生物的复杂性,而不是人们的僵硬工作。我们会发现它们的复杂程度令我们敬畏。仅是大脑本身,就比任何对它的描述都要复杂,比任何的模拟仿真都要强大,它的多样性、自我保护和自我更新能力异常丰富和有力。其中的秘密就是逐步发育成长,而不是一次性搭建。所以,我们的软件系统也应该以增量的方式开发。也就是说,首先系统应该能够运行,即使未完成任何有用功能,只能正确调用一系列伪子系统。接着,系统一点一点被充实,子系统轮流被开发,或者是在更低的层次调用程序、模块、子系统的占位符(伪程序)等。
- 如何培养杰出的设计人员呢?限于篇幅,不允许进行较长的介绍,但有些步骤是显而易见的。
- 尽可能早地、系统地识别顶级的设计人员,最好的通常不是最有经验的人员。
- 为设计人员指派一位职业导师,负责他们技术方面的成长仔细地为他们规划职业生涯。
- 为每个方面制订和维护一份职业计划,包括与设计大师的、经过仔细挑选的学习过程,正式的高级教育和短期的课程--所有这些都穿插在设计和技术领导能力的培养安排中。
- 为成长中的设计人员提供相互交流和激励的机会。
第十七章 - 再论“没有银弹”
- 卷首语:生死有命,富贵在天。-- 威廉三世,奥兰治亲王
- 卷首语:任何人若想看到一件完美无瑕的作品,他所想的那种作品过去不存在,现在和将来也不会出现。-- 亚历山大·蒲柏,《批判论文》
- 就我的经验而言,在系统工作中所遇到的大多数复杂性是组织结构上一些失误的征兆。试图为这些现实建模,建立同等复杂的程序,实际上是隐藏,而不是解决这些杂乱无章的情况。
- 我曾作为物理学家接受过培训,因此倾向于用更简单的概念来描述“复杂”事物。现在你可能是正确的,我无法断定所有的复杂事物都容易用有序的规律表达……同样的道理,你不能断定它们不能。
- 在所有被误导的科学探索中,最悲惨的莫过于对一种能够将一般金属变成金子的物质,即点金石的研究。这个由统治者不断地投入金钱,被一代代的研究者不懈追求的、炼金术中至高无上的法宝,是一种从理想化想象和普遍假设中--以为事情会像我们所认为的那样--提取出的精华。它是人类纯粹信仰的体现,人们花费大量的时间和精力来认可和接受这个无法解决的问题。即使被证明是不存在的,那种寻找出路和希望能一劳永逸的愿望,依然十分强烈。而我们中的绝大多数总是很同情这些明知不可为而为之的人的勇气,因此它们总是能得以延续。所以,将圆形变方的论文被发表,抑制脱发的洗液被研制和出售,提高软件生产率的方法被提出并成功地推销。我们太倾向于遵循我们自己的乐观主义(或者是发掘我们出资人的乐观主义)。我们太喜欢忽视真理的声音,而去听从万灵药贩卖者的诱惑了。
- 面向对象编程的第一个特征是,强制的模块化和清晰的接口。其次,它强调了封装,即外界无法看到组件的内部结构;它还强调了继承和伴随着的层次化类结构以及虚函数。面向对象还强调了强抽象数据类型化,它确保某种特定的数据类型只能由自身的相应函数来操作。
- 我们推测重用的障碍不在生产者一边,而在消费者一边。如果一个软件工程师,潜在的标准化软件构件消费者,觉得寻找能满足他需要的构件进行验证比自行编写的代价更加昂贵时,重复的构件就会产生。注意我们上面提到的“觉得”。它和重新开发的真正投入无关。
- 重用是一件说起来容易,做起来难的事情。它同时需要良好的设计和卓越的文档。即使我们看到了非常罕见的优秀设计,但如果没有好的文档,我们也不会看到能重用的构件。
- 思索的层次越高,所需要处理的基本思考要素也就越多。因此,编程语言比机器语言更加复杂,而自然语言的复杂程度更高。高级语言有更广泛的词汇量、更复杂的语法以及更加丰富的语义。
- 对软件重用问题,我们需要研究适当的学问,了解人们如何拥有语言。一些经验教训是显而易见的:
- 人们在上下文中学习,因此我们需要出版一些复合产品的例子,而不仅仅是零部件的库;
- 人们只会记忆背诵单词,而语法和语义是在上下文中通过使用逐渐地学习的;
- 人们根据语义的分类对词汇组合规则进行分组,而不是通过比较对象子集。
- 又怎么样呢?Parnas和Brooks不是已经告诉我们了吗?软件开发是一件棘手的事情,前方并不会有魔术般的解决方案。现在是从业者研究和分析革命性进展的时候,而不是等待或希望它出现。软件领域中的一些人发现这是一幅使人泄气的图画。他们是那些依然认为突破近在眼前的人们。
- 但是我们中的一些--那些非常固执,以至于认为我们是现实主义者的人--把它看成是清新的空气。我们终于可以将焦点集中在更加可行的事情上,而不是空中的馅饼。现在,有可能,我们可以在软件生产率上取得逐步的进展,而不是等待不大可能到来的突破。
第十八章 - 《人月神话》的观点:是与非
- 卷首语:我们理解也好,不理解也好,描述都应该简短、精炼。-- 塞缪尔·勃特勒,《休迪布拉斯》
- 编程行业“满足我们内心深处的创造渴望和愉悦所有人的共有情感”,其提供了5种乐趣:
- 创建事物的快乐;
- 开发对其他人有用的东西的乐趣;
- 将可以活动、相互啮合的零部件组装成类似迷官的东西,这个过程所体现出令人神魂颠倒的魅力;
- 面对不重复的任务,不断学习的乐趣;
- 工作在如此易于驾驭的介质上的乐趣--纯粹的思维活动--其存在、移动和运转方式完全不同于实际物体。
- 同样,这个行业具有一些内在固有的苦恼:
- 将做事方式调整到追求完美是学习编程的最困难部分;
- 由其他人来设定目标,并且必须依靠自己无法控制的事物(特别是程序);权威不等同于责任;
- 实际情况看起来要比这点好一些:真正的权威来自于每次任务的完成;
- 任何创造性活动都伴随着枯燥艰苦的劳动,编程也不例外;。
- 人们通常期望项目在接近结束时,软件项目能收敛得快一些,然而,情况却是越接近完成,收敛得越慢;
- 产品在完成前总面临着陈旧过时的威胁;只有实际需要时,才会用到最新的设想。
- 缺乏合理的时间进度是造成项目滞后的最主要原因,它比其他所有因素的总和影响还大。
- 良好的烹饪需要时间,某些任务无法在不损害结果的情况下加快速度。
- 所有的编程人员都是乐观主义者:“一切都将运作良好”。由于编程人员通过纯粹的思维活动来开发,我们期待在实现过程中不会碰到困难。但是,我们的构思本身是有缺陷的,因此总会有bug。
- 围绕着成本核算的估计技术,混淆了工作量和项目进展。人月是危险和带有欺骗性的神话,因为它暗示人员数量和时间是可以相互替换的。
- 向软件项目中增派人手从三个方面增加了项目必要的总体工作量:任务重新分配本身和所造成的工作中断;培训新人员;额外的相互沟通。
- 小型、精干队伍是最好的--思绪尽可能少。
- 两个人的团队,其中一个是领导者,常常是最佳的人员使用方法(留意一下上帝对婚姻的设计)。
- 一位首席程序员、类似于外科手术队伍的团队架构提供了一种方法--既能获得由少数头脑产生的产品完整性,又能得到多位协助人员的总体生产率,还彻底地减少了沟通的工作量。
- 功能与理解上的复杂程度的比值才是系统设计的最终测试标准”,而不仅仅是丰富的功能。(该比值是对易用性的一种测量,由简单应用和复杂应用共同验证。)
- 如果要得到系统概念上的完整性,就必须有人控制这些概念。这实际上是一种无需任何歉意的贵族专制统治。
- “因为左手不知道右手在做什么,从而进度灾难、功能的不合理和系统缺陷纷纷出现。”由于存在对其他人的各种假设,团队成员之间的理解开始出现偏差。
- Parnas强烈地认为使每个人看到每件事的目标是完全错误的;各个部分应该被封装,从而没有人需要或者被允许看到其他部分的内部结构,只需要了解接口。
- 团队组织的目标是为了减少必要的交流和协作量。
- 每个子项目具有两个领导角色--制作人,技术总监或架构师。这两个角色的职责有很大的区别,需要不同的技能。
- 仅仅通过对编码部分时间的估计,然后乘以其他部分的相对系数,是无法得出对整项工作的精确估计的。
- 精炼、充分和快速的程序往往是战略性突破的结果,而不仅仅是技巧上的提高。
- 更普遍的是,战略上的突破常来自于对数据或表的重新表达。数据的表现形式是编程的根本。
- 软件产品易于掌握的特性和不可见性,导致它的构建人员(特别容易)面临着永恒的需求变更。
- 高级语言的使用、编译时操作、通过引用的声明整合和自文档技术能减少变更引起的错误。
- 抛开理论不谈,一次分配给某个小组的连续的目标时间块被证明是最好的安排方法,比不同小组的穿插使用更为有效。
- 高级语言不仅提高了生产率,还改进了调试:bug更少而且更容易寻找。
- 有时必须回退,推翻顶层设计,重新开始。
- 系统测试期间,一次只添加一个构件。
- 程序修改人员所使用的文档中,除了描述事情如何,还应阐述它为什么那样。对于加深理解,目的是非常关键的,即使是高级语言的语法,也不能表达目的。
- 软件系统可能是人类创造中最错综复杂的事物(从不同类型组成部分数量的角度出发)。
- 软件工程的焦油坑在将来很长一段时间内仍然会使人们举步维艰,无法自拔。
第十九章 - 20 年后的《人月神话》
- 卷首语:只能根据过去判断将来。-- 帕特里克·亨利
- 卷首语:然而永远无法根据过去规划将来。-- 埃德蒙·伯克
- 一个整洁、优雅的编程产品必须向它的每位用户提供一个条理分明的概念模型,这个模型描述了应用、实现应用的方法以及用来指明操作和各种参数的用户界面使用策略。用户所感受到的产品概念完整性是易用性中最重要的因素。
- 功能建议的吸引力在初期阶段是很明显的,性能代价在系统测试时才会出现。而随着功能一点一点地增加,手册慢慢地变厚易用性损失以不易察觉的方式蔓延。
- WIMP是一个充分体现了概念完整性的用户界面的例子,完整性的获得是通过采用大家非常熟悉的概念模型--对桌面的比喻和一致、细致的扩展,后者充分发挥了计算机的图形化实现能力。
- 首先应该构建实时系统的基本轮询回路,为每个功能提供子函数调用(占位符),但仅仅是空的子函数。对它进行编译、测试,可以使它不断运行。它不直接完成任何事情,但至少是正常运行的。
- 通过权力委派,中心的权威实际上是得到了加强;就整体而言,组织机构实际上更加融洽和繁荣。
- 软件工程的焦油坑在将来很长一段时间内会继续使人们举步维艰,无法自拔。软件系统可能是人类创造中最错综复杂的事物,只能期待人们在力所能及的或者刚刚超越力所能及的范围内进行探索和尝试。这个复杂的行业需要:进行持续的发展;学习使用更大的要素来开发;新工具的最佳使用;经论证的工程管理方法的最佳应用;良好的自我判断以及能够使我们认识到自己的不足--谦逊的品格。
结束语
- 在计算机技术进步的同时,计算机相关学科知识也在飞速发展。当我在20世纪50年代中期刚从学校毕业的时候,能看完当时所有的期刊和会议报告,掌握所有的潮流动向。而我现在只能对层出不穷的学科分支遗憾地说“再见”,对我所关注的东西也越来越难以全部掌握。兴趣太多,令人兴奋的学习、研究和思考的机会也太多--多么不可思议的矛盾啊!这个神奇的时代远远没有结束,它依然在飞速发展。更多的乐趣,尽在将来。
读后感
《人月神话》久负盛名,堪称软件工程项目管理领域的“圣经”。起初见到书名,很难联想到这是一本关于项目管理的著作——“神话”二字赋予它一层哲学般的朦胧色彩,尤其“人月”二字的组合令我不解其意,甚至让我一度以为这是一部探讨思维与创造的形而上之作。
翻开书页,却走入了一个经历丰富、经验深厚的作者世界。弗雷德里克·布鲁克斯以其在IBM System/360和OS/360项目中的宝贵经验,深入剖析了软件项目管理的复杂性与挑战。
关于一些豆瓣评论的看法
在豆瓣平台,我能看到一些评价认为此书“技术已过时”,“观点过时”。我认为这实则是一种望文生义的误读。他们忽略了这是一本五十年前的书,因而不能用现在的眼光去看待它。需要考虑当时的技术条件,当时的背景。更何况《人月神话》从来不是一本讲具体技术的书,它探讨的是软件工程中永恒的人性与组织问题。布鲁克斯在五十年前提出的诸多观点——如“人月神话”“外科手术团队”“没有银弹”——至今仍闪烁着惊人的预见性。
软件工程的核心困境,五十年来并未因技术进步而彻底解决。为什么?我觉得或许是因为这门学科的本质关乎人的思维与合作,而非单纯的技术叠加。正如布鲁克斯所指出的,软件开发的难点在于“概念上的构造”,而非代码的实现。只要人类依然参与其中,沟通、协作、认知负荷这些根本问题就会一直存在。
我的项目经历体会
作为一名学生,我虽未担任过架构师,也无缘领导中型项目,但在有限的比赛与小项目经历中,已能深切体会到布鲁克斯笔下的诸多困境:
- “人月神话”:项目中盲目加人并不能加快进度,反而可能因沟通成本增加而拖慢整体。人多未必好,甚至可能滋生“搭便车”心理。
- 文档之重:良好的文档至关重要,却极难写好。书中关于文档“精确胜于生动”的提醒,对我这样的写作者来说是宝贵的教诲。
- 沟通之难:每个人的知识背景不同,有效传递信息并非易事。巴别塔的隐喻在此显得格外深刻。
我之前参与比赛和进行个人项目时,软件项目管理地十分混乱,很多时候都是临时抱佛脚,最后赶工期。而且直观地感觉就是人多不一定好,有人摸鱼,而且有可能会有:“这个工作我不想做,想着别人会去做”这样的侥幸心理。一份优质的文档确实极其重要,但如何写出这样的文档却并不简单,而这本书中提供了一些颇具建设性的建议。至于沟通,我也很有感触——每个人的知识背景和理解框架各不相同,要把一件事清晰、完整地传达给他人,同样也并不是一件轻松的事情。长路漫漫啊...
AI时代的“外科手术团队”
如今,AI编程助手正掀起新一轮生产力革命。许多人预言,未来只需少数程序员配以AI,即可完成如今需团队协作的项目。这似乎与布鲁克斯提出的“外科手术团队”模型遥相呼应——只是如今,“副手”“工具维护者”“语言律师”等角色,有相当大一部分可以由AI承担。
然而,AI 并非“银弹”。虽然它在执行任务和辅助工作方面表现突出,但在需求理解、系统设计与架构决策等层面,目前仍需依赖人类的智慧。正如布鲁克斯所强调的:“概念完整性”必须由少数人来把握(对应当下大模型基于海量数据的学习结果)。如今 AI 的编程能力已相当惊人,但它确实仍存在局限,需要人类在整体上予以把控,尤其是在需求描述和系统设计方面。此外,一个完全没有编程经验的人可能很难准确表达需求,也不一定能用 AI 可理解的方式让它有效工作。因此,我认为程序员这个职业仍会存在,但需求可能会大幅减少,许多能力不足的程序员——比如只会调用的“调包侠”或仅能编写Toy Program的开发者——可能会被逐渐淘汰。未来的程序员,更应该去做那些 AI 难以胜任、需要创造力和设计能力的工作。根据我的使用体验,与 AI 的一次对话往往无法直接解决问题,通常需要经过多轮迭代和调整,才能逐步接近预期效果。所以在我看来,AI 尚不能完全替代人类程序员。最后还有一点不得不提:无论一个项目的人员如何精简,总归需要至少一个人来承担责任——毕竟,机器是没法“背锅”的(笑)。
想起之前看赵斌的视频他说的说:“我与AI共同完成一件事,比任何一个单独完成要好得多。”现在来看,这句话说的真是精辟啊。
那个年代
阅读时,我常想起那个编程仍用纸带、内存以KB计数的年代。虽然我对那个时代的了解大多来自于课本和科普视频,但依然能感受到那时的技术条件是多么的有限。
前辈们在如此有限的条件下,却构建出操作系统、编译器乃至整个软件工程的基础范式。那个时代的程序员,都是顶级的高智商人才。他们不仅要精通底层硬件,还要理解复杂的系统设计。想想此书成熟的年代,那些靠敲汇编语言,在狭小的屏幕,纸带存储,小的可怜的内存完成复杂工作的前辈。想想那些动辄就是议论重新设计语言,实现编译器的前辈。风淡云轻的写此书。堪称举重若轻,令人肃然起敬。
如今我们拥有强大的IDE、云服务、开源生态与AI工具,却不应忘记:工具再强,依然服务于人的思考与创造。今天的我们身处敏捷、DevOps、AI辅助的时代,却仍在“ Deadline ”与“需求蔓延”之间反复挣扎。
结语
合上书页,我感受到的不仅是对软件管理的理解加深,更是一种对创造本身的敬畏。《人月神话》没有给出“银弹”,却提供了一面镜子,让我们在焦油坑中看清自己、团队与系统的真相。它或许不会让你立刻成为更好的程序员,但很可能会让你成为一个更清醒的创造者,开始思考如何更有效地与他人合作,共同驾驭复杂的项目。本书作者弗雷德里克·布鲁克斯的洞见,跨越了时代的界限,依然闪耀着智慧的光芒,值得每一位软件从业者细细品味与深思。