人月神话
焦油坑
问题:类比焦油坑,介绍系统开发职业,以及这个职业中的乐趣与苦恼。
- 编程系统产品
程序->编程系统->编程系统产品
程序->编程产品->编程系统产品 - 职业的乐趣
- 职业的苦恼
人月神话
问题:缺乏合理的进度安排是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还要大。什么原因导致了这种灾难发生?
- 乐观主义
- 人月
正确的理解人月。人月是一个衡量工作量的单位,1人月就是一个人工作一个月。100人月就是100人工作100一个月。
注意人和月不可以等价转换要综合考虑很多情况。 - 系统测试
对于软件任务的进度安排经验法则:
1/3 计划
1/6 编码
1/4 构建测试和早期系统测试
1/4 系统测试,所有的构件已完成 - 空泛的估算
- 重复产生的进度灾难
项目的时间依赖于顺序上的限制,人员的最大数量依赖于独立子任务的数量
外科手术队伍
问题:如何在有意义的进度安排内创建大型的系统?
- 问题
对于效率和概念的完整性来说,最好由少数的人员来设计和开发,而对于大型系统,则需要大量的人手,以使产品能在时间上满足要求。如何调和这两方面的矛盾? - Mills的建议
- 如何运作
- 团队的扩建
扩建过程的成功依赖于,每个部分的概念完整性。只有概念的完整性越高扩建才更容易。
必须清晰的划分体系结构设计和实现之间的界线。
贵族专制,民主政治和系统设计
- 概念的完整性
主张:在系统设计中,概念完整性是最重要的考虑因素。
即为了反映一系列连贯的设计思路,宁可省略一些不规则的特性和改进,也不提倡独立的和无法整合的系统,哪怕他们其实包含着许多很好的设计。 - 获得概念的完整性
编程系统(软件)的目的是使计算机更加容易的使用。为了做到这一点计算机装备了语言和各种工具,使用这些工具的代价是:工具软件的外部描述的规模大小是计算机系统本身说明的10~20倍。对于用户来说寻找一个特定功能很容易,但相应的有太多的选择,需要记住太多的格式。
所以软件的目标是易用性,功能与概念的复杂程度的比值才是系统设计的最终测试标准。单是功能本身或者简洁都无法成为一个好的设计评判标准。
简洁和直白来自概念的完整性,易用性实际上需要设计的一致性和概念上的完整性。 - 贵族专制统治和民主政治
概念的完整性要求设计必须由一人,或者非常少数互有默契的人员来实现。
对于大型的项目,将设计方法,体系结构方面的工作与具体的实现相分离是获得概念完整性的强有力方法。
结构师的工作,是运用专业的技术知识来支持用户的真正利益。
结构师一直处在解决用户问题,实现用户利益的核心地位。若要得到系统概念上的完整性,必须有人控制这些概念。
产品的成本性能比在很大程度上依赖实现人员,易用性在很大程度上依赖结构师。
外部的体系结构规定实际上是增强,而不是限制实现小组的创造性。 - 在等待时,实现人员应该做什么
在说明完成时候,才雇用编程实现人员。
画蛇添足
问题:用什么样的准则和机制来约束结构师的创造性热情?
- 结构师的交互准则和机制
- 自律——开发第二个系统所带来的结果
贯彻执行
问题:对于一个由1000人开发的系统,一个10个结构师的小组如何保持系统概念的完整性?
- 手册:文档化的规格说明
- 形式化的定义
- 直接整合
- 会议和大会
- 多重实现
- 电话日志
- 产品测试
为什么巴比伦塔会失败
- 巴比伦塔的管理教训
目标清晰,人力充足,时间足够,技术足够 依旧失败了。
缺乏- 交流:无法交流从而无法合作,从而工作陷入停顿。
- 交流的结果:交流的缺乏导致了争辩,沮丧和群体猜忌。
- 大型编程项目中的交流
现实中,随着工作的进行,许多小组慢慢地修改自己程序的功能、规模、和速度,他们明确或者隐含的更改了一些有效输入和输出结果用法上的约定。那么,团队如何进行相互之间的交流沟通?- 非正式途径:清晰定义小组内部的相互关系和充分利用电话进行沟通,从未达到对所书写文档的共同理解。
- 会议:常规项目会议。团队一个接一个地进行简要的技术概述。这样能澄清成百上千的细小误解。
- 工作手册:在项目开始阶段,就准备正式的项目工作手册
- 项目工作手册
是什么:不是一篇独立的文档,是对项目必须产出大的一系列文档进行组织的一种结构。项目所有的文档都必须是该结构的一部分。包括目的,外部规格说明,接口说明,技术标准,内部说明和管理备忘录。
为什么:
处理机制:
现在如何入手: - 大型编程项目的组织架构
团队组织的目的是减少所需的交流和合作的数量。减少交流的方法是人力划分和限定职责范围。当使用人力划分和职责限定时,树状管理结构反映出对详细交流的需求会减少。
但是实际上,树状组织架构是作为权利和责任的结构而出现的,其基本原理--管理角色的非重复性--导致了管理结构是树状的,但是交流的结构并未限制的如此严格,树状结构几乎不能用来描述交流沟通,因为交流是通过网状结构进行的。
如何考虑树状编程队伍,使这种结构行之有效,每棵子树所必需具备的基本要素。及他们的关系。
胸有成竹
前面有 计划进度、编码、构建测试、系统测试 分别占用整个时间的比例。
但是我们不能根据已经知道的某个时间,除以对应的比例来估算整个时间。
构建独立小型的程序的时间统计数据不适用与编程系统产品,因为工作量是规模的幂函数,即工作量随着这产品规模的增大指数型增加。
消足适履
- 作为成本的空间
程序有多大?除了运行时间以外,它占据的空间也是主要开销。
用户支付给开发者一笔费用,作为必要分担的开发成本。IBM APL交互式软件系统的租金为每月400美元,在使用时至少占用160K字节的内存,内存的租金为12美元每月千字节。
总结:用户支付给开发者开销以外,部署的机器和运行时占用的内存也需要钱(现在好像没有这么贵)。
要综合考虑花费在内存上的租金是否能用在其他硬件、编程人员、应用程序上有更好的性价比。
当系统设计者认为对于用户而言,常驻程序内存的形式比加法器、磁盘等更加有用时,它会把硬件生死线中的一部分移到内存。
规模是软件系统产品一个很大的组成部分,开发人员必须设置规模的目标,控制规模,不要有不必要的规模。 - 规模控制
仅对核心程序设定规模是不够的,必须把所有方面的规模都编入预算。- 和指定驻留空间预算一样,应该制定总体规模和预算;和制定规模预算一样,应该制定后台存储访问的预算。
- 在指明模块有多大的同时,确切定义模块的功能。
在规模上碰到问题的程序员,会检查自己的代码舍弃其中的一部分功能,因此要明确定义功能。 - 培养开发人员从系统整体出发,面向用户的态度是软件编程管理人员最重要的职能。
在大项目中,团队成员往往局部优化自己的程序,很少会考虑对整体的影响。
- 空间技能
在性能保持不变的情况下,更多的功能意味着更多的空间。 用功能交换尺寸不是时间换空间是减少不必要的重复功能换空间 - 数据的表现形式是编程的根本
由于缺乏空间而绞尽脑汁的编程人员,常常能通过从自己的代码中挣脱出来,回顾,分析,实际情况,仔细思考程序的数据,最终获得非常好的结果。
实际上,数据的表现形式是编程的根本。调整数据或表的表达形式
提纲挈领
没分文档的准备工作是集中考虑,并使各种讨论意见明朗化的重要时刻。
文档的跟踪维护是项目监督和预警的机制。文档本身可以作为检查列表、状态控制,也可以作为汇报的数据基础。
任何管理任务的焦点都是时间、地点、人员、项目内容和资金。
为什么要有正式的文档?
- 书面记录决策是必要的,只有记录下来,分歧才会明朗,矛盾才会突出。
- 文档作为同其他人沟通的渠道。项目经理会发现许多应该诶普遍认同的策略,完全不为团队的一些成员所知。项目经理的基本职责是使每一个人向着相同的方向前进,所以主要的工作室沟通,不是做出决定。文档可以极大的减少负担。
- 文档作为数据基础和检查列表。通过周期性的回顾,能清楚项目所在的状态,以及哪些需要重点进行更改和调整。
项目经理的任务是制定计划,并实现计划。但是只有书面计划是精确和可以沟通的。计划中包括了时间,地点,人员,项目内容和资金。可以将文档作为工具友好地利用起来。
未雨绸缪
- 实验性工厂和曾大规模
大多数项目,第一个开发的系统并不合用。是否预先计划抛弃原型的开发或者是否将原型发布给用户。
为舍弃而计划 - 唯一不变的就是变化本身
抛弃原型概念本身就是对事实的接受--随着学习的过程更改计划。 - 为变更设计系统
如何为上述变化设计系统?
最重要的措施是使用高级语言和自文档技术,以减少变更引起的错误。 - 为变更计划组织架构
为变更组建团队比为变更进行设计更加困难。只要管理人员和技术人才的天赋允许,老板必须对他们的能力培养给予极大的关注,是管理人员和技术人才具有互换性。 - 前进两步,后退一步
发布后的变更被称为程序维护。对于一个广泛使用的程序,其维护成本通常是开发成本的40%以上。用户越多,错误越多。
程序维护中个一个基本问题---缺陷修复总会以固定20%~50%的几率引入bug。整个维护过程是前进两步,后退一步。
维护人员常常不是编写代码的开发人员,而是一些初级程序员或者新手。
使用能消除或至少指明副作用的程序设计方法,会在维护成本的有很大的回报。同样设计实现的人员越少,接口越少,产生的错误就越少。
6. 前进一步,后退一步
大型操作系统:模块总数量随版本号的增加呈线性增长,但是收到影响的模块数量随版本号的增加呈指数增长。
干将莫邪
每个团队有一个工具管理人员,这个角色管理所有的通用工具,能指导他的客户和老板使用这些工具。同时,还能编制老板需要的专业工具。
工具包括:
机器支持有目标机器和辅助机器。
仿真装置:如果目标机器是新机器,则需要一个目标机器的逻辑仿真装置。
数据服务:编译器和汇编平台、程序库和管理、编程工具、文档系统
高级语言和交互式编程:
整体部分
如何开发一个可以运行的系统?如何测试系统?如何将经过测试的一系列后见集成到已经测试过,可以依赖的系统?
- 剔除Bug的设计
Bug的主要来源,是系统各个组成部分的开发者都会做一些假设,这些假设之间的不匹配。
产品的概念完整性在使它易于使用的同时,也使开发更容易进行,而且Bug更不容易产生。- 关键的工作是产品定义。许许多多的失败完全是因为那些产品未精确定义的地方而导致的。 细致的功能定义、仔细的规格说明、规范化的功能描述说明以及这些方法的实施,大大减少了系统中必须查找的bug数量。
- 自上而下的设计:一种被很多最优秀的编程人员多年使用的设计流程形式化。将系统开发划分为体系结构设计、设计实现和物理编码实现,每个步骤都可以使用自上而下的方法很好的实现。
将设计看成一系列精华步骤。通过比较粗略的任务定义和大概的解决方法得到主要结果。然后,对该定义和方案进行细致的检查,以判断结果与期望之间的差距。同时,将上述步骤的解决方案在更细的步骤中进行分解,每一项任务定义的精化变成了解决方案中算法的精化,还可能伴随这数据表达式的精化。
一些糟糕的系统往往就是试图挽救一个基础很差的设计,而对它添加了各种各样表面装饰般的补丁。自上而下的方法减少了这样的企图。
- 构建单元调试
- 系统集成测试