2003-《人月神话》

人月神话

举个例子:在软件项目管理中有一个词叫“人月”,“人”就是一个人、两个人的人,“月”就是一个月、两个月的月。所谓“人月”,就是一个人在一个月内所能完成的工作量。假设某个项目预估需要 12 个人月,那么你就算了,如果指派 4 个人来做这个项目,理论上需要 3 个月;而如果指派 6 个人来做这个项目,理论上则只需要 2 个月便可以完成。

你看,这就是用一个简单的数量模型,来控制一个复杂工程。很多公司在做软件工程项目的时候,就是这么计算人力和时间投入的。但是,干过软件工程的人都知道,这么算,是很荒谬的。

程序员的世界里,有一本很经典的书,就叫《人月神话》,就是在驳斥这种算法。 这本书的作者是图灵奖的得主布鲁克斯。他的意思是,软件开发,工程庞大,又非常复杂。我们人类对于这种项目,通常是分工合作,因为它促进效率,节省时间的。但是,随着一个项目参与的人越来越多,分工越来越细,人和人之间需要的沟通量,也指数增长。很快你会发现,沟通花费的时间,渐渐地就比分工省下来的时间还要多。说白了,过了一个临界点,人越多不是越帮忙,而是人越多越添乱。一个人 12 个月能完成的事,不见得上 12 个人 1 个月就能完成,甚至 12 个月也未必能完成。 所以,《人月神话》这本书里建议了一种组织方式,叫“外科手术式的队伍”。就像一台外科手术一样,有一个主刀大夫,软件项目也应该有一个首席程序员,其他人都是给他提供支持的。这样,就既能获得由少数头脑产生的产品完整性,又能得到多位协助人员的总体生产率,还彻底地减少了沟通的工作量。

你看,关键人,关键支点,在复杂系统中的位置越来越重要了。无论是主刀大夫还是首席程序员。

类似的误区,我们还可以看到很多。比如,有些公司,他们真的认为一头大象可以坐在它任何它想坐的地方,他们真的相信通过砸钱砸人就可以进入任何一个想染指的领域,但很多情况都会失败。这是一种“人月神话”。老喻就举了一个例子:2010 年,Facebook 做了一个职业社交网站,这是冲着这个领域的老大领英来的。Facebook 想的很简单啊。我有的是流量,也不缺钱,光这个网站就拿到了近 5000 万美金的投资,怎么算账都能赢。果然,通过导流,该个职业社交网站的用户迅速增长到了 2500 万。但是然后呢?过不了多久,Facebook 就只好认输,以 200 万美金加上一点股份,就把这个网站处理掉了。你看,Facebook,是社交网站,世界第一大,它做职业社交,简直就是一墙之隔,居然就做不成。为啥?其实就是我们今天说的这个效应,他们相信人月神话,相信总体上的数量可以取代关键点上的质量。

人月神话的本质是啥?是我们在简单社会养成的一种思维模式。我们以为,数量优势,即使不是绝对优势,也至少是个全面优势。但是,进入现代复杂社会之后,这个逻辑不再成立。问题不在于你是不是有优势,而在于你是不是知道:哪里才是关键局部?什么才是关键优势?

一. 焦油坑——重新认识职业

编程提供的快乐:

  • 创建事物的快乐
  • 产品对他人有用的快乐
  • 把零部件组装成航空母舰的魅力
  • 持续学习的快乐
  • 通过思维作为介质来实现功能,这种介质如此灵活轻便,使得工作更快乐 职业痛苦的原因:
  • 追求完美。一旦出现一丁点错误,系统就不会按预期来运行
  • 由他人来设定目标,并且需要依赖自己无法控制的事物(比如外部组件/接口…)
  • 创造性活动都伴随着枯燥无味的重复劳动。概念设计是有趣的,但是找 bug 却是无聊的。
  • 人们期待项目进度是线性收敛的,但是越到项目后期,收敛越慢
  • 产品在完成前总面临着陈旧过时的威胁 这就是编程,一个让许多人痛苦挣扎的焦油坑,以及一个乐趣和苦恼共存的创造性活动【💥 boom】

二. 人月神话——不合理的进度安排

