第一部分——华丽开幕
我将《人月神话》的前五章分为本书的第一部分,在这一部分,一个软件项目的所有组成部分均被呈现出来。诚然,一个糟糕的软件诞生可能有方方面面的原因,其中开发过程的错误常常负有主要责任,但很少有人将问题放在开发过程之前,一味地寻求在过程中优化,结果就是优化越来越难,而生产出来的软件产品越来越糟。在该部分,Brooks博士剑指开发团队与预期项目,从技术人员很少思考的管理层面入手,自下而上地解决软件开发过程中那些难以搞定的问题。
焦油坑:程序与产品
将程序(program)作为最终产品去评价团队编程效率是一个极具诱惑力的方案,这一评估方法也创造了不少让人神往的“车库神话”。Brooks在这里戳破了这一美好的幻想:可用的软件产品不是一个人在自己开发环境中的自娱自乐。
软件能被称之为产品的前提是它能被用户所使用,这要求程序可在任何用户手中使用,且能对一切数据做出适当的反馈。而很多时候,因为客户端硬件或者用户需求限制,程序需要在预先定义的预算内实现功能。同时为了考虑程序的健壮性,还需要有大量的测试用例去检测在不同的环境与使用方式下程序能否正确给出结果。
关键点
以上的现实情况,要求一个完备的编程系统产品程序必须拥有以下特性:
- 测试用例全面且充分
- 支持跨环境/平台/硬件
- 完备的用户文档
- 统一的编码/界面风格
- 将运行成本优化到预算之下
- 定义精确的接口
Brooks预测在完成以上所有特性后、程序成为真正有用的编程系统产品,将花费正常一个程序编写9倍左右的成本。大多时候,我们更倾向于以编写一个program作为成本预估的基准,从而过低地估计了真正product开发的难度,最终项目进行到一半时才发现开始所看到的巧克力喷泉原来是一个让人无法脱身的焦油坑,导致项目只能在腰斩和匆忙交付中做出抉择。
心得
对于初学软件工程的学生来讲,应该摒弃掉自娱自乐的Programming思维。在如今大语言模型的加持下,编码实现程序变得越来越简单,而“与用户交互”这种“人-机器-人”模式却始终是困扰软件行业已久的问题。在编程前期就为未来编程做好规范,选择适合所开发项目的开发模式,对程序的外部功能做出严格规约,充分考虑到程序以后可能面对的各种情况,是任何一个软件工程从业者都应该具备的基本素养。
人月神话:编程不是搬砖
“所有编程人员都是乐观主义者”,在Brooks博士看来,这并不是一个好的现象。这种盲目的乐观主义通常会导致复杂系统的实际开发与预想出现极大偏差。在这里,Brooks博士给出了五个软件项目进度安排出问题的五个原因:
- “一切都将运行良好”的不真实假设;
- 人和时间可以在计量工作量时线性转换的错误想法;
- 对自己的合理估算缺乏应有的固执;
- 缺少对项目进度的监控;
- 盲目增加人力导致恶性循环。
关键点
编程不是搬砖,缺乏物理介质的创造性活动往往会让乐观主义麻痹程序员对可能面对的问题的警觉性,而且简单地认为人员数量可以与单位时间工作量做直接换算。Brooks博士通过一个建立数学模型,用数据证明了“Man-Month”神话的欺骗性,指出“缺乏合理的进度安排”是造成项目滞后的最主要原因,并提出了著名的Brooks定律:
向进度落后的软件项目增加人手,会使进度更加落后。
Adding manpower to a late software project makes it later.
心得
本章与后面的《没有银弹》一文,可以说是Brooks本书中最重要的两个章节。Brooks博士用“人月神话”诱惑我这种急功近利的人进来阅读,却在第二章直接撕破脸,直接论证了“Man-Month”不存在,属实是有点不讲武德()。
虽然本书主要面对项目管理者,但是对个人项目开发也有很大的启发。我以我浅薄的见解,将我学到的东西总结为以下几点:
- 保持悲观:乐观当然会让开发充满激情,但是复杂项目难免会有问题。与其让未曾预想的error影响自己的信心和项目开发进度,不如在构建前就为“全部都会出错”留出充足的调试空间。
- 剪切的艺术:Brooks博士在论述人月神话的虚假性时,将程序设定为是“关系错综复杂、难以分解的任务”,而事实上如今软件开发已经更加模块化、构件化。《编码完备》第二部分中,作者用启发式方法将软件系统逐步分解为相互独立、可复用的子程序,而在我个人编程时,更倾向于将这些子程序一个个地进行集中时间编写,这样我便可以无需考虑我过去的实现方式,而是直接去调用这些
我都不知道怎么跑起来的方法去进行更高层次的操作。 - 把话说明白:然而未来我们大概率不会独自进行编程,在参与项目开发时难免要付出沟通成本,而Brooks博士认为沟通与培训是人月神话不成立的一个重要前提条件。换而言之,沟通付出的成本往往是限制效率增长的重要因素。幸运的是,Brooks博士对于这一问题,在6、7两章中给出了解决方案,这个暂且按下不表。
- 测试优先:这里我直接引用了Brooks博士的经验法则:
1/3用于计划
1/6用于编码
1/4用于构件测试和早期系统测试
1/4用于系统测试
不要质疑!如果没有为测试留下充足的时间,很大概率会导致项目被迫进行不断调试而为先前对测试的忽视付出代价,甚至会导致项目开发难以为继。本人以前有一个个人C++项目便因为急于实现某功能而没有添加调试用语句,导致整个项目出现问题而无法追踪,最终只能被迫回滚至初始版本,浪费了大量的时间,也严重挫伤了开发信心。
外科手术团队:专业团队
下面两章可以说是本书里面离技术最远的两章。这两章Brooks博士更多考虑了“人”的问题,像我这种未曾参加工作实践的纯真学生实在无法厚着脸皮给出自己的见解。所以这里只对文章内容进行简单归纳。
关键点
现实表明,少数精英团队的编程效率极高,但数千人年的项目却不得不需要大量人手。为了解决两者之间的矛盾,Brooks博士引用了Harlan Mills的建议:
- 首席程序员:负责设计整个系统,起指挥与监督作用。
- 副手:给首席程序员提供意见,充当团队与首席之间沟通的桥梁,必要时还能起到保险作用。
- 管理员:负责解决首席无法时刻注意的行政、经济、法律等层面的问题。
- 编辑:将首席的设计落实为可维护文档。
- 文秘:协助管理员与编辑的工作。
- 程序文员:代码与技术日志管理,是程序员的一个细分。
- 工具维护人员:负责管理服务的构建、维护与升级。
- 测试人员:设计测试用例,帮助首席规范设计。
- 语言律师:提供语言使用方面的建议,优化代码。
- 程序员:干活的苦b。
具体人员分配可根据实际团队人数进行适当规划,但无论如何分配,首席人数一定占一小部分,只需协调这些首席的分工,便能轻松控制整个团队的开发方向。
贵族制、民主制和系统设计:集权AB面
无论是在哪一个领域,少数头脑决定方向总是让人很不舒服的一件事。无论打工人是否拥有才能,他们总会觉得自己的抱负无法施展而郁郁不得志。这一章Brooks博士通过“建筑设计”和“贵族制与民主制”两个隐喻,详细地描绘了第三章所提的开发团队结构所产生的“少数人集权”的AB面。
关键点
A面:
- 为保证系统概念上的完整性,被迫要牺牲一些与当前系统不相协调的好改进或好想法;
- 架构与实现分离不彻底可能会导致更糟糕的情况;
- 多数实现人员工作成果的寿命要比少数架构师成果的寿命短。
- 可能会导致实现人员劳动力的空闲。
B面:
- 由于设计来自于一人或少部分设计思想一致的人,系统所使用语言、工具、环境、风格等能够保持高度一致。
- 为实现作出限制后,实现人员的注意力将工多集中在“如何实现”上,反而会激发实现人员的创造力(可类比“土炮”,条件艰苦反而催生了很多武器的发明)。
- 分工更精准,每个人各司其职,大大降低了前面令人困扰的劳动成本,从而提高了开发效率。
扬长避短:
- 易用性(Simplicity),即功能与概念的复杂程度的比值才是系统设计好坏的最终测试标准。一个具有高易用性的系统应该简洁且直白,同时要满足设计上的一致性和概念上的完整性。
- “架构陈述的是发生了什么,而实现描述的是如何让它发生”。架构与实现必须分清,即架构之定义功能,而实现则是算法层面上的。如果从软件构造角度来讲,则可简单理解为架构设计接口功能,实现设计实现类,两者决不能相互干扰,接口必须保证只完成一些泛用的基本操作,不能限制实现类里的实现方式。
- Blaauw将项目开发活动分为架构、实现、实施三个部分。实现人员可以在简单规约已经构成的时候去开发一些基本的模块或者工具,从而并发的进行工作,不会存在劳动力闲置的问题。
第二系统效应:架构师自我修养
现在我们的架构师已经对整个团队与项目都做出了相关规范,然而架构师自身却缺乏限制,这种源头上的不受约束很有可能导致灾难性的、无法更正的后果。除了上下层开发人员的交流以外,我们明显需要一个更有强制约束力的方案。
关键点
限制架构师的第一大难关就是承包商报价、实际预算和建造人员的要求。架构师必须时刻考虑这三方来调整自己的计划,让三者保持一个动态平衡,这个保证了架构的质量与可行性。
然而仅进行过一次系统架构的架构师在开发第二个系统时,经常会陷入“第二系统效应”的陷阱中:过度设计系统功能、过度修饰过时功能。前者大大破坏系统的易用性,后者则削减了系统投入使用后的收益。
心得
人不能吃完第一个馒头后直接吃第三个馒头,但我们可以在吃第二个馒头时喝口水。Brooks为新手架构师提供了一套减小第二系统效应负面影响的方法:
- 在架构时特别注意可能发生危险的地方,防止过度修饰。
- 为每个功能添加空间时间限制,防止臃肿设计。
- 将设计详细体现在文档中,以供项目管理进行监督。
从我个人角度来看,、在个人自控力不强的情况下,还有几个更容易实现的方法:
- 在正式参加项目开发前,可以尝试自行架构两个个人小项目练手,此时第二个项目可以不受任何约束、依照自己的想法进行开发。无论第二个项目设计成功还是失败,一定会因为项目的过度设计出现一些难于克服的困难。总结这些在项目开发中遇到的问题并与第一次开发对比,可以快速验证出哪种颗粒度的功能需求并不切合实际,从而第二系统效应不会影响自己未来真正会遇到的正规项目开发。
- 在开发最开始时只开发基本功能,但要保证这些功能接口的可拓展性。接下来采用增量方法,在每个基本功能上加以少量修饰。这样虽然会增大交流成本,使得文档维护工作量提高,但是能保证系统的正常运转,且能较早的发现过时的功能接口。这种千层饼式的开发方法能很大程度上减少底层功能废弃所产生劳动力浪费,而节省的成本远远高于文档工作所付出的努力。
万事俱备
Brooks博士前五章已经将一个完整的、有编制的团队组合完成,并将项目的方方面面安排得井井有条。然而只有一个孤零零的团队并不能做出项目。而在接下来几章,Brooks博士将联通这个团队,卡紧团队机器中的每一个齿轮,为团队的创造性活动画出空间与时间的场地,准备进行一场复杂且充满挑战的开发活动。