人月神话,1975年首次发行,软件工程领域的经典著作,其核心观点历经半个世纪依然具有现实意义。尽管我的项目经验尚浅,但它已经为我在系统开发过程中提供了指导性思想。相信在过一两年后再读本书时更能感同身受。
人月神话(纪念典藏版)
原作名:The Mythical Man-Month: Essays on Software Engineering
出版社:清华大学出版社,第一版
作者:【美】小弗雷德里克·P.布鲁克斯(Frederick P. Brooks,Jr.)
译者:UMLChina
焦油坑
- 编程为什么有趣?
- 这种乐趣来源于造东西的成就感。
- 这种乐趣来源于制造对他人有用的东西。
- 乐趣来源整个过程体现出的一股强大的魅力——将相互啮合的活动部件组装在一起,看到他们以精妙的方式运行着,并收到了预期的效果。
- 这种乐趣来源于持续的学习,来源于这项工作的非重复特性。
- 这种乐趣还来源于在易于驾驭的介质上工作。
- 编程固有的苦恼:
- 苦恼来自对完美的追求。
- 苦恼来自由他人来设定目标、供给资源和提供信息。
- 设计宏大概念是有趣的,但寻找琐碎的bug却是一项重复性的活动。
- 投入了大量的辛苦的劳动,产品在即将完成或者终于完成的时候,却已显得陈旧过时。
这,就是编程,一个让许多人痛苦挣扎的焦油坑,以及一项乐趣和苦恼共存的创造性活动。
- 在众多软件项目中,软件项目的进度安排不合理普遍发生的原因是什么呢?
- 我们的估算技术还很不成熟,说得更严重一些,它们反映了一个悄无声息但很不真实得假设——一切都将运作良好。
- 采用的估算技术隐含地假设人和月可以互换,错误地将进度与工作量相互混淆。
- 由于对自己的估算缺乏信心,软件经理通常缺少安托万大厨那样的有礼貌的固执。
- 对进度缺少监控。
- 当意识到进度有偏移时,下意识(以及传统)的反应是增加人力。
人月神话
- 缺乏合理的进度安排是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还要大。
- 所有的编程人员都是乐观主义者:“一切都将运作良好”。
- 由于编程人员通过纯粹的思维活动来开发,我们期待在实现过程中不会遇到困难。
- 但是,我们的构思本身是有缺陷的,因此总会有bug。
- 围绕着成本核算的估计技术,混淆了工作量和项目进展。
- 人月是危险和带有欺骗性的神话,因为它暗示人员数量和时间是可以相互替换的。
- 在若干人员中分解任务会引发额外的沟通工作量——培训和相互沟通。
- 关于进度安排,我的经验是1/3计划、1/6编码、1/4构件测试以及1/4系统测试。
- 作为一门学科,我们缺乏数据估计。
- 我们对自己的估计技术不确定,因此在管理和客户的压力下,我们常常缺乏坚持的勇气。
- Brooks法则:向进度落后的软件项目增加人手,会使进度更加落后。(Adding manpower to a late software project makes it later.)
外科手术团队
贵族制、民主制和系统设计
- 概念完整性是系统设计中最重要的考虑因素。
- 功能与理解上的复杂程度的比值才是系统设计的最终测试标准,而不仅仅是丰富的功能。
第二系统效应
- 尽早交流和持续沟通能使架构师有较好的成本意识,使开发人员获得对设计的信心,并且不会混淆各自的责任分工。
- 想到成功,架构师必须做到以下几点:
- 牢记是建造人员对实现有创造性和发明性的责任,所以架构师只是建议,而不是下指令;
- 时刻准备好提出实现所指定事物的方法,同样准备接受其他任何能达到目标的方法;
- 对上述建议保持低调和不公开;
- 准备放弃所坚持的改进建议;
- 听取开发人员在体系结构上的改进建议。
- 在开发第一个作品时,架构师倾向于精炼和简洁。他知道自己对正在进行的任务不够了解,所以会谨慎、仔细地工作。
- 在开发第一个作品时,他会对面不断产生的装饰和润色功能。这些功能都被留存在一边,以备“下次”使用。
- 第二个系统是一个人所设计过的最危险的系统。而当他着手三个及以后的系统时,先前的经验会相互验证,得到对此类系统通用特性的判断,而且系统之间的差异会帮助他识别出经验中不够通用的部分。
- 一种普遍倾向是过度设计第二个系统,向系统添加很多修饰功能和想法,它们曾在第一个系统中被小心谨慎地放在一旁。
- 第二个系统效应的另一个表现于纯粹的功能修饰有所不同,也就是说存在对某些技术进行细化、精炼的趋势。由于基本系统设想发生了变化,这些技术已经显得落后。
- 架构师如何避免开发第二系统效应?显然,他无法跳过第二个系统,但他可以有意识地关注这个系统的特殊危险,并加以额外的自我约束,来避免那些对功能的过多修饰,并避免延伸出会因假设和目的的变化而废除的功能。
- 项目经理如何避免第二系统效应?他要坚持要求,资深架构师至少有两个系统的经验。同时,保持对特殊诱惑的警觉,他可以提出正确的问题,确保原则上的概念和目标在详细设计中得到完整的体现。
转递消息
一句古老的格言警告说:“不要携带两个时钟出海,而是带一个或三个。”同样的原则也适用于形式化和记叙性定义。如果同时具有两种方式,则必须以一种作为标准,另一种作为辅助描述,并照此明确地进行划分。这两者的任何一个都可以作为主要标准。
为什么巴别塔会失败
- 巴别塔项目的失败是因为缺乏交流以及交流的结果——组织。
- 组织,是成功的关键。交流和组织的技能需要管理者付出大量思考,并具备与软件技术本身同等丰富的经验能力。
- 因为左手不知道右手在做什么,从而进度灾难、功能的不合理和系统缺陷纷纷出现。“由于存在对其他人的各种假设,团队成员之间的理解开始出现偏差。
- 团队应该以尽可能多的方式进行相互之间的交流:非正式地进行简要技术陈述的常规项目会议,共享的正式项目工作手册。
- 项目工作手册不是独立的一篇文档,它是对项目必须产生的一系列文档进行组织的一种结构。
- 每一位团队人员应该了解所有的材料(工作手册)。
- 团队组织的目标是为了减少必要的交流和协作量。
- 为了减少交流,组织结构包括了人力划分(division of labor)和限定职责范围(specialization of function)。
- 组织中的交流是网状,而不是树状结构,因此所有的特殊组织机制都是为了进行调整,以克服树状组织结构中交流缺乏的困难。
- 每个子项目具有两个领导角色——制作人,技术总监或架构师。这两个角色的职责有很大的区别,需要不同的技能。
胸有成竹
削足适履
- 除了运行时间以外,程序所占据的内存空间也是主要开销。特别师对于操作系统,它的很多程序是永久驻留在内存中的。
- 即便如此,花费在驻留程序所占据内存上的金钱仍是物有所值的,比其他任何在配置上投资的效果都要好。规模本身不是坏事,但不必要的规模是不可取的。
- 软件开发人员必须设立规模目标,控制规模,发明一些减少规模的方法——就如同硬件开发人员为减少元器件所做的事情一样。
- 规模预算不仅仅在占据内存方面是明确的,同时还应该指明程序对磁盘的访问次数。
- 规模预算必须与分配的功能相关联;在知名模块大小的同时,确切定义模块的功能。
- 在大型团队中,各个小组倾向于不断地局部优化,以满足自己的目标,而较少考虑对用户的整体影响。这种方向性的问题是大型项目的主要危险。
- 从系统整体出发以及面向用户的态度是软件编程管理人员最重要的职能。
- 在早期应该制定策略,以决定用户可选项目的粗细程度,因为将它们作为整体打包能够节省内存空间。
- 为了取得良好的空间—时间折中,开发队伍需要得到特定的某种语言或者机型的编程技能培训,特别是在使用新语言或者新机器时。
- 精炼、充分和快速的程序往往是战略性突破的结果,而不仅仅是技巧上的提高。
- 更普遍的是,战略上的突破常来自于对数据或表的重新表达。数据的表现形式是编程的根本。
提纲契领
未雨绸缪
- 对于大多数项目,第一次开发的系统并不合用。它可能太慢、太大,而且难以使用,或者三者兼而有之。
- 要解决所有的问题,除了重新开发一个更灵巧或者更好的系统以外,没有其他的办法。系统的丢弃和重新设计可以一步完成,也可以一块块地实现。所有大型系统的经验都显示,这是必须完成的步骤。而且,新的系统概念或新技术会不断出现,必须构建一个用来抛弃的系统,因为即使是最优秀的项目经理,也不能无所不知地在项目最初解决这些问题。
- 管理上的问题不再是“是否构建一个试验性的系统,然后抛弃它”,而是必须这样做。现在的问题是“是否预先计划抛弃原型的开发,或者是否将该原型发布给用户”,从这个角度看待问题,答案更加清晰。将原型发布给用户,可以获得时间,但是它的代价高昂——对于用户,使用起来极度痛苦;对于重新开发的人员,分散了精力;对于产品,影响了声誉,即使最好的再设计也难以挽回名声。
- 因此,为舍弃而计划,无论如何,你一定要这样做。
- 一旦认识到试验性的系统必须被构建和丢弃,具有变更思想的重新设计将不可避免,那么,面对整个变化现象就是非常有用的。第一步是接受这样的事实:变化是与生俱来的,不是不合时宜和令人生厌的异常情况。
- 程序员不愿意为设计书写文档,不仅仅是因为惰性,更多的是源于设计人员的踌躇——要为自己尝试性的设计决策进行辩解。
- 只要管理人员和技术人才的天赋允许,老板必须对他们的能力培养给予极大的关注,使管理人员和技术人员具有互换性;特别是希望在技术和管理角色之间自由地分配人手的时候。
- 具有两条晋升线的高效组织机构存在一些社会性的障碍,人们必须警惕并积极地同它做持续的斗争。
- 很容易为不同的晋升线建立相互一致的薪水级别,但同等威信的建立需要一些强烈的心理措施:相同的办公室、一样的支持以及技术调动的优先补偿。
- 组件外科手术队伍式的软件开发团队是对上述问题所有方面的彻底冲击。对于灵活组织架构问题,这的确是一个长期行之有效的解决方案。
干将莫邪
整体部分
祸起萧墙
- 一天一天的进度落后比起重大灾难更难以识别,更不容易防范和更加难以弥补。
- 根据一个严格的进度表来控制大型项目的第一个步骤是制定进度表,进度表由里程碑和日期组成。
- 里程碑必须是具体的、特定的和可度量的事件,能进行清晰的定义。
- 如果里程牌定义得非常明确,以至于无法自欺欺人时,程序员很少会就里程牌的进展弄虚作假。
- 对于大型项目,一个对里程牌报告进行维护的计划和控制小组是非常可贵的。
另外一面
- 培训和管理人员基本上没有向编程人员成功地灌输对待文档的积极态度——文档能在整个生命周期对克服懒惰和进度的压力起促进和激励作用。
- 大多数文档只提供了很少的总结性内容。必须放慢脚步,稳妥地进行。
- 为了使文档易于维护,将它们合并至源程序是至关重要的,而不是作为独立文档进行保存。
- 最小化文档担负的三个关键思路:
- 借助那些必须存在的语句,如名称和声明等,来附加尽可能多的“文档”信息;
- 使用空格和格式来表现丛书从属和嵌套关系,提高程序的可读性;
- 以段落注释,特别是模块标题的形式,向程序中插入必要的记叙性文字。
- 程序修改人员所使用的文档中,除了描述事情如何,还应阐述它为什么那样。对于加深理解,目的是非常关键的,即使是高级语言的语法,也不能表达目的。
没有银弹
- “在未来的十年内,无论是在技术上还是在管理方法上,都看不出有任何突破性的进步,能够保证在十年内大幅度地提高软件的生产率、可靠性和简洁性。”
- 软件开发中困难的部分是规格说明、设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程序进行验证。