第 8 章 胸有成竹

  • 构建小型程序的(统计)数据不适用于大型系统项目
  • 开发投入随程序规模的指数增长,指数约为 1.5。Boehm:1.05~1.2
  • 项目估算中对人年工作时间数的计算会有误差(可能有一半时间用于处理编码调试无关的工作,如文字工作,其他琐事等)
  • 生产率会根据任务本身的困难程度表现出显著差异,简单任务产率高,复杂的则低。
  • Corbato 的数据:生产率对常用编程语句基本是固定的(包括注释和可能的错误),并且是与行数相关。因此适当的高级语言可以提高生产率(5倍)

第 9 章 削足适履

  • 程序的规模控制,当时的内存使用费用(12$/月/kb)和软件使用费用(400$/月)都十分高昂,运行在磁带而不是内存上时则性能堪忧
  • 设立驻留空间和后台存储访问的预算
  • 规模不是坏事,但不必要的规模是不可取的,开发人员需要控制规模
  • 同时为防止为了压缩代码空间而甩锅,需要确切定义模块的功能
  • 防止过于注重优化自己的模块(局部)而忽略了对整体的影响
  • 减小尺寸方法一:裁剪功能,但是功能过于细分也会耗费空间和降低性能
  • 减小尺寸方法二:(空间换时间?没看懂)提高团队的水平;开发高效/简洁的两套公共库(有些过时了)
  • 从根本上改变:改变数据的表达方式,或者使用高速的算法之类的。[数据的表现形式是编程的根本]

第10 章 提纲挈领

  • 堆积如山的资料中只有少数才是关键
  • 每份文档的准备工作是集中考虑,并使各种讨论意见明朗化的重要时刻。不这样的话项目会永远处在混乱中
  • 管理任务的关注焦点都是时间(进度)、地点(工作空间分配)、人员(组织架构图,组织架构受产品架构的约束)、项目内容(目标和技术说明)和资金(预算)
  • 项目经理的基本职责是使每个人朝相同的方向前进,主要工作是沟通,而不是决定。任务是制订并实现计划

第 11 章 未雨绸缪

  • 只有变化才是不变的
  • 重要的是先做尝试,失败了再试试别的方法
  • 大型工程总是始于一个试验性的系统(原型),完成验证后再将其丢弃
  • 具体措施有细致的模块化、可扩展的函数、精确完整的模块间接口设计和完备的文档;调用队列、表驱动;使用高级语言和自文档技术
  • 为变更组建团队。项目经理需要有两到三个顶级程序员作为技术轻骑兵,以便工作密集时由他们高效地解决问题。系统变化->管理结构变化->技术路线和管理路线的级别要对应,对于人才两个方面都要培训
  • 维护程序的成本通常是开发成本的40%或更多,且非常受用户量影响
  • 缺陷修复总会以固定(20%~50%)的几率引入新的 bug,所以是“前进两步后退一步”。小错误很可能是系统级别的问题。另外改 bug 的也常常不是写 bug 的那个人。设计的实现人员越少、接口越少,错误也会越少
  • 改 bug 使系统变得混乱(倾向于破坏系统的架构,熵增),系统设计使系统变得有序(熵减),此时是“前进一步后退一步”,虽然整个过程中系统仍然是可用的

第 12 章 干将莫邪

  • 巧匠因为他的工具而出名
  • 个性化的工具对软件项目来说却是个妨碍,影响沟通。另外工具软件的生命周期也短
  • 每个团队应该有一个专门管理工具的人,但把所有管理人员集中成一个工具小组又是没有必要的(书里没说,应该是有额外的管理开销?)
  • 提前预约,按时间块分配机器资源
  • 如果是新产品,则需要它的逻辑仿真装置,可提供可靠的调试平台。可靠不等于精确。不过至少它在一段时间内会比新硬件要稳定
  • 不确定会剥夺开发人员找 bug 的动力,因为 bug 可能根本不存在(这里指硬件平台无法稳定工作)
  • 高级语言,在更高的层面开发,强大的编译器代码优化
  • 交互式编程(如 python 的控制台执行脚本语句)。漫长的调试周转时间是调试的祸根

第 13 章 整体部分

  • 关键的工作是产品定义,未精确定义的地方容易导致失败(与 bug)
  • 自上而下的系统设计,并逐步精细化。好处:结构清晰,功能易描述,细节的抑制使结构上的缺陷更明了,设计在每个精化步骤上都是可测试的,测试可以尽早开展
  • 单元调试的发展:本机调试、内存转储、快照、交互式调试、完整的测试用例
  • 系统集成调试:
    • 使用经过调试的构件单元,而不是合在一起尝试,这样能节省更多测试时间。
    • 充分的测试平台:伪构件–用来测试接口是否正常工作。专用的测试数据生成/分析等辅助程序
    • 测试期间做好变更控制,不能像开始前随意更新代码
    • 小而频繁的版本更新容易使系统变得不稳定,可以拉长间隔扩大阶段,以获得相对的稳定