缺乏合理的进度安排是造成项目滞后的最主要原因,那为什么会存在不合理安排?

  • 过于乐观
    • 年轻的程序员们总认为“一切都将运作良好”,每项任务都只花费自己的时间
    • 但是考虑到外部依赖的问题、任务的前后次序、组合的成本,这很难满足“一切都将运作良好”的条件
  • 人月的关系是复杂的
    • 开发成本随着人数和时间的不同,有很大的变化。
    • 用人月来衡量工作是非常危险的,因为它建立在人与人之间不需要相互交流的前提
    • 在错综复杂的任务中,沟通和交流的工作量非常大,如果添加更多的人手,实际会延长进度
  • 忽略测试的重要性
    • 由于乐观主义,软件出现 bug 的数量比我们预料的多得多。
    • 不为系统测试安排足够的时间,简直就是一场灾难……
    • 推荐的任务安排比例:1/3 计划 、1/6 编码 、1/4 持续单元/集成测试 、1/4 最后完整系统测试
      • 【虽然比较理想,但是任务安排的比例的确应该多向设计和测试偏移。反观我们现在,编码几乎占满整个生命周期,忽略了计划和测试的重要性,这样往往会导致开发进度的失控:前期不仔细思考方案和确定需求,后期频繁变化导致时间不够,为了赶进度又压缩测试空间,最后质量不高又要加班修改。】
  • 空泛的估算
    • 迫于任务的紧急度,直接按客户期望来排期,忽略了排期是否合理
      • 【所有客户/PM 都觉得自己现在的需求是最重要最紧急的,但是我们(程序员)要确保的是在合理的时间内做出*高质量的东西,我们要有自己的坚持和判断。】
    • 排期需要有数据来支持
    • 在可靠估算出现前,项目经理需要挺直腰杆,坚持自己的估计
      • 【好的 PM/PMO 会大幅提高 RD 的寿命,减少猝死率】
  • 进度落后的错误处理
    • 不要进度一落后,就想着加人 🔪
    • 项目经理应该仔细调整项目,重新安排任务,或者剪除造成落后的任务
    • 因为新人加入会有前期成本,并且人与人的沟通成本也会递增。所以添加更多的人,往往不会解决进度落后的问题。
      • 【部门早期也有这个问题,以为堆人就可以解决问题,最后大家还是疯狂加班的日子….男默女泪】
    • Brooks 法则: Adding manpower to a late software project makes it later

三. 外科手术队伍——如何构建项目团队

面对大型项目开发,如果组建少数精干团队,沟通成本低,但是开发周期长;如果组建大型团队,组织沟通难。书中推崇一种外科手术队伍的团队结构,各司其职来提高团队工作效率:

  • 技术
    • 架构师。定义功能,设计程序,书写文档,需要天赋、经验和系统知识。
    • 评估员。来评估设计,相比于架构师来说经验会少一点。
    • 专家。对某些领域有很深的研究,协助解决棘手问题
    • 文档维护者。统一维护项目的文档和工具
    • 程序员。负责功能的具体实现
    • 测试。负责测试用例、搭建测试平台、日常测试
  • 管理
    • Boss。在管理上有决定权
    • 文秘。负责协助 Boss 的事务落实

同时作者推崇由少数人思考确定系统的方向,多数人来解决实际实现问题。并且需要对开发人员进行专业化分工,来减少成员的交流成本,提高工作的效率。 【我想起了某为的工作分工,架构师写文档和伪代码,程序员只要实现特定模块,变成真の一块砖。作者推崇的这种方式对于公司来说是好事,但是对个人来说不见的很好,需要大家辩证看待】

当我们需要扩建团队规模时,需要调整人数比例,减少决定设计的人员,增多开发实现的人员,保持整个系统设计概念的完整性。并且清晰划分体系结构设计和实现的界限,是的工作易于管理,提高效率。

四. 贵族专制——保证系统概念的完整性

  • 系统设计的完整性,要求设计工作必须是少数人专制的
  • 具体实现的工作同样具有创造性,并且会极大的影响代码的性能 【赞同,设计的确不需要过多的民主,有经验的少数人来确定方案容易快速对主干达成共识。另外做具体实现的同学是可以去学习顶层设计思考,饭一口一口吃,等攒好经验了再参与设计】

五. 画蛇添足——约束设计

架构师交互准则

  • 只建议开发人员方案,不能支配。【需要给实现人员的灵活创造空间】
  • 时刻准备制定一种实现方案。【必须有自己的兜底实现方案】
  • 上述建议保持低调
  • 随时准备放弃坚持的改进建议。【开放谦逊】

保持自律

在开发第一个系统时,架构师倾向于精炼和简洁。但是第二个系统设计的时候,往往会过度设计和修饰,需要在功能设计上保持自律:

  • 方案需要多人评审
  • 保持完整的概念和清晰的目标
  • 不过度修饰

