顶级程序员书单系列五:《人月神话》

我的记忆

记得是许久之前读的这本计算机领域的经典图书了,当时就只记住了一点:增加人,并不能解决问题。由于当时的我并没有实际的开发经验,觉得他给出的建议并没有其他顶级程序员书单给的更棒,就仅把他排到15-20名的位置。后来,经技术交流群里的小伙伴的提醒,我决定重读此书。

重读这本书的感受

读完感觉,这本书还是超级厉害的,它不同于其他图书教授的“术”,他已经上升为“道”了。软件开发是熵增的过程,我认为优秀的开发人员就应该是一位优秀的园艺设计师(掌握设计)和认真的修草工(掌握编码),只有这样,应对复杂的软件开发,才可以真正的让软件的生命更长一些,让软件的一生更精彩一些。顶层设计很重要,后期的重构也很重要,软件开发,还有一段很长的路要走啊!

一些笔记

什么是编程

“这,就是编程。一个许多人痛苦挣扎的焦油坑以及一种乐趣和苦恼共存的创造性活动。对于许多人而言,其中的乐趣远大于苦恼。而本书的剩余部分将试图搭建一些桥梁,为通过这样的焦油坑提供一些指导。”

软件开发计划

1/3 计划
1/6 编码
1/4 构件测试和早期系统测试
1/4 系统测试,所有的构件已完成

Brooks 法则

向进度落后的项目中增加人手,只会使进度更加落后。

手册和文档的重要性

技术说明几乎是必不可少的。如果某人就硬件和软件的某部分,去查看一系列相关的用户手册。他发现的不仅仅是思路,而且还有能追溯到最早备忘录的许多文字和章节,这些备忘录对产品提出建议或者解释设计。对于技术作者而言,文章的剪裁粘贴与钢笔一样有用。

良好的组织结构

如果项目有 n 个工作人员,则有(n*n - n)/ 2 个相互交流的接口,有将近 2的n次方个必须合作的潜在团队。团队组织的目的是减少不必要交流和合作的数量,因此良好的团队组织是解决上述交流问题的关键措施。减少交流的方法是人力划分(division of labor)和限定职责范围。

树状编程队伍

  1. 任务(a mission)
  2. 产品负责人(a producer)
  3. 技术主管和结构师(a technical director or architect)
  4. 进度(a schedule)
  5. 人力的划分(a division of labor)
  6. 各部分之间的接口定义(interface definitions among the parts)

开发人员应该具备的态度

从系统整体出发、面向用户开发。

数据的表现形式是编程的根本

创造出自精湛的技艺,精炼、充分和快速的程序也是如此。技艺改进的结果往往是战略上的突破,而不仅仅是技巧上的提高。这种战略上突破有时是一种新的算法,如快速傅立叶变换,或者是将比较算法的复杂度从 n*n 降低到 n log n。

唯一不变的就是变化本身

“是否预先计划抛弃原型的开发,或者是否将该原型发布给用户?”——为舍弃而计划,无论如何,你一定要这样做。

一旦认识到试验性的系统必须被构建和丢弃,具有变更思想的重新设计不可避免,从而直面整个变化现象是非常有用的。第一步是接受这样的事实:变化是与生俱来的,不是不合时宜和令人生厌的异常情况。Cosgrove 很有洞察力地指出,开发人员交付的是用户满意程度,而不仅仅是实际的产品。用户的实际需要和用户感觉会随着程序的构建、测试和使用而变化。

如何开发一个可以运行的系统?

剔除 bug 的设计(防范 bug 的定义/测试规格说明/自顶向下的设计)
构件单元调试(本机调试/测试用例)
系统集成调试(使用经过调试的构件单元/搭建充分的测试平台/控制变更/一次添加一个构件/阶段(量子)化、定期变更)

流程图怎么画

因此,一页纸的流程图,成为表达程序结构、阶段或步骤的一种非常基本的图示。同样,它也非常容易绘制。图纸既没有,也不需要遵循精心制订的 ANSI 流程图标准。所有图形元素如方框、连线、编号等,只需要能使这张详细的流程图可以理解就行了。

没有银弹

复杂度、一致性、可变性和不可见性。困难是现代软件系统无法规避的固有的概念复杂性,无论是任何时间,使用任何方法设计和实现软件的功能,它都存在。因此——没有银弹。

但1990 年 Brad Cox 的一篇非常出色的论文《这就是银弹》,有说服力地指出重用和交互的构件开发是解决软件根本困难的一种方法。作者也表示赞同。

瀑布模型是错误的

瀑布模型的基本谬误是它假设项目只经历一次过程,而且体系结构出色并易于使用,设计是合理可靠的,随着测试的进行,编码实现是可以修改和调整的。换句话说,瀑布模型假设所有错误发生在编码实现阶段,因此它们的修复可以很顺畅地穿插在单元和系统测试中。

增量开发模型更佳——渐进地精化

从事实时系统开发的 Harlan Mills,早期曾提倡我们首先应该构建实时系统的基本轮询回路,为每个功能都提供子函数调用(占位符),但仅仅是空的子函数。对它进行编译、测试,使它可以不断运行。它不直接完成任何事情,但至少是正常运行的。

接着,我们添加(可能是基本的)输入模块和输出模块。瞧,一个可运行的系统出现了,尽管只是一个框架。然后,一个功能接一个功能,我们逐渐开发和增加相应模块。在每个阶段,我们都拥有一个可运行的系统。如果我们非常勤勉,那么每个阶段都会有一个经过调试和测试的系统。(随着系统的增长,使用所有先前的测试用例对每个新模块进行的回归测试也采用这种方式进行。)

在每个功能基本可以运行之后,我们一个接一个地精化或者重写每个模块——增量地
开发整个系统。不过,我们有时的确需要修改原有的驱动回路,或者甚至是回路的模块接口。

因为我们在所有时刻都拥有一个可运行的系统,所以我们可以很早开始用户测试,并且可以采用按预算开发的策略,来彻底保证不会出现进度或者预算的超支(以允许的功能牺牲作为代价)。

猜你喜欢

转载自blog.csdn.net/qq_32648593/article/details/106931208