第 14 章 祸起萧墙

  • 项目的延迟是一天天积累下来的
  • 定义清晰、可度量的而不是模糊的里程碑,避免弄虚作假
    • 正:结构师签字认可的说明、源码完成并输入到磁盘库、测试用例全部通过
    • 反:代码 90% 完成、调试过程中总是“99%完成”
  • 在某项活动开始前着手估计,并且两周仔细修订一次,这样最终结果就不会偏离太多;期间对所需时间的高估会不断下降,但低估却会一直持续到 deadline 前三周左右
  • 团队要有进取心,要关心每一天的滞后
  • PERT 图澄清依赖关系和关键路径,为甩锅提供答案
  • 面对进度落后,老板需要减少和一线经理的角色冲突,不越俎代庖,以便一线经理能提交真实情况。老板要把会议分为“状态-检查”和“问题-行动”会议,只在必要的时候采取措施。
  • 拥有了解真相的评审机制。PERT 图及明确的里程碑(需要有一个1-3人的计划和控制小组来跟踪更新 PERT 图)

第 15 章 另外一面

  • 如何持续更新软件的文档–身体力行代替长篇大论
  • 什么样的文档
    • 使用程序:描述性的文字,展示程序各方面的特征(作用,环境,输入输出的范围和格式等等)
    • 验证程序:一小组测试用例,以验证软件能够正常工作
    • 修改程序:完备的细节。子系统的结构图,算法的完整描述,所有文件规划的解释,数据流处理概要等
  • 流程图:被过分吹捧的东西,可以从文字列表表达这些内容,另外只画在一页纸上就行了。很多公司都是事后才补的流程图。
  • 自文档化:文档依附于源代码中。
    • 使用语言的声明、符号名称等表达更多的信息
    • 用空格和一致的格式表现从属和嵌套关系,提高可读性
    • 除了逐行注释,也要有段落注释,能从总体上加深对事情的理解。

第 16 章 没有银弹

  • 软件活动的
    • 根本任务:打造构成抽象软件实体的复杂概念结构;
    • 次要任务:用编程语言表达这些抽象实体,并在空间和时间的限制下将其映射成机器语言
  • 关于软件任务中的必要活动,应当
    • 多调研,避免开发重复软件
    • 获取/制定需求时,使用快速原型开发
    • 逐步添加新的功能
    • 挑选/培养杰出新生代的概念设计人员
  • 根本困难
    • 复杂度。软件的复杂度是它的根本属性,复杂度会导致沟通、复用困难、bug、安全性、管理问题、概念设计、学习理解等一系列问题。
    • 一致性。各种人为惯例、系统、遵循新接口或兼容性导致的复杂特性
    • 可变性。软件属于纯粹思维活动的产物,可以无限扩展。软件扎根于文化的母体中。各种应用、用户、自然及社会规律、硬件等的变化都强迫着软件发生改变。
    • 不可见性。软件无法可视化,没有空间形体特征,阻碍了个人设计和思路的互相交流
  • 解决次要问题的突破
    • 高级语言的诞生
    • 分时。保证及时性,避免设计-验证的流程拖得太长打断了思路
    • 统一的编程环境,解决了程序的共用问题,提高生产效率
  • 银弹的希望
    • Ada 等高级编程语言。仅仅是另一种高级程序语言
    • 面向对象编程。仅消除了设计表达上的困难,仍未解决设计的复杂度
    • 人工智能。AI-1指用计算机解决过去仅由人类解决的问题。AI-2模仿人类的行为解决问题。AI-2的专家系统可以基于规则库实现,为软件活动提供各种优化建议等。实现的要点在于能否自动或半自动的产生规则。最有力的贡献在于用优秀的经验和知识为缺乏经验的开发人员提供百科全书式的帮助,传播优秀实践。
    • “自动”编程。“自动编程总是成为一种热情,使用现在并不可用的更高级语言编程的热情”。确实存在一些例外,但它们不外乎:用较少参数便可以迅速描述特征;已知解决方案很多;对给定问题参数,如何选择具体方案也有清晰的规则。对于本身就是人类思维活动,并且具有高度复杂性的软件来说,存在自动编程意味着已经实现了先进的人工智能
    • 图形化编程。基于流程图进行的设计。然而先前提到流程图是不必要的工具,表现复杂程序时的低效,并且软件是难以可视化的,许多交叉引用、层次逻辑、数据流等逻辑无法在图形中表达。
    • 程序验证。并不能在设计之初就消除 bug。尽管能减少测试的工作量,却不能将其取代。而且程序验证也只能建立满足技术说明的程序,而设计完整的技术说明才是最困难的部分。
    • 环境和工具。统一的通用工具和系统,智能化编辑器等。开发环境中部署集成数据库,记录开发细节供参考等。有价值,但回报也有限。
    • 工作站。除了编译机,日常的机器基本已经不成负担了…
  • 概念上根本问题的解决方法:如何准确地表达复杂概念结构
    • 不开发软件是解决复杂度的最好方法(认真脸),使用现有的软件
    • 需求精炼&快速原型。从客户处抽取需求是很困难的,通过不断迭代原型来逼近真实需求
    • 增量开发。系统可以一步步完成,逐步发育,而不是一步到位。逆向跟踪方便,原型开发也方便。
    • 卓越的设计人员。但是,低劣的设计和良好的设计可能有方法完整性的差距,但到卓越的层次就需要更多创造性的过程。方法学无法孕育或激发创造力,虽然能培养和释放创造力。所以如何培养这样的人才十分重要。可以有:早期识别和甄选、安排专业导师、制订职业计划、提供人才相互间的交流机会