六. 贯彻执行

如何确保团队中每个人都能理解和实现架构师的决策呢?

  • 清晰、准确的文档,同时保持文档的时效性
  • 卓有成效的会议
    • 每周定期交流
    • 每个人都要承担义务
    • 内外部同时寻求问题的解决方案
    • 正式书面文件制定决策
    • 架构师有决策权力
  • 鼓励 oncall,同时保存日志
  • 加强测试,保证设计决策被落实到系统

七. 为什么巴比伦塔会失败——交流和组织

巴比伦塔是人类第二大工程壮举,同时也是记载的第一个彻底失败的工程。它拥有清晰的目标、充足的人力、丰富的材料、足够的时间和技术,为什么会失败呢?原因在于缺乏交流和组织。

团队中如何进行交流沟通:

  • 非正式途径。电话、面基,快速解决问题
  • 会议。定期常规性会议,按时同步进度和问题
  • 工作手册。用于共享的大型项目

组织架构的调整

  • 团队组织的目标是为了减少必要的交流和协作量。从组织结构上可以从人力划分、限定职责范围来实现。
  • 组织交流是网形,而不是树形
  • 领导角色可以是产品负责人和技术负责人的灵活组合,具体看是资源进度为主还是技术设计为主。

八. 胸有成竹——工作效率

  • 由于外部因素影响,实际工作时间会比预期多..
  • 使用适当的高级语言,会提高 5 倍编程生产率

九. 削足适履——项目控制

程序的功能、内存等不能够无限大,必须要在前期进行规模控制

  • 软件开发人员必须要设置规模目标,控制规模
  • 在各个模块进行局部优化的时候,架构师需要关注系统完整性
  • 从系统整体出发以及面向用户的态度是软件编程管理人员最重要的职能 【+1】
  • 培养员工新技能来提高系统性能 【+2 在业务迭代的同时,也需要专门安排同学有目的地去学习和调研新技术】
  • 数据的表现形式是编程的根本

十. 提纲挈领——文档

  • 文档关注的焦点:时间、地点、人员、项目内容、成本
  • 为什么要有文档:
    • 书面记录,使得分歧明朗、矛盾突出
    • 作为和他人沟通的渠道
    • 周期回顾
  • 文档的重要性:文档封装了项目经理的制定和实现计划的大量工作,可以让各项计划和决策在整个项目范围内得到交流

十一. 未雨绸缪——拥抱变化

用户的实际需要和用户感觉会随着程序的构建、测试和使用而变化。当目标无可避免的要发生变化时,设计和技术的变化也不可避免。我们需要随着学习的过程更改设计

系统设计的变更

  • 文档和软件需要有日程表和版本控制

组织架构的变更

  • 老板需要关注管理人员和技术人员的能力培养,给予他们互换的可能
  • 高层人员必须做好技术和情感上的准备,亲自参与开发工作

BugFix 的不可控

  • 软件维护成本通常是开发成本的 40%+。
  • 并且因为只考虑局部影响,没考虑全局影响,BugFix 有 20%-50%的几率引入新的 bug。 【没经验的同学会常犯这个错,如何培养这种全局意识是每个 mentor 要承担的事情】
  • 需要前期有能够消除或者指明副作用的设计方法

迭代的不可控

  • 系统开发是为了减少混乱度,所以它本身是亚稳态。软件维护和迭代是提高混乱度 【哈哈哈哈,系统越写越烂太真实了,设计评审和收敛保证系统概念完整延续,代码规范检测、CodeReview、维护通用工具保证系统实现的统一风格,但是每个程序员都有自己的风格,整个系统还是会趋向于扩张和混乱…】
  • 在迭代是,需要明确目标和设计,减缓系统退化到非稳态的进程

十二. 干将莫邪——通用工具

  • 开发和维护公共的通用编程工具,使得项目工作效率更高
  • 通用工具需要专人开发和维护
  • 项目经理需要重视通用工具的重要性

十三. 整体部分——整体系统思考

如何开发系统、测试系统、集成依赖,让我们系统性考虑下:

剔除 bug 的设计

  • 完整和明确的测试用例
    • 【一个好的测试用例往往包含了所有系统功能和业务场景描述,能够很好的减少系统必须查找的 bug 数量。但是这样要求 RD 必须深刻了解业务场景和功能实现,这样才能枚举出系统中所有的操作和期待的结果】
  • 自上而下的设计
    • 从体系结构设计、设计实现、物理编码实现三个步骤中,精细化每个步骤,拆出和细化独立的模块
    • 为什么自上而下的设计可以避免 bug?
      • 结构清晰,更容易对功能进行描述
      • 模块的分割和独立避免系统级别 bug
      • 细节的抑制是的结构缺陷更容易识别
      • 【依赖反转】

构造测试平台

  • 类型:单测、集成、e2e
  • 手段:搭建测试平台,控制变更内容

十四. 祸起萧墙——如何避免项目延期

【这章写的很实用,建议读读原文】 项目进度的严重失控,通常来自白蚁的肆掠,而不是龙卷风的侵袭。实际上,重大灾害是比较容易处理的,因为它往往和压力、重组、新技术的出现有关,整个项目组是可以应付自如的。但是一天天的进度落后是难以识别和弥补的。我们需要如何避免每日的项目进度落后:

  • 定义具体的、清晰的、可度量的里程碑
    • 【我们在项目开发时很容易定一个模糊的里程碑,而且在 deadLine 的时候也装鸵鸟,不深究是否达到了标准,这样往往会留下一些小尾巴到下个周期,造成项目进度慢慢的落后。做到里程碑清晰可度量,并且在 check 的时候能够真实的衡量结果,也是字节提倡的坦诚清晰】
  • 保持进取心
    • 保持团队积极进取的心态,关心每一天的落后,保证关键任务不会偏离进度。
  • 及时反馈问题
    • 一线经理想要自行解决问题,但是老板想要时刻了解情况,双方想法的不同会造成信息隐瞒
    • 如何在团队建立良好的问题反馈机制:
      • 减少角色冲突。老板只了解信息,不越俎代庖,避免经理失去权威;经理及时上报,并且提出解决方案,让老板安心。
      • 有了解真相的评审机制。通过关键路径图、明确里程碑、周会多方面来评审是否存在问题。 【说的太棒了,因为角色需求的冲突,导致上下行为的不同,但是为了能够达到及时反馈问题的目的,需要双方都优化自己的行为。 管理者需要学会克制,关注管理而不是具体的实现。尤其是刚接触管理,总觉得下属 1 周的工作自己只要干 1 天,就自己来写代码。这样不放权不但会导致下属能力没法提升、降低团队的士气,而且也会使得自己没法专注管理工作。 执行者需要改变观念,及时反馈问题是为了让管理者更好的协调工作内容和时间,同时也优化再次安排工作的机制,而不要看成对自己能力的否定。并且汇报中附带解决方案,可以给老板信心。】

十五. 另外一面——如何写出良好的文档

我们都知道文档的重要性了,那如何写出良好的文档呢?

  • 用户文档
    • 目的
    • 环境和范围
    • 功能
    • 指令和选项
    • 性能
    • 测试用例
    • 修改记录
  • 流程图
  • 自文档化。即把文档整合到源码中,更方便和及时
    • 提高源码可读性
    • 注释
    • 规范项目结构和命名 【自文档化赛高,程序员更倾向于看代码,这个时候注释和命名就很重要了….请不要对自己的代码可读性过度自信,为了避免后续维护人员掉太多头发,加上一些注释吧】

十六. 没有银弹——根本和次要任务

在所有软件开发中,根本任务是打造构成抽象软件实体的复杂概念结构,次要任务是用编程语言表达这些抽象实体,将他们映射成机器语言。 在进行根本任务时,需要注意:

  • 仔细的市场调研,避免造轮子
  • 将快速原型开发作为迭代计划的一部分,来验证和获取需求
  • 有机的更新软件,逐步迭代
  • 不断挑选、培养概念设计人员 【不得不说,能创造新领域的人才是每个团队的宝】

有没有银弹?

有没有银弹来解决根本问题,答案是没有的。 软件项目管理需要具体问题具体分析,没有一个方法可以让所有项目都获得数量级的效率提升。 但是规范化方法和制度能够大幅度提高生产率。 同时解决方案不是一蹴而就的,是逐步迭代获取的。【日拱一卒,功不唐捐】

根本困难的原因

  • 复杂度
    • 软件实体的扩展会带来非线性的复杂度增长
    • 复杂度会导致技术困难和管理问题
  • 一致性
    • 为了保持一致性、兼容性,带来了软件复杂性
  • 可变性
    • 修改成本低,需求场景变化,会导致软件遭受持续变更的压力
  • 不可见性
    • 软件不具有空间的形体特征,限制了个人的设计过程,也阻塞了思路交流

