阅读记录
- 很奇怪,书中说大部分程序员都是乐观主义者,但是我自以为自己不是,也是。
这本
书辗转 KOreader 和微信阅读以及 epub 和 pdf 版 共计三个版本,读完还是深有感触。因为近一段时间在做很多技术+管理交叉的工作,所以多很多内容深有感触,也给我在软件开发的策略方面深有启发。
- 快速原型等方法,快速让软件先有一个能看到的东西出来,在做迭代。而不要做建筑,一下子做一个很大的东西。这个观点给我很大启发。
阅读笔记1
人月神话(二十周年纪念版)
[美] 弗雷德里克·布鲁克斯
第一版序言(Preface to the First Edition)
Page 12 @ 30 June 2024 04:51:03 PM
++在很多方面,管理一个大型的计算机编程项目和其它行业的大型工程很相似--比大多数程序员所认为的还要相似;在很多另外的方面,它又有差别--比大多数职业经理所认为的差别还要大。++
Page 14 @ 30 June 2024 04:53:03 PM
我相信由于人员的分工,大型编程项目碰到的管理问题和小项目区别很大;我相信关键需要是维持产品自身的概念完整性。
焦油坑(The Tar Pit)
Page 16 @ 30 June 2024 04:54:59 PM
过去几十年的大型系统开发就犹如这样一个焦油坑,很多大型和强壮的动物在其中剧烈地挣扎。
Page 16 @ 30 June 2024 04:55:19 PM
面上看起来好像没有任何一个单独的问题会导致困难,每个都能被解决,但是当它们相互纠缠和累积在一起的时候,团队的行动就会变得越来越慢
Page 17 @ 30 June 2024 04:55:59 PM
报纸上经常会出现这样的新闻,讲述两个程序员如何在经改造的简陋车库中,编出了超过大型团队工作量的重要程序。接着,每个编程人员准备相信这样的神话,因为他知道自己能以超过产业化团队的1000代码行/年的生产率来开发任何程序。
Page 18 @ 30 June 2024 04:59:09 PM
水平边界以下,程序变成编程产品(Programming Product)。这是可以被任何人运行、测试、修复和扩展的程序。它可以运行在多种操作系统平台上,供多套数据使用。要成为通用的编程产品,程序必须按照普遍认可的风格来编写,特别是输入的范围和形式必须扩展,以适用于所有可以合理使用的基本算法。接着,对程序进行彻底测试,确保它的稳定性和可靠性,使其值得信赖。这就意味着必须准备、运行和记录详尽的测试用例库,用来检查输入的边界和范围。此外,要将程序提升为程序产品,还需要有完备的文档,每个人都可以加以使用、修复和扩展。经验数据表明,相同功能的编程产品的成本,至少是已经过测试的程序的三倍。
Page 19 @ 30 June 2024 05:00:07 PM
回到图中,垂直边界的右边,程序变成编程系统(Programming System)中的一个构件单元。它是在功能上能相互协作的程序集合,具有规范的格式,可以进行交互,并可以用来组装和搭建整个系统。要成为系统构件,程序必须按照一定的要求编制,使输入和输出在语法和语义上与精确定义的接口一致。同时程序还要符合预先定义的资源限制--内存空间、输入输出设备、计算机时间。最后,程序必须同其它系统构件单元一道,以任何能想象到的组合进行测试。由于测试用例会随着组合不断增加,所以测试的范围非常广。因为一些意想不到的交互会产生许多不易察觉的bug,测试工作将会非常耗时,因此相同功能的编程系统构件的成本至少是独立程序的三倍。如果系统有大量的组成单元,成本还会更高。
Page 19 @ 30 June 2024 05:00:30 PM
右下部分代表编程系统产品(Programming Systems Product)。和以上的所有的情况都不同的是,它的成本高达九倍。然而,只有它才是真正有用的产品,是大多数系统开发的目标。
Page 20 @ 30 June 2024 05:00:55 PM
首先是一种创建事物的纯粹快乐。如同小孩在玩泥巴时感到愉快一样,成年人喜欢创建事物,特别是自己进行设计。我想这种快乐是上帝创造世界的折射,一种呈现在每片独特、崭新的树叶和雪花上的喜悦
Page 20 @ 30 June 2024 05:01:16 PM
其次,快乐来自于开发对其他人有用的东西。内心深处,我们期望其他人使用我们的劳动成果,并能对他们有所帮助。从这个方面,这同小孩用粘土为"爸爸办公室"捏制铅笔盒没有本质的区别。
Page 20 @ 30 June 2024 05:01:29 PM
第三是整个过程体现出魔术般的力量--将相互啮合的零部件组装在一起,看到它们精妙地运行,得到预先所希望的结果。比起弹珠游戏或点唱机所具有的迷人魅力,程序化的计算机毫不逊色。
Page 20 @ 30 June 2024 05:01:42 PM
第四是学习的乐趣,来自于这项工作的非重复特性。人们所面临的问题,在某个或其它方面总有些不同。因而解决问题的人可以从中学习新的事物:有时是实践上的,有时是理论上的,或者兼而有之。
Page 21 @ 30 June 2024 05:02:06 PM
最后,乐趣还来自于工作在如此易于驾驭的介质上。程
Page 21 @ 30 June 2024 05:02:38 PM
编程非常有趣,在于它不仅满足了我们内心深处进行创造的渴望,而且还愉悦了每个人内在的情感。
Page 22 @ 30 June 2024 07:26:37 PM
首先,必须追求完美。因为计算机也是以这样的方式来变戏法:如果咒语中的一个字符、一个停顿,没有与正确的形式一致,魔术就不会出现。
Page 22 @ 30 June 2024 07:28:37 PM
*实际上,我认为学习编程的最困难部分,是将做事的方式往追求完美的方向调整。
其次,是由他人来设定目标,供给资源,提供信息。编程人员很少能控制工作环境和工作目标。用管理的术语来说,个人的权威和他所承担的责任是不相配的。不过,似乎在所有的领域中,对要完成的工作,很少能提供与责任相一致的正式权威。而现实情况中,实际(相对于正式)的权威来自于每次任务的完成。*
Page 22 @ 30 June 2024 07:29:15 PM
对于系统编程人员而言,对其他人的依赖是一件非常痛苦的事情。他依靠其他人的程序,而往往这些程序设计得并不合理,实现拙劣,发布不完整(没有源代码或测试用例),或者文档记录得很糟。所以,系统编程人员不得不花费时间去研究和修改,而它们在理想情况下本应该是可靠完整的。
Page 23 @ 30 June 2024 07:36:20 PM
概念性设计是有趣的,但寻找琐碎的bug却只是一项重复性的活动。
Page 23 @ 30 June 2024 07:36:45 PM
最后一个苦恼,有时也是一种无奈--当投入了大量辛苦的劳动,产品在即将完成或者终于完成的时候,却已显得陈旧过时。可能是同事和竞争对手已在追逐新的、更好的构思;也许替代方案不仅仅是在构思,而且已经在安排了。
Page 24 @ 30 June 2024 07:37:31 PM
实际产品需要一步一步按阶段实现。实现落后与否的判断应根据其它已有的系统,而不是未实现的概念。
Page 24 @ 30 June 2024 07:37:44 PM
我们所面临的挑战和任务是在现有的时间和有效的资源范围内,寻找解决实际问题的切实可行方案。
人月神话(The Mythical Man-Month)
Page 25 @ 30 June 2024 10:44:42 PM
在众多软件项目中,缺乏合理的时间进度是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还大。
Page 25 @ 30 June 2024 10:44:56 PM
首先,我们对估算技术缺乏有效的研究,更加严肃地说,它反映了一种悄无声息,但并不真实的假设--一切都将运作良好。
Page 25 @ 30 June 2024 10:45:06 PM
第二,我们采用的估算技术隐含地假设人和月可以互换,错误地将进度与工作量相互混淆。
Page 25 @ 30 June 2024 10:45:13 PM
第三,由于对自己的估算缺乏信心,软件经理通常不会有耐心持续地进行估算这项工作。
Page 26 @ 30 June 2024 10:45:28 PM
第四,对进度缺少跟踪和监督。其他工程领域中,经过验证的跟踪技术和常规监督程序,在软件工程中常常被认为是无谓的举动。
Page 26 @ 30 June 2024 10:45:37 PM
第五,当意识到进度的偏移时,下意识(以及传统)的反应是增加人力。这就像使用汽油灭火一样,只会使事情更糟。越来越大的火势需要更多的汽油,从而进入了一场注定会导致灾难的循环。
Page 26 @ 30 June 2024 10:46:17 PM
所有的编程人员都是乐观主义者。可能是这种现代魔术特别吸引那些相信美满结局的人;也可能是成百上千琐碎的挫折赶走了大多数人,只剩下了那些习惯上只关注结果的人;还可能仅仅因为计算机还很年轻,程序员更加年轻,而年轻人总是些乐观主义者--无论是什么样的程序,结果是勿庸置疑的:"这次它肯定会运行。"或者"我刚刚找出了最后一个错误。"
Page 26 @ 30 June 2024 10:46:29 PM
所以系统编程的进度安排背后的第一个假设是:一切都将运作良好,每一项任务仅花费它所"应该"花费的时间。
Page 27 @ 30 June 2024 10:46:51 PM
Dorothy Sayers在她的"The Mind of the Maker"一书中,将创造性活动分为三个阶段:构思、实现和交流
Page 27 @ 30 June 2024 10:55:32 PM
书籍、计算机、或者程序的出现,首先是作为一个构思或模型出现在作者的脑海中,它与时间和空间无关
Page 27 @ 30 June 2024 10:55:43 PM
接着,借助钢笔、墨水和纸,或者电线、硅片和铁氧体,在现实的时间和空间中实现它们
Page 27 @ 30 June 2024 10:56:05 PM
然后,当某人阅读书本、使用计算机和运行程序的时候,他与作者的思想相互沟通,从而创作过程得以结束
Page 27 @ 30 June 2024 10:57:39 PM
对于创造者,只有在实现的过程中,才能发现我们构思的不完整性和不一致性。因此,对于理论家而言,书写、试验以及"工作实现"是非常基本和必要的。
Page 27 @ 30 June 2024 10:58:03 PM
在许多创造性活动中,往往很难掌握活动实施的介质,例如木头切割、油漆、电器安装等。这些介质的物理约束限制了思路的表达,它们同样对实现造成了许多预料之外的困难。
Page 28 @ 30 June 2024 10:58:58 PM
由于物理介质和思路中隐含的不完善性,实际实现起来需要花费大量的时间和汗水。
Page 28 @ 30 June 2024 10:59:12 PM
对遇到的大部分实现上的困难,我们总是倾向于去责怪那些物理介质,因为它们不顺应"我们"设定的思路。其实,这只不过是我们的骄傲使判断带上了主观主义色彩。
Page 28 @ 30 June 2024 10:59:46 PM
正由于介质的易于驾驭,我们期待在实现过程中不会碰到困难,因此造成了乐观主义的弥漫。而我们的构思是有缺陷的,因此总会有bug。也就是说,我们的乐观主义并不应该是理所应当的。
Page 29 @ 30 June 2024 11:02:00 PM
然而大型的编程工作,或多或少包含了很多任务,某些任务间还具有前后的次序,从而一切正常的概率变得非常小,甚至接近于无。
Page 29 @ 30 June 2024 11:04:53 PM
第二个谬误的思考方式是在估计和进度安排中使用的工作量单位:人月
Page 29 @ 30 June 2024 11:05:32 PM
成本的确随开发产品的人数和时间的不同,有着很大的变化,进度却不是如此。因此我认为用人月作为衡量一项工作的规模是一个危险和带有欺骗性的神话。它暗示着人员数量和时间是可以相互替换的。
Page 29 @ 30 June 2024 11:05:49 PM
人数和时间的互换仅仅适用于以下情况:某个任务可以分解给参与人员,并且他们之间不需要相互的交流(图2.1)。这在割小麦或收获棉花的工作中是可行的;而在系统编程中近乎不可能。
Page 31 @ 30 June 2024 11:07:02 PM
沟通所增加的负担由两个部分组成,培训和相互的交流。每个成员需要进行技术、项目目标以及总体策略上的培训。这种培训不能分解,因此这部分增加的工作量随人员的数量呈线性变化
Page 31 @ 30 June 2024 11:07:26 PM
相互之间交流的情况更糟一些。如果任务的每个部分必须分别和其他部分单独协作,则工作量按照n(n-1)/2递增。一对一交流的情况下,三个人的工作量是两个人的三倍,四个人则是两个人的六倍。而对于需要在三四个人之间召开会议、进行协商、一同解决的问题,情况会更加恶劣。所增加的用于沟通的工作量可能会完全抵消对原有任务分解所产生的作用,此时我们会被带到图2.4的境地。
Page 32 @ 30 June 2024 11:08:26 PM
因为软件开发本质上是一项系统工作--错综复杂关系下的一种实践--沟通、交流的工作量非常大,它很快会消耗任务分解所节省下来的个人时间。从而,添加更多的人手,实际上是延长了,而不是缩短了时间进度。
Page 33 @ 30 June 2024 11:09:07 PM
理论上,缺陷的数量应该为零。但是,由于我们的乐观主义,通常实际出现的缺陷数量比预料的要多得多。因此,系统测试进度的安排常常是编程中最不合理的部分。
Page 33 @ 30 June 2024 11:09:22 PM
*对于软件任务的进度安排,以下是我使用了很多年的经验法则:
1/3计划
1/6编码
1/4构件测试和早期系统测试
1/4系统测试,所有的构件已完成*
Page 34 @ 30 June 2024 11:10:32 PM
通过对传统项目进度安排的研究,我发现很少项目允许为测试分配一半的时间,但大多数项目的测试实际上是花费了进度中一半的时间。它们中的许多项目,在系统测试之前还能保持进度。或者说,除了系统测试,进度基本能保证
Page 34 @ 30 June 2024 11:10:53 PM
特别需要指出的是,不为系统测试安排足够的时间简直就是一场灾难。因为延迟发生在项目快完成的时候。直到项目的发布日期,才有人发现进度上的问题。因此,坏消息没有任何预兆,很晚才出现在客户和项目经理面前。
Page 35 @ 01 July 2024 12:55:57 AM
观察一下编程人员,你可能会发现,同厨师一样,某项任务的计划进度,可能受限于顾客要求的紧迫程度,但紧迫程度无法控制实际的完成情况。就像约好在两分钟内完成一个煎蛋,看上去可能进行得非常好。但当它无法在两分钟内完成时,顾客只能选择等待或者生吃煎蛋。软件顾客的情况类似。
Page 35 @ 01 July 2024 12:56:07 AM
厨师还有其他的选择:他可以把火开大,不过结果常常是无法"挽救"的煎蛋--一面已经焦了,而另一面还是生的。
Page 41 @ 02 July 2024 12:19:19 AM
在众多软件项目中,缺乏合理的时间进度是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还要大。
外科手术队伍(The Surgical Team)
Page 43 @ 03 July 2024 12:01:42 AM
需要协作沟通的人员的数量影响着开发成本,因为成本的主要组成部分是相互的沟通和交流,以及更正沟通不当所引起的不良结果(系统调试)。
Page 43 @ 03 July 2024 12:02:04 AM
*绝大多数大型编程系统的经验显示出,一拥而上的开发方法是高成本的、速度缓慢的、不充分的,开发出的是无法在概念
上进行集成的产品。*
Generated at: 2024-07-11-18-23-39
阅读笔记2
《人月神话(二十周年纪念版)》
[美] 弗雷德里克·布鲁克斯
18个笔记
◆ 外科手术队伍(The Surgical Team)
最好的和最差的表现在生产率上平均为10:1;在运行速度和空间上具有5:1的惊人差异!简言之,$20,000/年的程序员的生产率可能是$10,000/年程序员的10倍。
我常常重复这样的一个观点,需要协作沟通的人员的数量影响着开发成本,因为成本的主要组成部分是相互的沟通和交流,以及更正沟通不当所引起的不良结果(系统调试)
这一点,也暗示系统应该由尽可能少的人员来开发。实际上,绝大多数大型编程系统的经验显示出,一拥而上的开发方法是高成本的、速度缓慢的、不充分的,开发出的是无法在概念上进行集成的产品。OS/360、Exec 8、Scope 6600、Multics、TSS、SAFE等等--这个列表可以不断地继续下去。
得出的结论很简单:如果一个200人的项目中,有25个最能干和最有开发经验的项目经理,那么开除剩下的175名程序员,让项目经理来编程开发。
这种进退两难的境地是非常残酷的。对于效率和概念的完整性来说,最好由少数干练的人员来设计和开发,而对于大型系统,则需要大量的人手,以使产品能在时间上满足要求。如何调和这两方面的矛盾呢
Harlan Mills的提议提供了一个崭新的、创造性的解决方案2,3。Mills建议大型项目的每一个部分由一个团队解决,但是该队伍以类似外科手术的方式组建,而并非一拥而上
也就是说,同每个成员截取问题某个部分的做法相反,由一个人来进行问题的分解,其他人给予他所需要的支持,以提高效率和生产力
外科医生。Mills称之为首席程序员。他亲自定义功能和性能技术说明书,设计程序,编制源代码,测试以及书写技术文档。他使用例如PL/I的结构化编程语言,拥有对计算机系统的访问能力;该计算机系统不仅仅能进行测试,还存储程序的各种版本,以允许简单的文件更新,并对他的文档提供文本编辑能力。首席程序员需要极高的天分、十年的经验和应用数学、业务数据处理或其他方面的大量系统和应用知识
副手。他是外科医生的后备,能完成任何一部分工作,但是相对具有较少的经验。他的主要作用是作为设计的思考者、讨论者和评估人员。外科医生试图和他沟通设计,但不受到他建议的限制。副手经常在与其他团队的功能和接口讨论中代表自己的小组。他需要详细了解所有的代码,研究设计策略的备选方案。显然,他充当外科医生的保险机制。他甚至可能编制代码,但针对代码的任何部分,不承担具体的开发职责
管理员。外科医生是老板,他必须在人员、加薪等方面具有决定权,但他决不能在这些事务上浪费任何时间。因而,他需要一个控制财务、人员、工作地点安排和机器的专业管理人员,该管理员充当与组织中其他管理机构的接口。Baker建议仅在项目具有法律、合同、报表和财务方面的需求时,管理员才具有全职责任。否则,一个管理员可以为两个团队服务。
编辑。外科医生负责产生文档--出于最大清晰度的考虑,他必须书写文档。对内部描述和外部描述都是如此。而编辑根据外科医生的草稿或者口述的手稿,进行分析和重新组织,提供各种参考信息和书目,对多个版本进行维护以及监督文档生成的机制
两个秘书。管理员和编辑每个人需要一个秘书。管理员的秘书负责项目的协作一致和非产品文件。
程序职员。他负责维护编程产品库中所有团队的技术记录。该职员接受秘书性质的培训,承担机器码文件和可读文件的相关管理责任
所有的计算机输入汇集到这个职员处。如果需要,他会对它们进行记录或者标识。输出列表会提交给程序职员,由他进行归档和编制索引。另外,他负责将任何模型的最新运行情况记录在状态日志中,而所有以前的结果则按时间顺序进行归档保存
Mills概念的真正关键是"从个人艺术到公共实践"的编程观念转换。它向所有的团队成员展现了所有计算机的运作和产物,并将所有的程序和数据看作是团队的所有物,而非私人财产。
程序职员的专业化分工,使程序员从书记的杂事中解放出来,同时还可以对那些杂事进行系统整理,确保了它们的质量,并强化了团队最有价值的财富--工作产品
测试人员。外科医生需要大量合适的测试用例,用来对他所编写的工作片段,以及对整个工作进行测试。因此,测试人员既是为他的各个功能设计系统测试用例的对头,同时也是为他的日常调试设计测试数据的助手。他还负责计划测试的步骤和为测试搭建测试平台
语言专家。随着Algol语言的出现,人们开始认识到大多数计算机项目中,总有一两个乐于掌握复杂编程语言的人。这些专家非常有帮助,很快大家会向他咨询。这些天才不同于外科医生,外科医生主要是系统设计者以及考虑系统的整体表现。而语言专家则寻找一种简洁、有效的使用语言的方法来解决复杂、晦涩或者棘手的问题。他通常需要对技术进行一些研究(两到三天)。通常一个语言专家可以为两个到三个外科医生服务
阅读笔记3
《人月神话(40周年中文纪念版) (〔美〕弗雷德里克·布_ (Z-Library)》
弗雷德里克·布鲁克斯
97个笔记
◆ 外科手术队伍(The Surgical Team)
扩建过程的成功依赖于这样一个事实,即每个部分的概念完整性得到了彻底的提高--决定设计的人员是原来的七分之一或更少。所以,可以让200人去解决问题,而仅仅需要协调20个人,即那些"外科医生"的思路
要使工作易于管理,必须清晰地划分体系结构设计和实现之间的界线,系统结构师必须一丝不苟地专注于体系结构。总的说来,上述的角色分工和技术是可行的,在实际工作中,具有非常高的效率
◆ 贵族专制、民主政治和系统设计(Aristocracy, Democracy, and System Design)
对于计算机系统而言,尽管它们通常没有花费几个世纪的时间来构建,但绝大多数系统体现出的概念差异和不一致性远远超过欧洲的大教堂。这通常并不是因为它由不同的设计师们开发,而是由于设计被分成了由若干人完成的若干任务。
我主张在系统设计中,概念完整性应该是最重要的考虑因素。也就是说为了反映一系列连贯的设计思路,宁可省略一些不规则的特性和改进,也不提倡独立和无法整合的系统,哪怕它们其实包含着许多很好的设计。
编程系统(软件)的目的是使计算机更加容易使用。为了做到这一点,计算机装备了语言和各种工具,这些工具实际上也是被调用的程序,受到编程语言的控制。使用这些工具是有代价的:软件外部描述的规模大小是计算机系统本身说明的十倍。用户会发现寻找一个特定功能是很容易的,但相应却有太多的选择,要记住太多的选项和格式
只有当这些功能说明节约下来的时间,比用在学习、记忆和搜索手册上的时间要少时,易用性才会得到提高
现代编程系统节省的时间的确超过了花费的时间,但是近年来,随着越来越多的功能添加,收益和成本的比率正逐渐地减少
一旦以易用性作为衡量标准,单独的功能和易于使用都是不均衡的,都只达到了真正目标的一半。
对于给定级别的功能,能用最简洁和直接的方式来指明事情的系统是最好的。只有简洁(simplicity)是不够的,Mooers的TRAC语言和Algol 68用很多独特的基本概念达到了所需的简洁特性,但它们并不直白(straightforward)
简洁和直白来自概念的完整性。每个部分必须反映相同的原理、原则和一致的折衷机制。在语法上,每个部分应使用相同的技巧;在语义上,应具有同样的相似性。因此,易用性实际上需要设计的一致性和概念上的完整性。
概念的完整性要求设计必须由一个人,或者非常少数互有默契的人员来实现。
而进度压力却要求很多人员来开发系统。有两种方法可以解决这种矛盾。第一种是仔细地区分设计方法和具体实现。第二种是前一章节中所讨论的、一种崭新的组建编程开发团队的方法。
对于非常大型的项目,将设计方法、体系结构方面的工作与具体实现相分离是获得概念完整性的强有力方法。我亲眼目睹了它在IBM的Stretch计算机和360计算机产品线上的巨大成功。但同时我也看到了这种方法在360操作系统的开发中,由于缺乏广泛应用所遭受的失败。
系统的体系结构(architecture)指的是完整和详细的用户接口说明。对于计算机,它是编程手册;对于编译器,它是语言手册;对于控制程序,它是语言和函数调用手册;对于整个系统,它是用户要完成自己全部工作所需参考的手册的集合2。
结构师的工作,是运用专业技术知识来支持用户的真正利益,而不是维护销售人员所鼓吹的利益。
体系结构同实现必须仔细地区分开来。如同Blaauw所说的,"体系结构陈述的是发生了什么,而实现描述的是如何实现3。"他举了一个简单的例子--时钟。它的结构包括表面、指针和上发条的旋钮
而时钟的实现,描述了表壳中的事物--很多种动力提供装置中的一种,以及众多控制精度方案的一种
例如,在System/360中,一个计算机的结构可以用9种不同的模型来实现;而单个实现--Model 30的数据流、内存和微代码实现--可以用于4种不同的体系结构:System/360计算机、拥有224个独立逻辑子通道的复杂通道、选择通道以及1401计算机
我当然不认为只有结构师才有好的创意。新的概念经常来自实现者或者用户。然而,我一直试图表达,并且我所有的经验使我确信,系统的概念完整性决定了使用的容易程度。不能与系统基本概念进行整合的良好想法和特色,最好放到一边,不予考虑。如果出现了很多非常重要但不兼容的构想,就应该抛弃原来的设计,对不同基本概念进行合并,在合并后的系统上重新开始
就必须只能存在少数的结构师而言,答案是肯定的,他们的工作产物的生命周期比那些实现人员的产物要长,并且结构师一直处在解决用户问题,实现用户利益的核心地位
如果要得到系统概念上的完整性,那么必须控制这些概念。这实际上是一种无需任何歉意的贵族专制统治
第二个问题的答案是否定的,因为外部技术说明的编制工作并是比具体设计实现更富有创造性,它只是一项性质不同的创造工作而已。在给定体系结构下的设计实现,同样需要同编制技术说明一样的创造性、同样新的思路和卓越的才华。实际上,产品的成本性能比很大程度上依靠实现人员,就如同易用性很大程度上依赖结构师一样。
如同某艺术家的格言所述,"没有规矩,不成方圆。"最差的建筑往往是那些预算远远超过起始目标的项目
巴赫曾被要求每周创作一篇形式严格的歌剧,但这似乎并没有被压制他的创造性。并且,我确信如果Stretch计算机有更严格的限制,那么该计算机会拥有更好的体系结构。就我个人意见而言,System/360 Model 30预算上的限制,完全获益于Model 75的体系结构。
我曾见过很多次这样的结果,R.W.Conway也证实了这一点。他在Cornell的小组曾编制PL/I语言的编译器。他说:"最后我们的编译器决定支持不经过改进和增强的语言,因为关于语言的争议已经耗费了我们所有的时间和精力
概念完整性的缺乏导致系统开发和修改上要付出更昂贵的代价,我估计至少增加了一年的调试时间。
当建议由体系结构的团队来编写计算机和编程系统的所有外部技术说明时,编程人员提出了三个反对意见:.. 该说明中的功能过于繁多,而对实际情况中的成本考虑比较少.. 结构师获得了所有创造发明的快乐,剥夺了实现人员的创造力.. 当体系结构的队伍缓慢工作时,很多实现人员只能空闲地坐着等待
具体实现中创造和发明的机会,并不会因为指定了外部技术说明而大为减少,相反创造性活动会因为规范化而得到增强,整个产品也一样。
最后一个反对意见是时间顺序和阶段性上的问题。问题的简要回答是,在说明完成的时候,才雇用编程实现人员。这也正是在搭建一座建筑时所采用的方法。
如同Blaauw所指出的,整个创造性活动包括了三个独立的阶段:体系结构(architecture)、设计实现(implementation)、物理实现(realization)。
在计算机的设计中,一旦设计实现人员有了对手册的模糊设想,对技术有了相对清晰的构思以及拥有了定义良好的成本和目标时,工作就可以开始了
他可以开始设计数据流、控制序列、大体的系统划分等等。同时,还需要选用工具以及进行相应的调整,特别是记录存档系统和设计自动化系统。
同时,在物理实现的级别,电路、板卡、线缆、机箱、电源和内存必须分别设计、细化和编制文档。这项工作与体系结构及设计实现并行进行。。
在编程系统的开发中,这个原理同样适用。在外部说明完成之前,设计实现人员有很多的事情可以做。只要有一些最终将并入外部说明的系统功能雏形,他就可以开始了
首先,必须设定良好定义的时间和空间目标,了解产品运行的平台配置。接着,他可以开始设计模块的边界、表结构、算法以及所有的工具。另外,还需要花费一些时间和体系结构师沟通。
同时,在物理实现的级别,也有很多可以着手的工作。编程也是一项技术,如果是新型的机器,则在库的调整、系统管理以及搜索和排序算法上,有许多事情需要处理
概念的完整性的确要求系统只反映唯一的设计理念,用户所见的技术说明来自少数人的思想。实际工作被划分成体系结构、设计实现和物理实现,但这并不意味着该开发模式下的系统需要更长的时间来创建。经验显示恰恰相反,整个系统将会开发得更快,所需要的测试时间将更少。同工作的水平分割相比,垂直划分从根本上大大减少了劳动量,结果是使交流彻底地简化,概念完整性得到大幅提高。
◆ 画蛇添足(The Second-System Effect)
建筑行业的结构设计师使用估算技术来编制预算,该估算技术会由后续的承包商报价来验证和修正。承包商的报价总会超过预算。接下来,设计师会重新改进他的预算或修订设计,调整到下一期工程。他也可能会向承包商建议,使用更加便宜的方法来实现设计。
想要成功,结构师必须.. 牢记是开发人员承担创造性和发明性的实现责任,所以结构师只能建议,而不能支配;.. 时刻准备着为所指定的说明建议一种实现的方法,同样准备接受其他任何能达到目标的方法;.. 对上述的建议保持低调和平静;.. 准备放弃坚持所作的改进建议;
当正在实现产品时,某些特性的修改会造成意料不到的成本开销
在开发第一个系统时,结构师倾向于精炼和简洁。他知道自己对正在进行的任务不够了解,所以他会谨慎仔细地工作。
在设计第一个项目时,他会面对不断产生的装饰和润色功能。这些功能都被搁置在一边,作为"下一个"项目的内容。第一个项目迟早会结束,而此时的结构师,对这类系统充满了十足的信心,熟练掌握了相应的知识,并且时刻准备开发第二个系统
第二个系统是设计师们所设计的最危险的系统。而当他着手第三个或第四个系统时,先前的经验会相互验证,得到此类系统通用特性的判断,而且系统之间的差异会帮助他识别出经验中不够通用的部分。
一种普遍倾向是过分地设计第二个系统,向系统添加很多修饰功能和想法,它们曾在第一个系统中被小心谨慎地推迟了。结果如同Ovid所述,是一个"大馅饼"。
例如,后来被嵌入到7090的IBM 709系统,709是对非常成功和简洁的704系统进行升级的二次开发项目。709的操作集合被设计得如此丰富和充沛,以至于只有一半操作被常规使用。
让我们来看看更严重的例子--Stretch计算机的结构(architecture)、设计实现(implementation)、甚至物理实现(realization),它是很多人被压抑创造力的宣泄出口
OS/360是典型的第二次开发(second-system effect)的例子,是软件行业的Stretch系统。Strachey的赞誉和批评可以毫无更改地应用在其中。
例如,OS/360开发了26字节的常驻日期翻转例程来正确地处理闰年的12月31日的问题,其实它完全可以留给操作员来完成。
结构师如何避免画蛇添足--开发第二个系统所引起的后果(second-system effect)?是的,他无法跳过二次系统。但他可以有意识关注那些系统的特殊危险,运用特别的自我约束准则,来避免那些功能上的修饰;根据系统基本理念及目的变更,舍弃一些功能。
一个可以开阔结构师眼界的准则是为每个小功能分配一个值:每次改进,功能x不超过m字节的内存和n微秒。这些值会在一开始作为决策的向导,在物理实现期间充当指南和对所有人的警示。
项目经理如何避免画蛇添足(second-system effect)?他必须坚持至少拥有两个系统以上开发经验结构师的决定。同时,保持对特殊诱惑的警觉,他可以不断提出正确的问题,确保原则上的概念和目标在详细设计中得到完整的体现。
◆ 贯彻执行(Passing the Word)
我想我所见过的最好的一份手册是System360 Principles of Operation的附录
它精确仔细地规定了System/360兼容性的限制。它定义了兼容性,描述了将达到的目标,列举了很多外部显示的各个部分:源于某个模型与其他模型差异,带来变化的部分和保持不变的部分;或者是某个给定模型的拷贝不同于其他拷贝的地方;甚至是工程上的变更引起拷贝自身上的差异。而这正是一个规格说明作者所应该追求的精确程度,他必须在仔细定义规定什么的同时,定义未规定什么
形式化定义是精确的,它们倾向于更加完整;差异得更加明显,可以更快地完成。但是形式化定义的缺点是不易理解
记叙性文字则可以显示结构性的原则,描述阶段上或层次上的结构,以及提供例子。它可以很容易地表达异常和强调对比的关系,最重要的是,它可以解释原因。在表达的精确和简明性上,目前所提出的形式化定义,具有了令人惊异的效果,增强了我们进行准确表达的信心。但是,它还需要记叙性文字的辅助,才能使内容易于领会和讲授
一句古老的格言警告说:"决不要携带两个时钟出海,带一个或三个
同样的原则也适用于形式化和记叙性定义。如果同时具有两种方式,则必须以一种作为标准,另一种作为辅助描述,并照此明确地进行划分。
几乎所有的形式化定义均会用来描述和表达硬件系统或软件系统的某个设计实现
周例会是每周半天的会议,由所有的结构师,加上硬件和软件实现人员代表和市场计划人员参与,由首席系统结构师主持。
例会的决策会给出迅捷的结论,允许工作继续进行。如果任何人对结果过于不高兴,可以立刻诉诸于项目经理,但是这种情况非常少见。
在大多数计算机项目中,机器和手册之间往往会在某一天出现不一致,人们通常会忽略手册。因为与机器相比,手册更容易改动,并且成本更低。然而,当存在多重实现时,情况就不是这样。这时,如实地遵从手册更新机器所造成的延迟和成本的消耗,比根据机器调整手册要低。
对于存有疑问的实现人员,应鼓励他们打电话询问相应的结构师,而不是一边自行猜测一边工作,这是一项很基本的措施。他们还需要认识到的是,上述问题的答案必须是可以告知每个人的权威性结论。
一种有用的机制是由结构师保存电话日志。日志中,他记录了每一个问题和相应的回答。每周,对若干结构师的日志进行合并,重新整理,并发布给用户和实现人员。这种机制很不正式,但非常快捷和易于理解
项目经理最好的朋友就是他每天要面对的敌人--独立的产品测试机构/小组
最后的分析中,用户是独立的监督人员。在残酷的现实使用环境中,每个细微缺陷都将无从遁形。产品-测试小组则是顾客的代理人,专门寻找缺陷
◆ 为什么巴比伦塔会失败?(Why Did theTower of Babel Fail?)
据《创世纪》记载,巴比伦塔是人类继诺亚方舟之后的第二大工程壮举,但巴比伦塔同时也是第一个彻底失败的工程。
这个项目到底有多好的先决条件?他们是否有:1. 清晰的目标?是的,尽管幼稚得近乎不可能。而且,项目早在遇到这个基本的限制之前,就已经失败了。2. 人力?非常充足。3. 材料?在美索不达米亚有着丰富的泥土和柏油沥青。4. 足够的时间?没有任何时间限制的迹象。5. 足够的技术?是的,金字塔、锥形的结构本身就是稳定的,可以很好分散压力负载。对砖石建筑技术,人们有过深刻的研究。同样,项目远在达到技术限制之间,就已经失败了。
如果项目有n个工作人员,则有(n2 - n)/ 2个相互交流的接口,有将近2n个必须合作的潜在团队。团队组织的目的是减少不必要交流和合作的数量,因此良好的团队组织是解决上述交流问题的关键措施。
减少交流的方法是人力划分(division of labor)和限定职责范围(specialization of function)。当使用人力划分和职责限定时,树状管理结构所映出对详细交流的需要会相应减少
让我们考虑一下树状编程队伍,以及要使它行之有效,每棵子树所必须具备的基本要素。它们是:1. 任务(a mission)2. 产品负责人(a producer)3. 技术主管和结构师(a technical director or architect)4. 进度(a schedule)
- 人力的划分(a division of labor)6. 各部分之间的接口定义(interface definitions among the parts)所有这些是非常明显和约定俗成的,除了产品负责人和技术主管之间有一些区别
◆ 胸有成竹(Calling the Shot)
构建独立小型程序的数据不适用于编程系统产品
Joel Aron,IBM在马里兰州盖兹堡的系统技术主管,在他所工作过的9个大型项目(简要地说,大型意味着程序员的数目超过25人,将近30,000行的指令)7的基础上,对程序员的生产率进行了研
他根据程序员(和系统部分)之间的交互划分这些系统,得到了如下的生产率:非常少的交互 10,000指令每人年少量的交互 5,000较多的交互 1,500
对常用编程语句而言。生产率似乎是固定的。这个固定的生产率包括了编程中需要注释,并可能存在错误的情况... 使用适当的高级语言,编程的生产率可以提高5倍。
◆ 削足适履(Ten Pounds in a Five-Pound Sack)
由于规模是软件系统产品用户成本中如此大的一个组成部分,开发人员必须设置规模的目标,控制规模,考虑减小规模的方法,就像硬件开发人员会设立元器件数量目标,控制元器件的数量,想出一些减少零件的方法。同任何开销一样,规模本身不是坏事,但不必要的规模是不可取的。
对项目经理而言,规模控制既是技术工作的一部分,也是管理工作的一部分。他必须研究用户和他们的应用,以设置将开发系统的规模。接着,把这些系统划分成若干部分,并设定每个部分的规模目标
培养开发人员从系统整体出发、面向用户的态度是软件编程管理人员最重要的职能。
第二个技能是考虑空间-时间的折衷。对于给定的功能,空间越多,速度越快。这一点在很大的范围内都适用。也正是这一点使空间预算成为可能。
◆ 提纲挈领(The Documentary Hypothesis)
报价、预测、价格:这三个因素互相牵制,决定了项目的成败
◆ 未雨绸缪(Plan to Throw One Away)
软件系统的构建人员也面临类似的问题,但似乎并没有吸取教训。一个接一个的软件项目都是一开始设计算法,然后将算法应用到待发布的软件中,接着根据时间进度把第一次开发的产品发布给顾客。
对于大多数项目,第一个开发的系统并不合用。它可能太慢、太大,而且难以使用,或者三者兼而有之。要解决所有的问题,除了重新开始以外,没有其他的办法--即开发一个更灵巧或者更好的系统
系统的丢弃和重新设计可以一步完成,也可以一块块地实现。所有大型系统的经验都显示,这是必须完成的步骤
一旦认识到试验性的系统必须被构建和丢弃,具有变更思想的重新设计不可避免,从而直面整个变化现象是非常有用的
最重要的措施是使用高级语言和自文档技术,以减少变更引起的错误。采用编译时的操作来整合标准声明,在很大程度上帮助了变化的调整。
管理人员需要参与技术课程,高级技术人才需要进行管理培训。项目目标、进展、管理问题必须在高级人员整体中得到共享。
对于一个广泛使用的程序,其维护总成本通常是开发成本的40%或更多。令人吃惊的是,该成本受用户数目的严重影响。用户越多,所发现的错误也越多。
程序维护中的一个基本问题是--缺陷修复总会以(20-50)%的机率引入新的bug。所以整个过程是前进两步,后退一步。
◆ 祸起萧墙(Hatching a Catastrophe)
已经"90%完成"了;调试在大多时候都是"99%完成"的;"计划完毕"是任何人只要愿意,就可以声明的事件
◆ 没有银弹-软件工程中的根本和次要问题
任务时间 = Σ(频率)i×(时间
购买和自行开发。构建软件最可能的彻底解决方案是不开发任何软件。
即使支付100,000美元,购买的软件也仅仅是一个人年的成本。而且软件是立即可用的!至少对于现有的产品、对于那些专注于该领域开发者的成果而言,它们是可以立刻投入使用的。并且,它们往往配备了书写良好的文档,在某种程度上比自行开发的软件维护得更加完备。
我相信,这个大众市场将是软件工程领域意义最深远的开发方向
需求精炼和快速原型。开发软件系统的过程中,最困难的部分是确切地决定搭建什么样的系统。概念性工作中,没有其他任何一个部分比确定详细的技术需求更加困难,详细的需求包括了所有的人机界面、与机器和其他软件系统的接口。需求工作对系统的影响比其他任何一个部分的失误都大,当然纠正需求的困难也比其他任何一个部分要大。
因此,现在的技术中最有希望的,并且解决了软件的根本而非次要问题的技术,是开发作为迭代需求过程的一部分--快速原型化系统的方法和工具。
软件系统的快速原型对重要的系统界面进行模拟,并演示待开发系统的主要功能。原型不必受到相同硬件速度、规模或者成本约束的限制。原型通常展示了应用程序的功能主线,但不处理任何如无效输入、退出清除等异常情况。原型的目的是明确实际的概念结构,从而客户可以测试一致性和可用性。
我们可以通过遵循优秀而不是拙劣的实践,来得到良好的设计。优秀的设计是可以传授的。程序员的周围往往是最出色的人员,因此他们可以学习到良好的实践