第 17 章 再论“没有银弹”

  • 回应一些反对声音,以及对论点发表 9 年后的现状做分析

第 18 章 《人月神话》的观点:是与非(仅摘录一部分)

  1. 焦油坑
    • 人们通常希望软件项目在接近完成时能收敛得快一些,然而情况却相反。
    • 真正的权威来自于每次任务的完成
  2. 人月神话
    • 缺乏合理的时间进度是项目滞后的罪魁祸首
    • 所有的编程人员都是乐观主义者:“一切都将运作良好”
    • 我们的构思是有缺陷的,因此总会有 bug
    • 我们围绕着成本核算的估计技术,混淆了工作量和项目进展。人月是带有欺骗性的神话, 因为它暗示人员数量和时间是可以相互替换的。
    • 在若干人员中分解任务会引发额外的沟通工作量
    • 进度安排的经验:1/3计划、1/6编码、1/4单元测试、1/4系统测试
    • 因为我们对自己的估计技术不确定,所以在管理和客户的压力下我们常常缺乏坚持的勇气
    • 向进度落后的项目中增加人手只会使进度更加落后
  3. 外科手术队伍
    • 对真正的大型项目而言小型精干的队伍太慢了
  4. 贵族专制、民主政治和系统设计
    • 概念完整性是系统设计中的首要考虑因素
    • 为了获得概念完整性,设计必须由一个人或者具有共识的小型团队来完成
    • 将设计方法、体系结构方面的工作与具体实现相分离是获得概念完整性的强力方法
    • 要获得完整的系统概念,必须控制这些概念,这就是一种贵族专制统治
  5. 画蛇添足
    • 尽早交流和持续沟通,能厘清分工,并使开发人员获得对设计的信心
    • 实现是开发人员的责任,结构师只能给出建议,但他应该时刻准备着一种建议的实现方法
    • 听取开发人员改进体系结构的建议
  6. 贯彻执行
    • 大型团队中的设计也必须由一到两个人来完成,确保决定一致
    • 体系结构中与先前定义不一致的地方必须重新详细地定义
    • 必须采用形式化定义(https://en.wikipedia.org/wiki/Formal_methods)和记叙性定义中的一种作为标准,另一个作为辅助措施。
  7. 为什么巴比伦塔会失败
    • 缺乏交流,以及交流的结果——组织
    • 共享项目工作手册(包含项目所有的文档),并实时更新,能突出显示每次的变更
    • 子项目中具有两个领导角色:产品负责人和技术主管,有时候他们也可以是同一个人

第 19 章 20 年后的《人月神话》

  • 瀑布模型把系统测试和潜在的用户测试放在构件过程的末尾,因此只有投入全部开发投资后才能发现无法接受的性能问题,笨拙功能等
  • 另一个谬误是它架设整个系统被一次性构建,在所有设计编码单元测试完成后才为闭环的系统测试合并各个部分
  • 尽早保证拥有可运行的系统可以及早开始用户测试,也可以采用按预算开发的策略
  • 无论安排多少人手,几乎没有任何项目能在计算出的最优时间的3/4内获得成功
  • 团队质量是项目成功的最大因素,是下一个因素的 4 倍
  • 管理人员的职责不是要人们去工作,而是创造工作的可能,涉及到如空间、布置、餐饮等世俗的主题《人件:高生产率的项目和团队》
  • 附属职能行使原理,保留低级别组织的责任和自由能释放其创造力
  • 计算机的发展改变了人们的工作方式
  • 购买成品软件或者购买 sdk 简化了生产,提高复用