如何解决根本问题

  • 购买软件
    • 构建软件最彻底的解决方案是不开发软件【。。】
    • 因为多用户成本分摊,购买软件的成本比开发更低
  • 需求精炼和快速原型
    • 客户并不知道需求是什么,PM/RD 需要去抽取最深层次的需求。【不要轻信客户的想法。之前碰到一个客户要求反反复复修改,深入了解后是可以抽成一个通用功能】
    • 快速原型来模拟页面,明确实际概念结构,和客户对齐。
  • 增量开发
    • 先设计和开发主干,再填充子系统
    • 需要自上而下的设计【反复强调,有多重要就不用说了吧】
  • 卓越的设计人员
    • 培养和获取接触的设计人员

十七. 再论没有银弹

次要问题不会占据开发 90%的时间,也不会给生产率带来数量级的提高,所以要着手解决开发的根本问题。【我们现在工程化手段感觉还是在解决次要问题】

改善根本问题的所在

  • 复杂性是层次化的
    • 大多数时候系统的复杂性来自我们没有合理的抽象和分层
    • 逻辑处理的方法需要原子性,方便后续复用
    • 考虑系统设计的层次化、增量化 【想起一些大型组件的功能可插拔设计】
  • 不可见性
    • 软件许多概念性要素本质是拓扑的,可以使用可视化图形/空间的方式来表达和提效【基于模型的可视化建站?】
    • 这种可视化效果未来可能会改进开发人员思考和探索的质量,从而解决一些根本问题
    • 但是因为软件要素并不存在三维空间,所以不存在概念性设计到图形的简单映射 最后,还是没有银弹可以彻底解决根本问题。。 【软件的复杂多变性,决定了没有银弹】

总结

书里描述的有些零散,我按项目流程梳理了一遍我学习的内容:

  • 需求调研和验证
    • 尽早调研,避免造轮子
    • 满足需求的情况下,优先购买已有产品
    • 快速原型验证需求
    • 需要控制项目需求和功能的规模
  • 组建团队
    • 招优秀的人,好的程序员可能效率是糟糕程序员的 10 倍
    • 组建外科手术队伍,各司其职
    • 少数人思考确定系统的方向,多数人来解决实际实现问题
    • 专业化分工,减少合作成本
  • 项目排期和进度跟踪
    • 基于经验和过往数据来评估排期,不要过于乐观
    • 关注团队人员结构,正视人月概念,不要盲目加人
    • 科学安排项目各个环节的时间,给系统设计和测试预留充分时间
    • 设定清晰的里程碑,并且诚实的去衡量结果是否达标
    • 安排周会、月会,定期同步进度和问题
    • 保持团队进取心,进度往前赶
    • 及时反馈问题,调整计划
  • 系统设计
    • 采用自上而下的设计,提高系统扩展性,避免 bug
    • 由少数有经验的人负责系统设计,保证系统概念完整性
    • 设计人员要有兜底的具体实现方案
    • 需要方案评审,控制系统规模,避免过度设计
    • 设计变更在所难免,要管理好文档和版本
  • 文档沉淀
    • 记录分析和矛盾,方便决策
    • 周期回顾
    • 记录详细功能、设计,方便共享和沟通
    • 重视自文档化
  • 交流合作
    • 鼓励网形交流,而不是树形
    • 采用非正式途径、会议、工作手册等方式,同步进度和问题
    • 通过一些手段(人力划分、限定职责范围)来减少交流成本
  • 提高效率
    • 采用良好的系统设计
    • 重视文档
    • 维护通用工具
    • 构建测试平台
  • 处理未知变化
    • 拥抱变化,对系统设计、组织架构的变更做好准备
    • 重视前期系统设计和测试用例,极大减少系统 bug
    • 明确系统目标,保持设计完整性,减少系统熵值增加速度,

项目管理主要是对人的关注和管理,整本书大部分都在讲述软件工程管理方面的事情,比较少涉及技术问题,主要是来自一个信念:对于项目的成功而言,项目人员的素质、人员的组织和管理是比使用的工具或采用的技术方法更重要的因素。 并且人月神话也秉持着没有银弹的观点,没有限制我们处理问题的方法和想象的空间。项目管理是为软件开发服务的,我们在管理中的规范和标准并不是目的,而是手段。人月神话恰好是提供了一些启发性的观点,给予我们对项目管理有更多的思考和想象。