整体部分(The Whole and the Parts)
□防范bug的定义
防范bug的定义。系统各个组成部分的开发者都会做出一些假设,而这些假设之间的不匹配,是大多数致命和难以察觉的bug的主要来源。
产品的概念完整性在使它易于使用的同时,也使开发更容易进行以及bug更不容易产生。
关键的工作是产品定义。许许多多的失败完全源于那些产品未精确定义的地方。
细致的功能定义、详细的规格说明、规范化的功能描述说明以及这些方法的实施,大大减少了系统中必须查找的bug数量。
□测试规格说明
在编写任何代码之前,规格说明必须提交给测试小组,以详细地检查说明的完整性和明确性。
□自顶向下的设计
好的自顶向下设计从几个方面避免了bug。首先,清晰的结构和表达方式更容易对需求和模块功能进行精确的描述。其次,模块分割和模块独立性避免了系统级的bug。另外细节的隐藏使结构上的缺陷更加容易识别。第四,设计在每个精化步骤的层次上是可以测试的。
当遇到一些意想不到的问题时,按部就班的流程并不意味着步骤不能反过来,直到推翻顶层设计,重新开始整个过程。
一些糟糕的系统往往就是试图挽救一个基础很差的设计,而对它添加了很多表面装饰般的补丁。自顶向下的方法减少了这样的企图。
□结构化编程
基本上,该方法所设计程序的控制结构,仅包含语句形式的循环结构。
关键的地方和构建无BUG程序的核心,是把系统的结构作为控制结构来考虑,而不是独立的跳转语句。
★构件单元调试
早期的测试,机器采用纸带或者磁带的方式来读写,采用离线设备来完成磁带的准备和打印工作。所以花费在编写调试程序上的时间可能是程序编制时间的一半。
★交互式调试
交互式调试拥有和本机调试一样的操作实时性,但前者并没有象后者要求的那样,在调试过程中要预先进行计划。
第一次交互取得的工作进展是后续交互的三倍。这强烈暗示着,由于缺乏对调试会话的计划,我们没有发掘交互式调试的潜力。原有本机调试技术中那段高效率的时间消失了。
良好终端系统的正确使用,往往要求每两小时的终端会话对应于两小时的桌面工作。一半时间用于上次会话的清理工作:更新调试日志,把更新后的程序列表加入到项目目录中,研究和解析调试中出现的奇怪现象。剩余一半时间用于准备:为下一次操作设计详细的测试,进行计划的变更和改进。
★系统集成调试
软件系统开发过程中出乎意料的困难部分是系统集成测试。系统调试花费的时间会比预料的更长,需要一种完备系统化和可计划的方法来降低它的困难程度。
□使用经过调试的构件单元。
—使用完好的、经过调试的构件,能比搭建测试平台和进行全面的构件单元测试节省更多的时间。
文档化的bug:它申明构件单元所有的缺陷已经被发现,还没有被修复,但已经做好了系统调试的准备。
对文档记录bug的修复工作本身会注入未知的问题,接下来的系统测试会令人困惑。
但是所有这些良好的愿望只是试图为结果的偏离寻找一些合理理由。调试人员并不了解BUG引起的后果,另外对文档记录BUG的修复工作本身会注入未知的问题,接来下的系统测试会令人困惑。
□搭建充分的测试平台
辅助测试平台,指的是供调试使用的所有程序和数据,它们不会整合到最终产品中。
测试平台可能会有相当于测试对象一半的代码量,但这是合乎情理的。
一种测试辅助的形式是伪构件(dummy component),它仅仅由接口和可能的伪数据或者一些小的测试用例组成。另一种形式是微缩文件(miniature file),创建一个仅包含典型记录,但涵盖全部描述的小型文件是非常值得的。还有一种方式是辅助程序(auxiliary program)。
□控制变更
必须有人负责控制和负责各个构件单元的变更或者版本之间的替换。
必须存在系统的受控拷贝:一个是供构件单元测试使用的最终锁定版本;一个是测试版本的拷贝,用来进行缺陷的修复;以及一个安全版本,其他人员可以在该拷贝上工作
必须有人负责各个构件单元的变更或者版本之间的替换。
□一次添加一个构件
这样做的好处同样是显而易见的,但是乐观主义和惰性常常诱使我们破坏这个规则。
因为离散构建的添加需要调试伪程序和其它测试平台,有很多工作要做。
注意必须拥有完整的测试用例,在添加了新构件之后,用他们来测试子系统。因为那些原来可以在子系统上成功运行的用例,必须在现有系统上重新运行,对系统进行回归测试。
□阶段(量子)化、定期变更。
其他开发团队会使用经过测试的最新集成系统,作为调试自己程序的平台。测试平台的修改,会阻碍他们的工作。
但变更必须被阶段化,并且定期发布。这样,每个用户拥有稳定的生产周期,其中穿插着测试平台的改变。这种方法比持续波动所造成的混乱无序要好一些。
阶段量子要么很大,间隔很宽,要么小而频繁。小而频繁的阶段很容易变得不稳定,我的经验也同样证实了这一点--因此我决不会在冒险采用后一种策略。
量子(阶段)化变更方法非常优美地容纳了紫色线束技术:直到下一次系统构件的定期发布之前,都一直使用快速补丁;而在当前的发布中,把已经通过测试并进行了文档化的修补措施整合到系统平台。
祸起萧墙(Hatching a Catastrophe)
重大灾害是比较容易处理的,它往往和重大的压力、彻底的重组、新技术的出现有关,整个项目组通常可以应付自如。但是一天一天的进度落后是难以识别、不容易防范和难以弥补的。
★里程碑还是沉重的负担?
进度表上的每一件事,被称为“里程碑”,它们都有一个日期。选择日期是一个估计技术上的问题。
里程碑的选择只有一个原则,那就是,里程碑必须是具体的、特定的、可度量的事件,能够进行清晰定义。
具体的里程碑是百分之百的事件。
里程碑有明显边界和没有歧义,比它容易被老板核实更为重要。 如果里程碑定义得非常明确,以致于无法自欺欺人时,很少有人会就里程碑的进展弄虚作假。
好的里程碑对团队来说实际上是一项服务,可以用来向项目经理提出合理要求的一项服务,而不确切的里程碑是难以处理的负担。当里程碑没有正确反映损失的时间,并对人们形成误导,以致事态无法挽回的时候,它会彻底碾碎小组的士气。慢性进度偏离同样也是士气杀手。
★其他的部分反正会落后
进取提供了缓冲和储备,使开发队伍能够处理常规的异常事件,可以预计和防止小的灾祸。而对任务进行计算和对工作量进行度量,会对进取超前会造成一些消极的影响——这时,人们往往会比较乐观地放缓工作节奏。
必须关心每一天的滞后,它们是大灾祸的基本组成元素。
如何判断哪些偏离是关键的呢?只有采用PERT或者关键路径技术才能判断。
它显示谁需要什么样的东西,谁位于关键路径上,他的工作滞后会影响最终的完成日期。
PERT的准备工作是PERT图使用中最有价值的部分。它包括整个网状结构的展开、任务之间依赖关系的识别、各个任务链的估计。这些都要求在项目早期进行非常专业的计划。第一份PERT图总是很恐怖的,不过人们总是不断地进行努力,运用才智制订下一份PERT图。
★地毯的下面
每个老板都需要两种信息:需要采取行动的计划方面的问题,用来进行分析的状态数据。
有两种掀开毯子把污垢展现在老板面前的方法,它们必须都被采用。一种是减少角色冲突和鼓励状态共享,另一种是猛地拉开地毯。
□减少角色的冲突
老板必须区别行动信息和状态信息。他必须规范自己,不对项目经理可以解决的问题做出反应,并且决不在检查状态报告的时候做安排。
如果老板把会见、评审、会议明显标记为状态检查(status-meeting)和问题-行动(problem-action)会议,并且相应控制自己的行为,这对整个过程会很有帮助。
□猛地拉开地毯。
PERT图以及频繁的里程碑是这种评审的基础。大型项目中,可能需要每周对某些部分进行评审,大约一个月左右进行整体评审。
我发现在里程碑报告中很容易记录“计划”和“估计”的日期。计划日期是项目经理的工作产物,代表了经协调后的项目整体工作计划,它是合理计划之前的判断。
PERT图的准备工作是老板和要向他进行汇报的经理们的职责。需要一个小组(一至三个人)来关注它的更新、修订和报告,这个小组可以看作是老板的延伸。
对计划和控制职能进行适度的技术人力投资是非常值得赞赏的。它对项目的贡献方式和直接开发软件产品有很大的不同。计划和控制小组作为监督人员,明白地指出了不易察觉的延迟,并强调关键的因素。他们是早期预警系统,防止项目以一次一天的方式落后一年。