所有工作都平行进行,并经常保持同步在开发和出品方面,微软所遵循的是一种我们称为“所有工作都平行进行,并经常保持同步”的策略。我们把这一策略细分为五项原则:
·在平行的小组里工作,但要保持同步和每天调试。
·永远拥有一种理论上可以推出的产品,并拥有针对每个主要平台和市场的版本。
·在同一开发场所使用一种共同的开发语言。
·在构造产品的过程中不断测试产品。
·使用度量数据来决定重大的阶段成果和产品的发布。
这些原则刻画了一种相当简单的开发产品的方式,包括许多既是协调的和自发的,也是不断增长的和保持一致的步骤。但是没有一条原则(或微软使用的任一工具、任一方法)本身是高度创新性的。许多软件开发组织采取促进小组或个人分阶段以及平行工作的措施,数不胜数的公司和实验室展示了更为先进的软件设计和开发技术。在定义产品方面,微软的独到之处首先在于其实施:产品开发支持了一种依赖于产品特性的不断演进的大市场战略。同时,人们在一种沿袭自PC软件的组织松散的世界的合作文化中工作。微软的小组致力于快速地工作并将大量的产品和特性以低价提供给一个范围广阔的顾客群。
有一些原则为微软项目提供了框架,促进了其内部协作和问题的解决,同时也为在通常是迅速变动的时间表下产品的循序渐进保留了足够的灵活性。也许最重要的是那些我们用以标示同步化和稳定化过程的方法、程序及工具。微软运用这种方式处理不仅是大的,还有小的项目,并且仍然保有灵活的很小的小组,并保持联络轻松快捷的优越性。关键在于如果开发员可以通过经常的“构造”和定期的“稳定”来保持他们工作的同步化,一个大的小组就可以像许多小的小组一样地工作。这种渐进式的构造也使得微软的组可以永远拥有在理论上已做好出品准备的产品。
还有一些其他因素改善了微软项目的框架结构和灵活性。不同的组使用共同的程序语言和工具,并在同一地理位置工作。这样,个人和小组面对面地交流或转换项目就变得相对容易了。经理把开发员和测试员配对,这样他们在进展过程中就可以一起工作,来测试、调试以及对特性进行集成。由于项目经理是依靠度量(即数量测量)数据和统计趋势来做出诸如何时转向下一个里程碑或何时出品产品的决策,项目也变得更容易控制了。
反之,更具顺序性的软件开发方式可能会要求非常长的时间,因为它们是按顺序而不是平行地计划工作。经理人员也会发现很难精确地控制进展情况,因为他们在开发周期中往往把主要的测试安排得很晚——通常是太晚了。顺序型的过程也不利于促进频繁的产品构造。这样,顺序型开发对微软而言是不适宜的,特别是在那些开发产品的后续版本,如Excel5.0或Word6.0的组里。如果一个项目是创造一种全新的,毫无已有特性基础的产品,这个小组通常会采用一种混合的开发方式。开始的时候它采用较为传统的顺序型步骤;而一旦特性的第一部分完成了,小组就会迅速转向一种经常构造过程以保证下一步所开发的构件的同步化和稳定性。例如,Windows NT作为一种新的操作系统产品在其四年的开发周期中就遵循了这种类型的混合方式。开始时它采用的是说明和细节设计的步骤,然后是渐进的里程碑和每日构造过程。
另外,微软对应用软件产品和系统软件产品所采用的开发原则是有差异的。系统产品由于其代码规模之大和组成部分之多,其构造往往不那么经常。系统产品的运行可能需要一昼夜的时间,而应用软件产品只需几小时。这样,“永远拥有可推出的产品”的概念更多地运用于应用软件而非系统软件产品。
微软同时也更经常地为应用软件产品构造满足不同硬件平台(PC和Macintosh)和不同语言要求的产品版本。系统产品往往只瞄准一个平台(如Windows),最终用户所见到的特性中较少包含母语(如英语)及文化惯例(如货币的版式)方面的内容。应用软件项目更重视实用性测试,因为它们有很多要让非专家用户也能够理解的特性。系统产品则更广泛地使用非常巨大的β测试,因为它们必须能与品种繁多、情况各异的硬件设施和应用软件程序协调运作。
原则一:在平行的小组里工作,但要保持同步和每天调试每日构造过程每日构造过程由不少步骤组成,因为会有许多不同的人平行地对同一源代码(产品的最微小的构件)作出不同的改变。例如,Excel3.0项目在其巅峰时期共有34个开发员积极地每天改变同一源代码——一个Excel可执行(。EXE)文件。勾画了微软开发员创造每日构造及保持每个人“同步化”的步骤。
每日构造过程——检查检查从源代码的中央主版本而来的源文件的私人拷贝。只有检查了这些私人拷贝的开发员才能改动这些源代码的私人拷贝。其他开发员可以检查同一源文件的不同的私人拷贝。
完成特性通过对源文件的私人拷贝的改变、增加或删减代码来完成特性。开发员使用一种交互式源代码编辑器工具来进行改变。代码完成可能耗费不到一天或多达数天的时间。
构造私人版本构造产品的一个私人版本,对其源文件的私人拷贝作出改变,以完成新特性。开发员会为每一个目标平台,如Windows和Macintosh构造一个产品的私人版本。这种构造通常一夜之间就完成了。
测试私人版本测试产品的私人版本以确保新的特性工作良好。
同步化代码改变把包含了用以完成新特性的改变的源文件的私人拷贝与源文件当前的中央主版本进行比较(这种比较的过程被称作同步化或改变同步化)。开发员使用一种“差异”工具来比较文件和自动确定差异。源文件的当前中央主版本可能已与开发员在步骤1检查时的源文件的私人拷贝有所不同,因为从那时起可能有某些已完成其他特性的其他开发员已改变和录入了他所检查的源文件的某些部分。开发员应该在下午晚些时候把他的源文件的私人拷贝与当前的源文件的主版本进行比较。然后他就可以肯定他是在使用最新的源代码的中央主版本。每天的某一特定时刻之后没有人可以再改变源代码的中央主版本。例如Excel源代码改变的最后时限是每天下午两点。如果Excel开发员在特定的某一天想要把他的代码同步化,就应该在当天下午两点以后进行。
融合代码的改变随时更新源文件的私人拷贝,以使它们不仅包括其他开发员对相同文件所作的改变,还包括开发员自己的改变(这种更新过程被称作“融合”)。资源库管理程序(SLM)自动完成这种融合。它还会提醒开发员文件之间存在的任何不一致性(称为“融合冲突”),而这需要开发员动手去解决。以Excel为例,同步化和融合通常要花5到20分钟的时间,这取决于改变的性质和程度。
构造私人版本通过代码改变的融合,将其他开发员所做的改变也融合到源文件中。对这样的源文件做一私人拷贝,使用此私人拷贝来构造产品的一个私人版本。开发员可能需要为每一个目标平台,如Windows和Macintosh构造一个版本。构造通常在一夜之间完成。
测试私人版本测试产品的私人版本,以确保开发员新完成的特性工作良好。开发员在完成步骤5、6和7后那天的上午进行这一特性的测试。
进行快速测试在绝大多数组里对开发员的产品私人版本进行一种高度自动化的被称作“快速测试”(其他名称包括“烟雾测试”)的测试。这是为了确保在添加了他的特性后产品的基本功能仍运行良好。快速测试并不直接测试他的新特性;它只是确保他的改变不会与产品的其他特性冲突,也不会间接地引起错误。开发员在完成步骤5、6和7后那天的上午进行快速测试。以Excel为例,进行快速测试通常需要大约30分钟。
记录如果通过了特性测试(步骤8)和快速测试(步骤9),开发员可以正式地把他的源文件的私人拷贝记录入源文件的中央主版本。记录过程的第一部分是再次运用“差异”工具把自己的源文件的私人拷贝与源文件当前的中央主版本同步化(就像他在步骤5所做的)。记录过程的第二部分是运用SLM工具解决私人拷贝与主文件之间的任何融合冲突(就像他在步骤6所做的)。记录过程的最后部分是完全更新源文件的主版本以使它们包含开发员用以完成他的特性的改变。其他开发员可能已在同一天的早些时候改写了同样的源文件;这可能发生在此开发员把他的改变记录入那些文件之前。这也就是为什么开发员除了在步骤5和步骤6中从事同步化和融合工作外,还需在此步骤中再次进行同步化和融合,并把它们作为记录过程的一部分。一天之中当开发员记录改变时,他必须密切注意同一天中晚些时候的其他记录,以确保它们不会间接影响到他在同一天的改变。如果确实存在相互影响,他也许需要撤回或“退出”他的一些改变,直到开发员们可以解决这两套改变之间的互斥性问题。每个项目都有一个每日记录的最终时限,这样开发员每天必须在一个确定的时间之前完成所有对源文件中央主版本的改变。例如如果一个Excel开发员在某一天希望记录他的改变,他就必须在那天下午2:00之前完成。他在完成步骤5、6和7之后的那天进行记录。这与步骤8和9是同一天。在Excel,记录约需5到60分钟,这取决于文件的数目。
生成每日构造每天在记录的最终时限(比如下午2:00)后,一个被指定为项目“构造主管”的开发员运用源文件的中央主版本生成一个完整的产品构造,由这一构造形成的新的内部产品版本被称为“每日构造”。这是在产品发展和不断提高功能的过程中拍的一张相对稳定的快照。不管有多少开发员在一天内记录他们的源代码改变,项目都必须在当天生成每日构造。构造主管管理构造过程,直到所有的代码编译成功构造结束。这也许要求他们工作至深夜甚至凌晨,这取决于完成构造所需的时间长度。构造完成之后,构造主管执行一系列自动测试。这些保证了产品的基本功能运作良好,构造基本稳定。然后构造主管把每日构造提供给所有项目人员,包括项目经理、开发员、测试员以及用户培训人员,供他们使用和评价。——每日构造过程有一些关键步骤。首先,为了开发某一产品的某一特性,开发员“检查”源代码的中央主版本的源文件私人拷贝。他通过改变他的源文件私人拷贝来完成他的特性。然后他把自己的私人拷贝的改变记录下来,把这些再返回到源代码的主版本。记录过程包括一个自动测试,以帮助确保对源文件的改变不致引起产品其他部分的错误。一个开发员通常每周至少两次把他的代码记录回主拷贝,但他也可以每天记录。
无论单个开发员多久向源代码记录一次他的改变,一个专门指定的被称为项目“构造主管”的开发员每天都要运用源代码的主版本生成产品的一个完整的构造。生成产品的一个构造的过程由执行一系列良动的被称为“构造计划”的命令组成。这创造了一个产品的新内部版本,还包括许多“编译”源代码的步骤。自动编译把产品的源代码翻译成一个或多个“可执行”文件(可执行文件可以直接在计算机上完成特定的操作,而不像那种也许只包括一些文档或一些数据的文件)。每天所构造的产品的新内部版本就是“每日构造”。乔恩·德·沃思,Excel的前任开发经理和Office的现任开发经理,曾经这样解释Excel组所遵循的每日构造过程背后的规则和逻辑:
思路是我们总希望所记录的代码拥有尽可能高的品质。为了做到这一点,我们建立了一些规则。第一条规则是:
如果你今天想做记录,在两点以前做。你所记录的代码必须被编译和链接。对于Excel5.0,它必须为Windows Excel,Macintosh Excel和日本版的Windows Excel这样做。我们还在大量Excel源代码的基础上做了一个叫作Graph的程序。这样Windows Graph和Macintosh Graph也必须编译和链接。Windows和Mac Excel必须通过一个快速测试宏,这种宏是用来测试那些经常会出问题的部分的。人们必须把他们的版本下载到他们的机器并启动宏,但这之后它就是自动进行的了,所以你可以在两点以前记录完毕。当我说到你必须能够编译和链接,我的意思是你必须在前一夜已经与项目同步化,解决了你所有的融合冲突,以及已能够从一个清洁的状态构造我所说的一切。很多人每天保持同步,但你的确只需在记录的前一天做这项工作。我确信我们几乎每个人都不止每两天做一次。
每日构造——保持小组之间协调的严格法则每日构造工作给项目小组提供了关于产品进展状况的迅速信息反馈。Windows NT的软件工程经理娄·帕雷罗里认为每日构造是痛苦的但是有用的:“每日构造工作是世界上最痛苦的事。但它也是最伟大的,因为你可以得到立即的信息反馈。”微软的法则是很少的,但是项目必须严格遵守经常构造的过程,因为这保证了开发产品中每天之间的稳定性,并把所有的开发活动组织到了一起。就像MS-DOS和Windows的前任测试经理戴夫·马里茨所评价的:“每天五点整必须有一张关于构造的快照,这绝对是一条严格的军事化的纪律。我坚信总是会有什么东西要出乱子。所以你永远应该每天拍一张快照,无论发生了什么,即使第二天是假期,你知道不会有人要用它。每个人所需要知道的是节奏并感觉到项目是受控制的。即使你知道从现在起两周后的构造也仍将是不稳定的。星期型的构造也还是引起剧烈的低谷或高潮的波动,但那并不重要。”
的开发经理爱德·弗莱斯认为每日构造过程对于那些想有效运作的开发小组而言是至关重要的。一个大的微软项目努力像一些小的自发的小组一样工作,即使是分组也不会有几个人——就像构造PC软件的早期。但是大的项目不再只是小的小组之和。由于对产品构件的需要,他们也非常依赖于许多其他的组和个人。即使一个特性小组里的单个开发员也需要利用其他人所改变的源代码来工作。弗莱斯描述了每日构造过程如何保证了小组之间的协作:“那确实非常重要。我们不可能使这样大的一个组没有每日构造过程,因为我们需要合作。我们努力像小的小组一样工作。但我们不是小的小组。我们需要别人所做的工作。我们也需要产品基本上在任何时候都能运行,否则它会干扰你的领域。你不能让从事绘图工作的人暂停打字工作,否则没有人能打字,那么他们就无法打字以进入他们所要从事的领域。”
每日构造过程使得许多项目成员可以像一个整体一样地工作,因为它提供了一种代码控制机制,可以几乎一直产生出产品的能工作的一个版本。为了追踪代码的改变以及发现冲突,微软使用一种内部开发的被称为SLM的工具作为源代码库管理程序(微软人充满情意地叫它作“Slime”)。微软项目在采用自动测试的同时也使用SLM来帮助保证所开发产品的持续性和稳定性。就像弗莱斯所概括的:“我们依赖于广度测试或快速测试来做同样的事。我们依赖于我们的源代码控制系统;当它不运行时我们就会有麻烦。它使不同的人可以改变相同的文件,并且会处理和协调冲突。在你记录之前,你必须先与已记录的版本相融合,而冲突的部分会在你的当地构造上立刻显示出来。然后你必须在记录之前解决这一冲突。”
保持与产品的每日“同步化”
我们已经强调,每日构造过程保证了所开发产品的基本功能在绝大多数时间里运行良好。为了确保这种高度稳定性,微软希望开发员们每天都同步化和融合(但不一定记录)他们的源文件。例如,弗莱斯相信,形成每天晚上离开办公室之前“同步化”的习惯十分重要:如果一个开发员耽搁了较长时间,同步化和融合中的问题就会增加。
开发员经常把自己检查过的源文件与其他开发员在调校后记录下的已完成的源文件进行同步化和融合。结果是开发员总拥有一个变化的最新版本。每日同步化和融合的步骤帮助协调不同开发员之间的源代码的改变,防止项目晚些时候的较大的有错误倾向的融合冲突发生。弗莱斯解释说:“我们依然认为每个人每天晚上同步化和每个人与项目始终保持同步化是重要的,这样他就可以了解其他人所做的改变如果你试图停下来等待在你回来与每个人同步化的时候,你将有可能遇到巨大的融合冲突所以我们努力做到几乎任何时候,所有的人都在同一套源代码上工作。”
事实上有很多原因可以解释为什么人们经常做记录。首先,一个开发员越少做记录,其他开发员就会越多地改变他们工作于其上的源文件。这样,较迟记录就意味着开发员在融合他的文件时可能面临较多的问题。其次,做记录创造了源文件的一个中央支持拷贝。这在开发员的机器出现技术故障或其他情况使得检索文件很困难时尤为有价值。第三,做记录保存了开发员所改变的所有文件以及它们之间差异的历史。这种“差异史”对于未来发生问题时的寻根溯源很有用处,正如弗莱斯所说的:“你可以看到是谁在什么时候改变了什么。”人们经常做记录只是为了利用这一工具的这一特性。
构造暂停开发员的一条重要规则是确保他们所记录的改变的编译正确。改变不能与其他特性冲突或引起产品的结构性不协调而导致自动构造过程的停止。
“暂停构造”对于那些需隔夜执行构造过程的大项目而言是特别严重的问题,因为直到第二天上午才会有人发现这一故障。小组因而失去了宝贵的时间。它必须修正引起构造失败的原因,重新开始构造,并且同时必须回到前一个构造进行测试。由于开发员们不能辨识最新变化的影响,他们也许会推迟他们的记录,而这阻滞了整个进程。
每天下午的中间,构造主管开始使用源代码的中央主版本进行产品的主要构造。他通常要等到构造成功之后才回家;所得到的版本即这一产品的每日构造。应用软件组一般把构造主管的职务交由下一个所记录的代码导致了构造无法成功的人来担任。Excel的测试经理马克·奥尔森解释道:“你不可以暂停构造,这是根本性的规则如果你在某一天阻止了构造,你就成为进行构造的人,直到有其他人又阻止了它。所以如果你把过程弄乱了,你就会受到惩罚。”
各组通常都对暂停构造的人施以惩罚。在应用软件组,由有罪的人来担任构造主管的职务。但在系统产品组,通常会有一个由2到4人组成的专门的构造小组;在这些组里开发员也许得付5美元或更多的罚金。微软人还喜欢讽刺一下暂停构造的开发员,经常给他们一顶特制的帽子戴,比如在Word组里给他戴一顶Word Perfect的帽子。娄·帕雷罗里回忆在Windows NT3.0工作时记录代码的仪式:
我们过去的习惯是用羊角。规则是:如果你暂停了构造,你就戴上羊角,然后当另一个人暂停了构造,你就把羊角给他。现在你只需付5块钱。某天有一个人做了一项涉及150个文件的巨大记录,然后他就把一张50美元的钞票贴在旁边,这是颇为幽默的。我告诉我的调试人员们,如果他们有一大堆错误要修改,还有一大堆记录要做他们可以一石二鸟。他们应该把一张百元钞票贴在板上。如果运行没有问题,他们可以把他们的钞票拿走;一旦出现问题,他们就丢了这笔钱。这样他们就会进行所有他们需要做的测试,因为要构造调试器尚需约40分钟的时间。
尽量缩短总的构造过程同步化、融合以及记录的过程对于开发员而言是相对非生产性的时间,因为他们使得机器负载相当沉重,而开发员在这段时间内不能从事任何其他基于计算机的活动。所以开发员会尽量缩短总的同步化、融合和记录过程。他们需要有效地使用他们的时间、迅速地完成改变——特别是在项目后期测试员需要快速修改错误的时候。在项目的后期,开发员经常做“一天通过”:改变代码经常是对错误的修改——并在改变发生的当天做完记录。乔恩·德·沃思谈到缩短总的构造过程和迅速完成错误修改的必要性:“其实人们并非总需要每天记录,因为他们所从事的特性领域并非“一天通过”。时间变得昂贵是在项目结束阶段你做“一天通过”的时候——而且实际上也没有其他什么会像“一天通过”的错误修改那么昂贵。你想要依靠那些能尽快修改错误的测试员,因为这种修改不可避免地阻滞了特性的其他领域。这是总的记录过程真正开始有作用的时刻一旦我们进入错误修改阶段,依靠测试员是很重要的。”
同步化、融合以及记录源文件所需要的时间与所改变的文件的数量和改变的程度严格成比例。在Excel和Word,每日构造所需的时间相对较少,通常不到一个小时。德·沃恩作出分类:“至于总过程所需要的时间,同步化约需5到20分钟,取决于你检查的文件的数量。编译是在夜间完成的,所以我把其成本记为零。下载和进行快速测试大约需要半个小时,这取决于你检查出了多少事。一个小时是很长的时间。”
微软在Office项目上没有采用完全一样的每日构造方式。这是因为开发员必须同步化、融合和记录的文件数增加了一倍以上,所以所需的时间也会增加一倍以上。更快的PC机和开发工具使得对于每日构造而言产品的规模不那么重要了。不过对于一个像Office这样的大项目而言,缩短总过程的办法之一就是不要那么经常地做记录。由于这可能损害到较经常做记录的好处,微软重新构造了Office的源文件,使得开发员完成其特性时只需改变几个文件即可。于是开发员无需对整个产品进行再构造,而只需再构造Office中与他的改变有关的部分。
除了减少开发员在添加特性时所需改变的文件的数量,Office组还建立了一种两阶段过程来进一步减少同步化、融合以及记录源文件所需要的时间。这一过程的第一阶段允许开发员把源文件的改变记录进被称为“石灰岩”的系统的试验台版本。石灰岩试验台系统的构造无需对整个Office产品的重新构造;这样,它既不需要对每一个单独的应用软件进行快速测试,也不需要对整个产品进行完全的“回归测试”(回归测试是用来确定以前工作的特性或功能是否仍正常工作)。Office组也每天构造石灰岩试验台版本,并进行其自身的自动回归测试。
在第二阶段,构造主管定期把文件的石灰岩试验台版本中的改变转移到Office源文件的主版本。他们在开发员完成特定的特性之后把石灰岩的改变转入Office版本。这通常至少一周一次,并且在里程碑版本之前完成。对于里程碑版本,他们把石灰岩上的所有源代码改变转移到Office。这一过程要求构造所有的构件应用软件(Word、Excel、Power Point、Access以及Mail)和执行所有的快速测试以及应用软件专用的和Office专用的自动测试。
在做记录之前公布的有些记录非常庞大,一次就包括800个改变的文件。这些改变可能只是构件名称的改变或构件之间附加数据的交换。许多更常见的较小的记录则包括对产品的复杂改变。于是NT组在每日构造过程中又多加了一步。在记录源文件之前,开发员走到项目布告栏前写下他要记录的文件名。他把这一意向先公布出来,这样其他人就可以作出相应的预期,因为他的改变可能会引起不协调或影响源代码的其他部分。过一会儿,开发员就会接到构造主管的电话,告诉他可以记录他的代码了。开发员做好记录后向Windows NT构造主管发一份E-mail,特别说明产品的哪些部分有赖于新记录的代码并需要重新构造。在记录之前加入的这一步骤使其他开发员有了额外的时间来准备应付这些改变,使构造过程的统一协调成为可能,并把构造暂停的机会降到最低。
大的系统产品如Windows NT的构造主管,通常有一个小组帮助他的工作。NT3.0的构造小组有四个人;领导这一项目的戴夫·卡特勒也经常加入。Windows NT还把不同的构造要求交错开,使之平行进展并连夜处理,以此来处理这些多样化的构造要求。娄·帕雷罗里解释道:
这是每日构造过程。你接到一个电话,说:“好,我们准备好了接受你的记录。”你做记录,然后你向一个叫“NT构造”的名字发E-mail,精确描述要使之进入源代码群所需的操作。这样,你把文件同步化,构造这些东西,到这一步你再同步化这一文件,构造它。现在到目录上链接它,你会得到一个新的核心,一个新的Windows可执行文件或不管什么东西。你要一个新的编辑器。而且,你当然是在一天之内做完这么多事的。你拥有一台一天可以编译8、10、12小时的机器所以很多时候我们所要做的是在晚上八点时说:“去记录你的所有东西。”有人会在全天过程中做记录,但是我们直到晚上才进行构造。我们回家时应该有五个窗口在运行不同构件的清洁构造,就像整个核心的一个清洁构造。可以说是我们改变了过程目标的规模;重新构造一个核心约需一个半小时的时间我们不喜欢在白天做这些事,凌晨两点时它会工作良好。
构造周期的频率微软根据项目的特别要求和成功完成一个构造所需的时间来决定构造周期频率。系统产品由于它们的规模及包含的文件数目和相互依赖性,一般会需要较长的构造时间。微软每天构造Excel、Word和Office的试验台版本;至少一周构造一次Office的完整版本。Windows3.1、Windows NT和MS-DOS在它们开发周期的早期每月和每周构造,然后在发布前的数月内每日构造。Windows NT在产品出品前大约12个月的1992年7月开始完全的每日构造。微软在Windows95的扩展测试阶段每周构造。一些其他公司也采用经常构造。比如曾是DEC公司VMS操作系统项目开发小组成员的娄·帕雷罗里说,他那个开发小组每周和每两周进行一次构造。现在IBM和AT&T的一些主要软件开发项目每两周或每月构造一次。大的操作系统或顾客软件项目在项目的绝大多数时间里没有定期构造,然后在项目后期的集成和系统测试阶段进行每两周或每月一次的构造2,而Borland(Turbo Pascal)至少一个小组是每日构造。曾经在Borland管理这一组的布兰德·斯尔文伯格认为他的前任公司在开发方法方面最像微软:
在Borland,产品和小组都要小得多,所以事情进行得极为迅速我们在常规基础上构造产品,Turbo Pascal几乎每日进行构造,虽然那里只有6个程序员,很容易保持同步。当他们做了一个构造,那就是它了。那一产品的β测试周期只需6个星期,因为领头的开发员是如此之棒。Borland的工作也是非常出色的。他们比微软小,更灵活一点,但组织得不够那么好。在最高层次上他们也许比其他人更像微软,但我还不知道有谁做得比微软好我不会看着任何其他开发组织说:“嘿,我希望我们可以做得像他们一样!”
下一项原则描述了每日构造过程如何帮助微软做到始终准备好推出一件产品。
原则二:永远拥有一种理论上可以推出的产品,并拥有针对每个主要平台和市场的版本从每日构造中产生的一个主要好处是项目总能拥有一件稳定,然而又是不断进化的、有一套灵活特性的产品。不断增进的特性开发、经常性的同步化以及持续的测试帮助迫使项目努力建立和保持产品的一个达到或接近顾客所需的质量水平的版本。当然,一个拥有广泛的新增特性的每日构造的可靠性是不确定的,但是理论上微软可以推出其三四个主要里程碑版本中的任何一个(就像每周构造中的大多数一样)给顾客。这种进化中的产品构造可随时出品的性质使得微软可以在产品开发周期相当晚的阶段还增、删特性。这使按时出品或改变特性以响应顾客的信息反馈或竞争压力都成为可能。项目也有很大的灵活性来决定何时发布其产品给顾客。在开发过程中,项目为其产品同时构造针对不同的平台(如Windows95、Windows NT和Macintosh)和不同市场(如美国、欧洲和亚洲)的不同版本。这些同时发展的版本使得微软可以在极短的时间内向不同的PC平台和市场推出产品。
了解你的位置渐进的里程碑方式和每日构造过程使得微软小组在开发产品中可以紧跟其进展。它们还使得总的产品开发过程更可见和可预期。所以,一个项目小组总能了解相对于完成它所想构造的产品特性和功能而言,它的位置何在。在完成了产品的开发阶段之后,微软项目也会在产品实际发送给顾客之前插入一段相对较短的稳定期。但这与常规的软件开发方式有所不同,常规方式通常把他们时间表的50%花费在完成开发后的最终测试阶段。麦克·梅普尔斯概括了里程碑如何帮助微软人评估他们是否做好了发布一件产品的准备:
旧的把“编写说明、编写代码、测试代码、维护代码”作为一个生命周期的概念极易误导你关于离完成还有多远的认识。你会看到大批的项目在完成最后的20%的阶段花费了80%的时间。所以我们发展了这样一个里程碑过程的思路,你努力找出比较困难的事去做,你100%地完成它们,然后你重新估价你的位置如果这套特性就是产品,你就做所有推出它的准备工作如果你做的是对的,则从你结束产品到你可以提供整个产品的时间非常短这确实使过程容易预测得多了。在过程较早的时候你就知道你是否已陷入困境你知道你的位置。
迅速发布一件比小组最初的设计有更多或更少特性的产品的能力是微软的一项极具竞争力的优势。不过,其他公司如Lotus和Borland也采用同样的原则。至少Borland曾用这一方式把一种新产品比计划提前推向市场,以此来与微软竞争(我们没有任何微软由于采用每日构造过程而比计划提前出品产品的例子,却有很多项目砍掉特性以保证按时出品或缩短延误期的例子)。如布兰德·斯尔文伯格所言,Borland是分多阶段开发Turbo Pascal的,并不得不至少把其出品提前了五个月以面对竞争性的微软产品Quick Pascal。Borland在Turbo Pascal的开发过程中一直使之保持高度的可出品性,这使它能够迅速变化并成功地与Quick Pascal竞争。斯尔文伯格从Borland的角度回忆了这场两大产品的竞争情况:
如果你总有一些正在开发的东西,你在竞争市场上的步伐就可以更为轻捷。如果你的竞争对手推出了你没有预料到的东西,你总还可以拿出你的中间产品并出品它。实际上,当我还在Borland的时候就有一起极好的例子。我们在开发Turbo Pascal,那确实是Borland的基础语言产品。我们有特许经营权,而公司声望中的很大部分是与Turbo Pascal连在一起的。我们听到传言说微软正准备推出一种叫作Quick Pascal的产品于是我们组织起这个战斗小组,说:“好吧,如果我是比尔,我会做什么来与Borland构造的超级语言产品竞争呢?”于是我对我所认为的Quick Pascal的样子做了说明,然后用这一说明来引导我认为Turbo Pascal的下一个版本应该是什么样子。
能分阶段开发Turbo Pascal真是太好了,因为我们本来计划下一版本要在九个月后才出来。但是我们听到Quick Pascal四个月后就完成了,我们需要到那时也有一些东西出来与他们抗衡,并且我们可以做得到。我的预测是对的,我对那件产品有什么功能的说明98%是正确的。我所在的位置足以抗衡他们自认为将令人震惊并将夺走Borland在Pascal方面的领导地位的产品发布。我们事实上推出了一种十分优秀的产品——而且毕竟,有多少人听说过Quick Pascal呢?不太多。我们赢了,因为我们能够永远处于有产品可推出的状态。
“培育”而不是设计软件微软现在完成特性是通过创造原型、由内部和外部用户进行测试以及调试这一个连续过程进行的,而不是在开始构造产品之前就试图创造出产品的完整的说明和细节的设计。项目在开发一个新的产品版本时也经常用新构件替换掉产品中旧的部分。开发员们经常把这种先初创单独的产品功能,再把它们系统地发展成完备的特性的实践过程称为“培育”软件(IBM的前成员弗雷德·布鲁克斯在他1987年关于软件开发的著名文章中也使用了这一说法)。娄·帕雷罗里这样描绘了项目如何初创和发展特性:“我相信这种理论,即你应该培育软件,而且如果你看一下应用软件组,你会发现他们正是这么做的。你得到一些开始工作的东西,然后,缓慢而毫不怀疑地发展它,找出它的弱点和优势。它能发展完善是因为人们会关心它并提出建议说:‘嘿,这东西要是能这样就太棒了。’此时你就‘培育’这一部分。或者,人们说:‘它糟透了’。”
微软的各组广泛采用这一重复初创的方式。例如帕雷罗里曾描述Windows3.0项目在最终发布产品前是如何数次重新编写主要的次级系统,如文件系统:
这是我确信无疑的东西。你希望尽快初创出原型,这样你就可以在深陷其中无法看清之前找出愚蠢的和错误的地方并修改它。有例为证:我们的文件系统人员曾经奢侈地做了四个文件系统,他们还更为奢侈地重做了其中两个。我们的这两个文件系统之所以被重写只是因为当我们在编写更多文件系统的时候我们发现了些做得不太好的事情。然后我们把这些概念综合进较早的文件系统。我们的串行端口驱动程序被编写了两次。对于整件事,我们只是回去并说“这是它的瓶颈。我们要重新编写它。”然后你并非连夜重写它。你逐步改进它,而当你完成时它就是全新的了。
短暂的代码半周期许多开发员指出小组应持续地重写软件,而不应过分精雕细刻代码的某一部分。戴夫·穆尔估计微软一个软件的“半周期”只有18个月,这意味着小组每18个月左右就改变或替代他们产品代码的50%。由于微软大约每12到24个月发布某种产品的一个新版本,这一半周期概念表明产品代码在每两次发布之间改变50%。实际上,克里斯·彼得斯指出微软并不太重视编写可共用代码,因为它会很快过时:“你可能花费了如此多的时间制造一种可共享的东西,然后看着它两年之后就过时了。在一个变化如此迅速而猛烈的世界上,几乎没有什么是要与人共享的。”
改变的速度,或者说代码的“翻腾”,在有些组内变得极端迅速。比如在Excel,爱德·弗莱斯声称开发员们至少一周一次,至少是部分地改变每一个源文件。他提醒道,频繁的代码改变使得要保持与源代码分离的、涉及细节的设计文档是很困难的:“Excel程序非常庞大,但是它改变得极快。一个星期内每个C文件的相当多部分都会被触及。如果你等一个星期的时间来同步化,整个项目中的几乎每个文件都将变得不同,因为有如此多的人在忙碌地做出不同的改变。有时他们所做的改变是巨大而全面的,会影响到很多文件。而这正是文档的问题。在此过程中取出一些东西,然后它就很快过时了。所以当我取出一些东西并说这就是关于如何做一些事的范例时,我总是很担心。”
要想改变特性,必须做到加倍的好久而久之,微软的项目学会了约束自己,不去进行产品上相对小的改变,因为那只给用户带来很小的改进,却要求大量的努力或可能导致不一致性。比如Windows应用软件的目的是为了让产品与Windows共同工作。这目的并非要求改变Windows本身——当然,除非改变可以带来较大的改进。
微软是在研制OS/2时得到这一教训的。就像克里斯·彼得斯回忆的:“OS/2是他们试图改变什么的尝试他们试图制造出仅改善10%但变动极大的东西,而没有人想要这10%的改进。我们有一条经验法则——如果你想保持一致性,那么在你把它变得不同之前,你得保证它有两倍的改进。”彼得斯又提及“还原按钮”的例子,这是在Word里首先出现的特性。Excel组开发了一种工作完全不同的“还原按钮”并在可用性实验室测试出它有10%的改进。这没有好到足够使Excel与Word使用不同的“还原按钮”,因为非常多的人要使用这两个程序;它也没有提供足够的改进,以使Word也采用Excel的格式。就像彼得斯所说的:“很明显,如果它好了很多,也许我们应该考虑它。但如果它只改进了一点,那么缺乏一致性也会成为问题的。”
重新编写代码的20%的成本保持产品随时可出品的另一个方面是投资于项目之间的代码重新编写(这类似于其他软件公司里的“维护”或产品“再工程”工作,虽然微软并无专门的组来专司此职)。作为一条非正式的原则,开发经理努力将他们的开发员的20%的时间分配于重做产品较弱的部分。如果他们在多个版本上持续地这样做,产品也将持续地提高;如果他们不这样做,则当开发员添加不同的特性以及用户要求产品的更多功能时总体质量会恶化。克里斯·彼得斯指出:“我们把这些东西称为20%的成本,它与重新编写或重新构造产品的某些部分有关。如果你不付这20%的成本,你的结局将会更糟。所以你应该努力找出最坏的部分。”通常,项目是用这部分时间重做程序的重要然而不是用户所容易看到的部分。这样的例子包括Excel中的保存名,Word中从256种字模转为4000种字模,重新编写程序的打印方式,或对特性的运行或互相影响的途径做更多的基础性改变。
微软人把这种工作视为“成本”,是从这样的角度考虑的:项目把开发员一周时间中的一天用于提高产品的基础性结构而不是添加新特性。开发员经常在他们发布完一件产品(在第4章里被称为“里程碑0”)之后到正式开始下一个产品版本的开发阶段之前的这段时期内使用这一时间。
同时构造产品的多个版本对于微软的竞争性策略而言还有一点也是十分重要的,即不仅对世界上不同的市场,也对不同的硬件平台同时准备好每种产品的不同版本。项目在资源分配上权衡,以期最大化不同产品版本之间用来进行调试、最终发送、服务于不同的硬件和操作系统平台以及服务于不同最终用户语言的可共享的代码的数量。这种共享使得不同项目可以同时开发和测试所有这些版本。比如Excel组构造了Excel4.0的20个不同版本,以适应五个不同的语言区——美语、德语、法语、西班牙语和远东语区(日语、汉语以及韩语),适应Windows和Macintosh操作平台,并且同时有产出(或出品)版本和“调试”版本(它包括了测试和发现错误的额外代码)服务于每一个语言/平台组合。(微软只向其顾客发送了其中的10个版本,因为调试版本是供项目在内部使用的。)为项目如何准备好推出这些不同的版本提供了很好的范例。在一个项目中,开发员把产品的语言专用部分(如错误信息的内容以及对话部分)分离成一个单独的文件。这种分离使得他们可以通过仅翻译一个单独文件的内容就把这些部分译成外语了;微软各组把这种翻译过程称为“本地化”。于是开发员们可以集中注意力于代码中与语言无关的在所有语言版本中都可共享的部分。马克·奥尔森对这种独立于语言的代码与产品中依赖于语言的部分的分离评论道:“美语版本是我们花时间最多的版本。我们产品中的所有语言专用的部分,就像所有被本地化或被翻译的UI(用户界面)一样,被组成了一个独立的DLL(动态链接库文件),我们称之为语言DLL。所以我们只需构造一个Excel。exe(可执行文件),然后为每种语言配一个DLL。法语版本的代码与美语版本是一样的,但是UI的特色被归入DLL了。”
对依赖于语言的本地化文件的翻译在开发过程相当靠后的阶段进行,这时用户界面已经稳定了。项目希望特性首先要稳定,这样在特性改变时他们无需翻译用户界面的内容。对本地化文件的真正的翻译其实是在项目马上要发布美语版本之前或刚刚发布完之后进行的。依赖于语言的本地化文件与独立于语言的代码的分离使得测试员可以通过主要集中测试一种国际产品的本地化部分而有效地利用他们的时间。奥尔森解释了这一过程是如何工作的:
那使我们可以分离产品的本地化,对翻译的测试和在对话、包含字符串的菜单以及排序顺序方面的本地化。我们最后再做那一类测试。而且我们在过程中所做的测试是为了确保类似国家设置这样的东西不会产生干扰,于是完成国家设置的代码成为了核心代码的一部分。在法语版本上我们也没做什么特别的;它也是同一代码基础的部分。所以对于每一特性,人们都必须了解改变代码页或在特性上改变国家设置的含义。对一些特性而言它一点儿也不起作用。但对于一个像格式数据这样的特性,你会看到货币格式或日期分隔符或名单分隔符,你就必须确保你是从正确的地方得到的信息。没有一成不变的代码。
项目为其产品系列创造了差不多同样数量的版本。Office构造了一种Win32(32位)版本和一、两种Macintosh版本(为Power Mac和更早的基于Motorola68000处理芯片族的Macintosh产品);他们为每一个平台构造了一个产出和一个调试版本,以及为每一种语言准备了不同的本地化文件。Office的远东版本最终会要求一个独立的构造而不仅仅是一个本地化文件,因为微软要采用单一代码标准(UNICODE标准)。单一代码对每个字符使用两个字节(这不同于现存的每个字符使用一个字节,然后以特别的扩展来完善它的方式),以对汉字字符提供广泛的支持。
向多个平台和市场发布产品同时构造一种产品的多个版本的真正好处在于微软可以在很短的时间内向不同的平台和市场推出各自的产品版本。公司的目的是对每一种主要产品在每一个主要的平台上和在每一种语言的市场上都拥有同时的出品(称为“同出品”);这意味着在约30天内发布完各种版本。在很短的时间间隔内发布不同的版本对于成功营销产品而言是至为重要的。当国外顾客听说在美国已有一种新版本上市,他们就会停止购买产品的旧版本,就像Macintosh的顾客,如果听说一种新的Windows版本问世了,他们就会停止购买旧版本的Excel和Word。
虽然目标是在30天内出品不同的版本,然而,微软在推出有些小语种版本时会慢一些。比如:Excel4.0首先在美语的Windows版本中出品,法语和德语Windows版本在30天后推出,但是西班牙语Windows版本在美语版本之后60天才推出。在美语Macintosh版本出品的Excel4.0比美语Windows版本晚大约20天,而国际Macintosh版本的推出也像Windows版本一样有30到60天的时滞。对于Windows95的目标是在美语版本之后三个月内推出汉语版本(Windows95代码也为中东版本和远东版本提供了基础)。这些交错的发布在很大程度上反映了逻辑上的延迟和测试,马克·奥尔森认为:“每件事都被铺开了,而这部分地是由于一些最后一刻的逻辑细节的需要。总有些事你没有考虑到。软件做好了充分的准备:文档做好了;装到磁盘上的‘只读’文件做好了;安装程序做完了;所有的示范文件和所有的我们出品的添加项最终也完成了。现在只需保持这份文件名单以确保它们是正确的,以及它们都会有机会被看到。”
在有些国际市场上的销售量并不太大,但是如果经理人员相信这些市场最终会变得可观,微软就会投资于这些外语版本。比如Word的希伯莱语版本所售出的拷贝相对很少。然而微软人希望在许多中东国家里有良好表现,他们也把中国视为一个巨大的目标市场。微软产品开发董事克里斯·威廉姆斯对这一投资策略进行描述:
中东组居于产品的业务单位之外是因为我们想分离出我们在那一地区的投资。如果我们把希伯莱语Word的开发置于Word组中,它的全部仅1200份的拷贝或它所能售出的量将使Word成员完全地忽略它或者也许他们会投入过度的努力。问题在于你无法判断向它投入了多少努力。所以我们把那些人专门分配到那个市场空间,他们的任务是在我们投资的基础上,为我们在那个市场空间占据一席之地而提供所需的产品。
现在,他们的知识产权法律令人震惊的糟糕,所以人们在90%的时间里是偷窃软件。但是我们知道世界的20%说阿拉伯语,迟早我们会在那个市场空间赚钱的。我们现在的策略是,花掉与所赚同样多的钱。我们有一个非常可观的投资,它几乎花掉了我们在那个邻近地区所获得的全部收入在日本和在远东的人们也在努力打开中国市场,这是我们在成本基础上投资的另一个重要领域。我们软件的拷贝正在涌入那个布场目标是当人们最终不得不购买软件时,他们已经知道了我们的软件,并且那是法律生效后他们所愿意去买的软件。我们基本上得到了市场份额。一旦我们开始从那项投资上获益,那将是惊人的。
原则三:在同一个开发场所使用一种共同的开发语言为了利于人们在组际和组内交流,微软人在西雅图郊外的公司总部从事他们的开发工作。他们还使用一种共同的“语言”——即他们使用一小套开发语言、惯例和工具。这种对如何创造产品的普遍共识有助于微软的个人交流和相对迅速有效地解决问题。
单一场所开发微软一直鼓励那种在微软总部完成所有主要开发的传统。这一规则只有极少数的例外。有一些海外适应项目(如Windows的日本版本,微软稍后也把它转移到了美国)以及某些应用软件产品和工具开发工作(如Power Point和Softimage)是在这一场所之外进行的,因为它们一开始就是微软的附加产品。但是正如第1章所提到的,比尔·盖茨特别希望组与组之间能面对面地解决问题和在技术上互相依赖。虽然E-mail有很多好处并已在微软广泛使用,单一场所开发使得项目中个人确实可以经常性地聚集在一起,并互相启发,发现新思路。经常性的和方便的交流可以防止问题的恶化。戴夫·马里茨强调了这些好处:
位置——单一场所开发——是如此如此的重要。Win[dows]3.1还没有离开日本的原因之一是因为它是在日本开发出来的。而那里有一种努力不让我们了解进展情况的日本式心态,因为那是他们所想要的工作方式,他们的经理也不让两个人在一起交谈。这就是它现在为什么要被带到这儿来的原因。在芝加哥的Windows95,我们能做到在走廊里蹓跶一趟就看到了进展情况。如果你不能随时看到别人的进展情况,你就遇到问题了。而这正是OS/2这个多场所开发项目(微软-IBM在80年代联合完成的项目)所遇到的问题之一。
微软经理还愿意投资于建设在微软总部工作的人们所需的基础设施。几乎每一个人——除了暑假实习生和偶尔的新雇员——都有自己单独的办公室。很多办公室都有窗户,因为建筑的平面图是“×”形的。产品单位还向项目个人慷慨提供他们需要的任何计算机和工具,包括一些不同的台式机器(通常包括一台Windows PC、一台Macintosh,还有一台任何其他类型的PC),已完全装载好软件和具有网络能力。
程序语言微软项目除了使用有限的C++和汇编语言外,主要使用C程序语言完成他们的产品。许多公司在商业性软件产品中使用C语言,因为它在不同的硬件平台上非常容易移植,且运行效率高并提供了很大的补充完善的灵活性。C语言本身独立于任何硬件平台——不像汇编语言,但是C程序所运用的内在系统函数(称为“库”),特别是用户界面库是平台专用的。所以用C语言来编写程序可能会有助于但并不能确保可移植性;微软在移植它的代码到许多平台(Windows,DOS,以及Macintosh)方面取得成功,是因为它还使用内部开发的工具来估价代码的可移植性,并对某些产品开发了在不同的平台上无需做巨大改变就可以再编译的“核心代码”。比如Excel4.0包含了可在Windows和Macintosh平台上共享的69%的核心代码。4(核心代码对于开发员而言有很多好处。但正如我们在第3章所提到的,在Word6.0中使用Windows-Macintosh核心代码的初次尝试,不仅带来了Macintosh上的“观感”的问题,还引起了一些其他现象。)虽然C语言有可移植、有效率及灵活的优势,软件开发区里的有些人却觉得它太原始了,并鼓励开发员使用一些不必要的技巧。比尔·盖茨也指出了C的劣势,但坚持微软开发员应用C来生成(或抽象)他们的代码:“像其他人一样,我们还没有找到一种不用C而用其他东西编写代码的方法。现在C是一种非常低层次的语言,我们在很多东西上使用C++。我们抽象出很多东西。”C++语言鼓励开发员编写高度组织的代码,抽象出一般概念而隐藏细节。微软在有选择的适当的项目上运用C++(例如Office和Windows NT的一些新构件),但C仍然占统治地位。
盖茨还强调,微软远在像C++这样一种语言支持他们之前就已经使用了目标抽象和复用的概念(称为“面向对象的程序设计”)。这证明了微软开发员的普遍技术水平。但他也承认微软没能促使这些方法上的细节成为其产品的优势:
很多人会说:“面向对象的程序设计会引起包含10项改进的因素”是的,它好处很多,因为工具会更多地支持它,而且它会带来更多的共享,甚至更多好处。但我们十年前所编写的代码就使用相同的抽象。我们广泛使用过程性的指针,这样你就可以拥有不同的数据结构而使用共同的子程序;这只是程序问题。公平地说,就对效率的理解以及清楚地知道自己分解东西的方式而言,C++会因它们在未来更适于被共享,而存在着被滥用的危险。很多人跌入这块希望之地的陷阱。这也就是为什么你也没有看到我们跳出来说“啊,顾客一定要来买我们的产品,因为它有针对性。”即使我们已编写了大量的C++代码,如果它运行更迅速,如果它有更多的特性,你会购买它。我们是否用心理专家来创造它并不重要。所以我们并不吹嘘我们在考虑到产品的最终用户时采用了或没有采用什么方法。
“匈牙利语”命名的惯例许多微软的应用软件项目以及一些系统项目,运用一种所谓的“匈牙利语”命名惯例,这个名称源自它的创始人查尔斯·西蒙尼的出生地匈牙利6(微软的匈牙利语与在欧洲使用的匈牙利语那种语言没有联系)。一种命名惯例是一个开发员给源代码中的程序和变量起一种明确的名称时所使用的类型或风格,它应该可以向那些试图调整或复用这一代码的开发员提供较高的可读性和可理解性。(软件开发中的一个普遍问题是源代码经常难被初创者以外的其他人理解。然而很多人还必须会读、理解并调整产品源代码,这样开发员们才有可能做到既会复用、改变或修改别人的代码,又能平行地工作。)匈牙利语命名法开始在微软使用是在1981年西蒙尼从Xerox PARC转到Multiplan工作时(见第3章)。他和其他人,如前任高级开发员董·克兰德,在他们80年代工作的应用软件组里极力促成它的使用。7匈牙利语命名惯例使得开发员们可以相对容易地阅读其他人的代码,只需源代码上最小量的注释。就乔恩·德·沃恩估计Excel产品代码中仅1%条是用来注释的,但由于使用了匈牙利语命名法,代码仍是非常容易被理解的:“如果你看到我们的源代码,你一定会注意到注释非常少。匈牙利语命名法赋予我们只要进入即可阅读的能力熟练掌握匈牙利语命名法会使你成为希腊学者一类的人物。你拿起一些东西就可以读它我记得微软对此最初的反映是:‘这是什么?这是疯狂!它不会带来任何不同。’但是它确实带来了。”
在匈牙利语命名法中,变量名由三部分组成:前缀、词根以及一个修饰语。8举例来说,变量名pch是指字符的指针;p是前缀,表示“指针”,ch是词根,表示“字符”。名称pch First是指字符数组的第一个元素的指针;First是修饰语,表示是第一个元素。虽然这个例子看起来可能很简单,但还有一些相当复杂的名称。变量名mpmipfn是下标为mi的表示菜单项的函数指针。开发员可以用这样的数组来分派命令。mp是词根,表示“数组”,mi是这一数组的下标类型,pfn是数组中所包含的元素的类型,它是函数(fn)的指针(p)。在匈牙利,人们先写姓再写名,就像微软开发员在他们的匈牙利语命名法里先写词根再写修饰语。
应用软件产品的绝大部分持续地广泛使用匈牙利语命名法。只有从Multiplan的早期版本而来的少量旧Excel代码不使用它。事实上几乎所有的Word代码都使用了匈牙利语命名法,除了一些暑期实习生所编写的代码(而其中一些人最终也修改了他们的代码并用匈牙利语命名法重新编写)。爱德·弗莱斯提到,有些人特别讲究要恰当地使用命名惯例:“这和任何语言一样。它从某种意义上讲也有方言土语,而有的人对他们的匈牙利语命名法使用极其严格,其他人可能没有那么严格。但是它已是我们文化的一部分,我甚至不会意识到我在用它。”
但是系统产品并不大量使用匈牙利语命名法。这也许是因为匈牙利语以及查尔斯·西蒙尼未曾成为系统分支文化的组成部分。娄·帕雷罗里赞同不要过于广泛地争论这种语言惯例的好处,但他在谈及他的小组中某一开发员的经历时也谈到了他的疑惑:
匈牙利语命名法会给他添麻烦,根本就没有帮他阅读代码,也没有使代码更易维护。他所看到的代码与其他代码有同样多的错误应用软件的人认为它很棒。我们并不反对人们使用匈牙利语命名法,但在我的Windows NT小组里没有人能在系统上很好地运用匈牙利语命名法。这是由于它并不像它看起来的那样有较好的描述性。描述性不是我们所关注的问题。我们的问题是同步化我认为即使由我来做一个应用软件,我也不会用匈牙利语命名法。我认为这是一个信仰问题,而在信仰问题上是不必争论的。我个人是在非匈牙利语世界里长大的。我们编写过庞大的系统,我们在NASA里所做的系统有两百万条代码,且都工作得很好。他们通过一种自上而下的方式和非常小的程序来做它,即使它是用FORTRAN编写的。
支持项目的通用工具微软人可以很好地交流还应归功于各项目使用一套通用工具来帮助管理和开发产品。许多工具像微软产品一样可供出售;还有一些特制的内部工具或商业产品的补充,由于数量少而不在市场上通行。一些内部工具也为微软提供了一种主要的竞争优势。例如,微软能迅速为不同的硬件平台和操作系统,如MIPSAltair、CP/M、AppleⅡ、DOS、Macintosh以及Windows开发出不同的语言和应用软件(见第3章)。比尔·盖茨强调了创造和使用内部工具的好处:
我们可以控制我们的工具,因为我们使用的是我们自己的工具在这一领域有很多值得称道的地方。为什么你能为Macintosh编写软件而别人不能?因为我们编写了我们自己的工具为什么公司处于第一的位置?因为我们编写了我们自己的工具。没有其他工具能做得这么好。这是一个巨大的竞争优势你只需做简单的事,就像我们为保护结构而在我们的编译器里处理内存指针一样。微软公司的优势在于我们是为真正的机器而编写。如果那是8088,我们为就8088编写。如果它是286保护模式,我们就为那种机器编写。如果它是AppleⅡ我们使我们的软件起作用。就规模和速度而言,没有任何一家软件公司能够接近我们的水平。而控制我们自己的工具正变得日益重要。即使在今天,我们还有工具来做这些工作。我们只是不把它们作为产品发布出去,因为那是我们的竞争优势。
微软的通用工具并无正式名单,而且任何名单都会随时间而改变并在不同产品组之间有所差异。然而还是有大多数小组使用我们可以在这里描述的类似的工具。程序经理经常使用Excel来管理产品说明,使用Excel或Microsoft Project来追踪日程信息。如果需要,他们使用Word来编写和打印一份项目文档。程序经理一般使用Visual Basic来初创产品特性的原型。(它的一个版本在应用软件产品,如Excel中被用作一种宏语言,这样用户可以根据自己的需要来改变产品。)开发员经常使用微软的商业化的Visual C++编译程序来从事C++的编写,微软也拥有它自己的C编译程序。有些项目有特制的内部代码调试器和分析器,帮助理解和评估代码。不同项目和产品的编辑器也不相同。比如Word使用一种被称为主编辑器的工具,它包含许多特制的宏,用于浏览代码、分辨变量用途以及函数调用的联系。开发员目前使用SLM进行源代码管理、同步化以及集成;微软出售它的名为δ的商业化版本(微软还从1994年买入的“一棵树”软件公司获得了Source Safe配置管理工具。有些项目用这个来代替SLM或δ。)过去很多开发员在进行开发工作时使用OS/2作为他们的操作系统平台,这主要是由于它具有多任务处理能力(使不同的进程可以同时执行)。但是现在很多项目改为使用Windows95或Windows NT。例如Office就是在Windows NT上构造的,即使稍后它要在Windows NT和Windows95上同时构造以促进自动测试。
测试员使用内部的RAID工具来记录和追踪产品错误的状态。要进行广泛的用户界面测试的产品,经常使用微软测试来自动地执行和再执行键盘动作。有一些项目使用一种内部测试案例管理程序(TCM),它把Visual Basic作为“前终端”而与一个结构化查询语言(SQL)数据库组合起来。这一工具组织了测试脚本,记录了执行测试的信息。
使用一套通用的工具和开发方法使人们在公司内部调动时能迅速适应产品的变化。盖茨认为这特别重要:
我们努力使各组使用工具不要差别太大,因为我们希望人们能够在组际间相当容易地调动。我们确实鼓励大量的组际调动,这样人们可以从事新鲜的工作并且可以了解别的组进度如何。今天人们所使用的工具已差别不大了。过去工具之间存在差别,系统有某些工具,应用软件有另一些工具,现在这一问题已基本被解决了。
虽然微软在工具开发和使用方面实力很强,但仍存在着提高的可能。一些开发员继续使用过时的工具只是因为他们已经熟悉了它们,而且他们宁愿把时间花在开发产品上。盖茨承认在有些类型的开发工具上投资不足:
我们有一些在工具上投资不够充分的情况,也确实有些时候外部工具更好一些很多年以前我们的调试器甚至不能达到正常水平而我们现在拥有了一些与动态信息反馈相联系的工具,你观察情况,然后返回并改变基于它的编译和存储组织。为什么我们不在多年以前就这样做呢?由此我忽然想到我们的源代码控制系统已相当破旧了,然而人们还在使用它。许多人还坚持用它,而它确实已很糟糕了。人们只是习惯了那些东西,即使是像我们这些拥有正确想法,诸如应该投资于工具、应该在工具投资上采用长远眼光的人也不例外。在资产负债表上它曾是一项巨大的资产。但是我们没有完全按照应采取的方式去执行它。所以现在它是一个巨大的负值。
但是即使是微软的相对不成熟的工具也有其优势。乔恩·德·沃恩指出他们的有些工具,比如编译器和调试器,与大学里学习计算机科学的学生所使用的相比是相当落后的。但他也提出,更复杂的工具可能不那么可靠。更进一步,他还发现微软的工具生成了就商业目的而言最好的代码:
我们的工具与学校里习惯使用的工具相比是相当原始的,信不信由你。事实上我们在Excel4.0才第一次进行源代码层次的调试。我深深感到这是微软工具在某种意义上滞后这一事实的写照。我们使用微软工具是因为与我们所评估的所有其他工具相比,它们生成了最好的代码。而对我们来说,关键是我们所出售的磁盘上的代码。这就是我们我行我素的原因。因此有很多像我这样的人,宁愿使用汇编语言调试器,因为它更快,也工作得更可靠。
在汇编代码层次上理解产品绝大多数微软开发员还有另一项共同的能力,像乔恩·德·沃恩一样,他们能够在一个非常深入的层次上理解他们的软件工作的状况。但是他们不会对所有用户都利用这一能力。特别而言,在Windows和Macintosh版本之间共享代码经常意味着微软开发员优先考虑Windows代码,而尽量不要为其他平台编写更深入的代码。但是一般而言,开发员如果能在尽可能深的层次上理解代码,这将是极其有用的:这样他们就可以弥补微软编译工具的局限,确保在给定的项目目标下出品效率最高的代码。德·沃恩强调,至少在Office和Excel组中,高级人员坚持开发员必须能够阅读汇编语言。经理不仅让开发员使用较高层次的语言,如C或C++来编写代码,而且还随机地在编译器上运行它。(编译器自动把高层次代码翻译成汇编语言代码,然后译成一种低层次的机器代码以使计算机可以直接理解。)比如Excel4.0中,语言的组成是75.7%的C代码,10.8%的汇编代码和13.5%的其他代码(如包含文件和标志文件)。10微软也曾在Word中用80%的C语言并将之在PC和Macintosh平台之间传送,另外20%的代码采用汇编语言。
增加汇编语言的使用可以加快执行速度。例如在Windows NT3.5上使用Excel和Word的新32位版本的自动宏测试的计算约需74秒的时间;而旧的包含更多汇编代码的16位版本只需70秒。11除了加快产品的速度,一些开发员还需要理解早期的MS-DOS代码,它是微软和IBM用Intel汇编语言编写的,其目的是为了减少内存要求和加快处理速度。
这种对产品的深层次的理解也包括掌握编译器生成不良代码的结构,然后编写代码避免或替代那些结构。德·沃恩估计他的开发员们对50%以上的源代码都阅读由编译器生成的汇编代码。他还抱怨微软不得不教会开发员在这一低层次上理解系统。大学里的计算机课程一般并不充分强调如何保证对存储和执行时间的有效利用,而这对于出售软件产品给个人计算机用户是至为重要的。德·沃恩详尽阐述了这些观点:
我们确实鼓励人们在非常低的层次上理解系统是如何工作的不幸的是你的工具常常不能充分地发挥作用,Windows和Mac系统也不是非常理想。所以你必须深入那一层次,找出它不能很好工作的原因。这是推动力之一。另一主要动力是我们对确保产生最好的产品代码兴趣浓厚。
当你看机器指令时,你就能知道代码的质量。你还会去学习——这是学习的唯一途径。你可以用C编写出好代码,但为了做到这一点,你必须了解你的编译器是如何生成代码的。如果你永远只在源代码层次的调试器里走来走去,你就找不到任何线索。
这是我们必须教给人们的态度。人们在学校里学习时,那没什么关系。但是当你制造一件人们想买的产品时,那绝对是有关系的思路是去掌握编译器所不擅长的构造。
我们可以要求编译器方面的专家来修改它,但是从某种角度上讲那是一件长期的事情,我们要求的是编写不存在这一问题的代码。
原则四:在构造产品的过程中持续测试产品微软里有不少原则指导着测试,而每一条原则都帮助小组在构造产品的过程中持续地测试它们。这种在开发同时测试的概念使微软与众不同:因为太多的软件开发员基本是在开发周期的尾声才强调产品测试的重要性,而这时要修改错误可能是特别困难和耗时的。而且,微软组现在是从一个相当广泛的角度来测试他们的产品的。
比如,项目创造了开发员可以每天运行,并且必须在他们把代码改变记录入源代码主版本之前运行的自动测试。测试系统通过使用产品的宏语言或模拟键盘敲击而自动执行。开发员们创造他们产品的“调试版本”,包括特别规则,不仅为了检查特别的条件,还可帮助发现和确定错误的位置,他们还实施代码复查以在阅读代码的过程中发现错误。可用性测试邀请“街边”的人们来到专门的实验室评价产品特性使用的方便程度。项目还把测试员与开发员配对(在微软,每一个开发员差不多都有一个测试员)。测试员通过阅读说明而在早期就开始准备测试方案以及测试策略;开发员在记录他们的代码之前向他们的“测试伙伴”提供他们代码的私人版本以供测试。
测试员然后使用多种方式。一种包括高度结构化的测试脚本(称为“基于方案的测试”)。另一种是“大猩猩测试”,测试员并不遵循任一脚本,而是试验他们所能想到的每一件东西来试图使产品出错。另外,微软项目还举办“臭虫聚会”,这是不同的人聚到一起共同寻找程序错误的集会。项目成员还使用他们所构造的产品,在内部分发初期的版本,以及向重要用户发出数以千计的β拷贝——所有这些努力都是为了在向市场发布一件产品之前找到错误。
为了尽可能快地测试并鼓励测试员在性能领域中获得专门知识,项目把一个典型的产品测试组重组成一些平行的小组,每个小组侧重于一个特定的特性领域。比如Excel5.0测试小组由45个负责测试Excel、MSGraph和Query Tool的测试员组成;他们还开发和测试内部的TCM测试案例管理工具。项目把这45个人组织成6个测试小组,每组有6到9个人。
当然,由于软件产品的复杂性、时间安排上的约束及人力的限制,测试不仅是一门科学,也是一门艺术。不幸的是,不能完全地测试一件产品也是确凿无疑的。微软的产品在不断地进步,但它们当然并非完美无缺,特别是在它们的早期版本中或当它们包括全新特性的时候。过去,微软也曾向市场推出过未经充分测试的产品;而随着新开发方式的采用和对决定何时一件产品准备好了推出的标准的日益精确化,这种现象已变得极为少见了。对那些在目标出品的特性未能完全工作或错误数量高于期望水平的产品,微软还会进行扩展β测试和延迟最终发布,特别是对系统产品。
(Windows95是这类延迟的一个较新的例子,它包括大量的新代码和复杂的新特性,诸如即插即用以及对应用软件程序的多任务处理。)快速测试促使每日构造过程有效运作的一个关键工具是一套被称作“快速测试”的高度自动化的测试。当一个开发员完成了一项新特性而尚未把源代码改变记录入源文件的主版本的时候,他构造产品的一个私人版本。然后他在其私人版本上执行快速测试。这确保了当他加入新特性后产品的基本功能仍保持良好(见步骤9)。
一件产品的宏语言除了为使用者提供按自己的要求改写和扩展产品功能的途径外,还为自动执行这些快速测试提供了有效机制。快速测试并非直接测试一项新特性;它只是确保一个开发员所作的改变没有与产品的其他特性发生冲突并间接引起错误。如果快速测试成功,开发员就可以记录他的代码改变了。微软把快速测试自动化了,这一点十分重要,因为许多开发员要执行很多遍这种测试,而且它会检查许多开发员也许会遗忘的特殊情况,然而开发员也不能完全依赖于快速测试,他们还需与他们领域里的其他专家交谈,正如爱德·弗莱斯所描述的:
从测试的角度看,大量的自动化是完成它的一条途径从开发员的角度看你做一些真正全新的事的情况是非常少见的。但愿情况是这样的:当你要把一些东西放进去时,你在代码中寻找一些类似的东西,然后你模拟它。于是你会看到所有这些特别的情况,如代码的有些部分说:
“如果录音机是开着的”,我会对录音机做些特别的事,然后你想“如果不做又会怎么样?”然后就有了一些不做的结果。这确实是捕捉那些对你不明显的情况的最好途径。另一件事就是我们非常依赖组内的专家。在不同的领域有不同的专家。只要人们交谈,就能产生许多素材。
测试“伙伴”和私人版本持续和同步测试的原则还意味着测试员必须在开发员编写代码的时候紧挨着他们工作。同时,为保证与代码保持一定的距离,微软的方法是测试必须保持与开发相独立的功能。如我们在第1章和第2章所描述的,测试员并不向开发组或程序经理报告。他们向测试经理汇报,再由测试经理向产品单位经理报告。但是项目通过在特定特性的小组里指定开发员和测试员一起工作而实现这种紧密的联系。而且测试员不仅测试新代码,还有机会在说明发展的过程中复查它以及在代码进化过程中平行地编写测试方案。克里斯·彼得斯强调这种开发员与测试员之间紧密联系的重要性:“在开发和测试方面能有独立然而是平等的组织,这使你可以避免出品坏东西。开发和测试需要深层次的融合,否则你会碰到这种糟糕的“检查员12”的现象,即开发员把东西扔过墙。我们现在所做的是测试员和开发员,至少是测试小组和特性小组,通常被配对,这样他们就会倾向于彼此一起非常紧密地共同工作。”
如我们已指出的,许多微软开发员对进展中的产品每天生成一个个人的每日构造,无论他们是否计划在那天记录代码改变。所产生的“私人版本”因而包含了开发员所作的尚未记录入源代码的主版本的最新版本的改变。开发员与他的指定的“测试伙伴”共享他的私人版本。这使得测试员可以估计特性的进展情况,也防止了许多错误进入源代码的主版本。马克·奥尔森指出这一技巧本身就创造了开发员和测试员之间的紧密的工作关系。它也为开发员提供了更多的时间来编写代码和改正问题:
私人版本测试的目标是在这一版本为小组中所有其他开发员所共享之前,在缺点进入真正的产出代码或进入主源代码库部分之前找到这些缺点。其他开发员不得不处理由于你记录的代码而引致的问题。所以关于私人版本测试有一些相当严格的规则这里只有开发员和测试员,他们形成一种工作关系根据他们所发展的信任的水平,他们可以是一个非常紧密的小组。开发员可以致力于编写代码和修改问题,测试员则关注于使这一特性迅速地稳定化。
开发员在记录他的代码之前要至少有一次使特性通过私人版本测试,而且经常是每天都这么做。爱德·弗莱斯相信非正式的私人版本测试缩短了测试员发现错误和开发员改正它之间的时间:“我们喜欢在从一个特性转向下一个之前作一次私人版本测试。这意味着编写一份测试版本文件和为你的特性专门做一份用于测试的特别版本。这是为了在保证事情的稳定的同时在不进入错误数据库的情况下先行测试,它使你能搜索到许多迅速产生的错误。这使我们比那种搜寻更正式的错误数据库的过程可以更快地调整方向。”
调试代码我们在关于多种版本的讨论中曾提及在产品的开发过程中,微软开发员为他们的产品创造“调试版本”。这来源于把不同的特定测试的表格,或“调试代码”,插入产品的源代码。当开发员做一个构造时,他把调试代码打开作为调试发送的一部分。而在产品发送时他把它关上,这样出品给顾客的版本就不会有额外的代码(开发员通常是通过一份在产品的用户界面上不可见的菜单来访问调试代码。比如Word的不可见的菜单紧挨着“帮助”选项)。
开发员必须把编写他们自己的调试代码作为他们日常工作的一部分。弗莱斯提到他们不得不计划开发调试帮助;否则人们不会做这些:“无论何时,当我们在计划产品的所有新特性的时候,我们努力弄清:‘那么,什么是我们还需要做的基础工作呢?让我们确保我们节约了一些时间并且为那项工作安排了一些时间,否则它是不会被做的。’我们努力为每一版本计划一些东西,一些会被加入整个程序的基础性的东西。我们同普通的特性一道计划它们。”调试代码主要为管理产品的执行和检查例外条件提供自动化的内置测试。这些帮助开发员改进存储器的使用和提高产品的执行速度。当使用调试代码时,开发员也可使用源代码或一个汇编调试器工具在程序执行中观察其内部的数据结构。弗莱斯描绘了Word中一个双重的、冗长的特性完成例子。小组用C语言完成了一套例程,又用本地计算机的低层次汇编语言完成了另一套(他们这样做是为了达到效率与可移植性之间的平衡):
我们在我们的应用软件产品中所做的某些肯定正确的事是我们给它们构造了大量的调试代码。在Word和Excel中都是如此。由于应用软件是如此巨大,一个好的框架里就必须包括你想构造来支持它和使得追踪问题较为容易的部分。一个例子是Word有C语言和本地语言两种版本。对其例程的所有汇编版本都有C版本。所以如果我们有一条用手动汇编语言编写的例程,我们也有C版本。而在我们的调试版本我们两种都包括;我们可以飞快地在使用C版本和使用汇编版本之间转换。而你拥有同一件东西的C版本和汇编版本的一个普遍问题是它们可能不同步。如果能保持它们同步会很有帮助。事实上你可以启动一种检查方式,依次运行这两种版本,然后确保结果是相同的。
确实有很多关于微软开发员使用调试代码提高他们构造的产品的质量或效率的例子。总体而言,即便在一件小产品里这些也可能达到成千上万条代码。特别重要的是存储器和数据结构检查、断言和工具化(工具版)。调试代码在产品构造向测试组的每周发布中也扮演了特别重要的角色。存储器和数据结构检查“存储器检查”说明了一个程序使用或分配的内存的每一个字节。比如,如果内存的一次自动检查失败,一个“MIF”(存储集成失败)的信息就会出现。一种类似的称为“数据结构检查”的技术使用特别的例程来保证产品中几乎每一个数据结构的一致性。这些保证程序已经正确地存储数据并可检索该数据。它们是一类运行于“空闲时间”的缺省的进程,在计算机处理器有空闲时间的时候自动地进行检查。
其他相似类型的调试代码模拟资源分配中的失误。例如Excel可以模拟几乎所有对操作系统功能的要求的失误。它通过给程序增加重复的“循环”和利用一个命令发送者“阻碍”大部分的Excel功能来进行模拟。这种循环给资源分配失误设置了域值,然后要求操作系统的一项特定功能。这项功能最终会在某一点上失败(由于所定义的域值)并返回一种失误状态。循环于是就完成了存储配置检查和数据结构一致性检查。如果没有问题,调试代码就提高失败域值到下一个最高水平并继续测试。如果操作系统功能失败了,则调试代码发现了一个问题并向开发员显示一个错误信息。其他调试代码的例子包括可移动内存检查、动态在“堆”中分配的内存再配置(称为“摇摆”)以及强迫的失败(“堆”是项目在执行过程中为保存数据而分配及回收的内存范围)。
断言调试代码的最普通的例子是“断言”。这些是代码中“如果——那么”测试,并且不管用户输入了什么数据,它都必须为真。比如一件银行应用软件产品可以在一次交易之后使用一个断言来测试一个支票账户的余额。它可以说:“如果余额大于等于0,则通过;否则失败。”这种断言的陈述应该永远是真实的。许多微软产品倾向于在程序中共享信息的“全局状态”。开发员经常使用断言来检查他们的关于程序中全局状态或传送进、出一个函数的特定“参数”的假设。如果人们在编写代码时没有充分考虑程序的全局状态或这些参数,就有可能产生很多错误。所以许多项目都有一条非正式的规则,即在开发员对存储在程序其他位置的数据作出假设的时候必须包括断言。乔恩·德·沃恩解释了这些断言会使追踪错误相对较快:“断言相当重要,因为错误的一项巨大来源是人们不了解全局状态或在对全局状态作了一些假设的情况下编写代码我们试图建立这样一种习惯,即如果你要对全局状态作出假设,你必须在你所编写的代码中断定它。对参数也一样如果你要对你函数的参数作什么假设,你必须断定它有了充分的断言,你就可以马上发现错误。”
工具化开发员还把调查加入代码之中,记录用户选择了哪些命令及源代码中哪些相应语句被执行。微软把包含这些调查的产品版本称为“工具版”。经用户同意并送回运行结果,这些版本可以成为内部测试、可用性测试以及区域β测试的有价值的补充。从工具版获得的数据使得开发员可以很容易地再现用户的行为,理解他们的问题,并且避免了不能重新模拟错误的情况。比如说,开发员重做出了Windows的Works2.0中所报告的错误的96.6%12,以及Windows NTPDK2中所报告错误的91.4%。
产品的工具版所能生成的数据的数量是极大的。事实上,比尔·盖茨就炫耀过微软的数据中包含用户所执行过的数以百万计的命令:“开发员和测试员也采用工具版。它是记录人们所使用的命令、所发生的错误以及所造成的结果的版本我们可以对事态的进展作大量的分析:人们为什么得到错误信息?什么是他们确实普遍使用的命令?什么是他们顺序使用的命令?也许我们可以在任务层次上捕捉到它?在工具化数据库里我们确实拥有用户所提供的数以百万计的命令。”
工具化还表明了源代码的哪些部分用户使用较多、哪些部分使用较少;软件开发员把这叫作“执行范围”。在整个执行范围内进行的测试可以帮助发现那些藏在源代码的较少使用的部分里的错误。德·沃恩观察到项目普遍在开发的后期使用自动化的测试(如一小套用一种宏语言编写的联结了很多操作的程序)来进行执行范围测试:“最终调试时间的一部分会被用来使开发员能帮助测试员进行覆盖更多代码的自动测试。”
每周测试版本在开发阶段,开发员每周一次把产品的每日构造提供给测试组。这种“每周测试版本”倾向于使用调试版本,因为这种版本中的方便之处如断言可以更快反映出问题。当项目接近出品日期时,开发员的每周版本会更多是产出版本(它不包括调试代码的数千条额外条码)而非调试版本。如弗莱斯所言,测试员还会编写他们自己的用以测试代码的例程并把它们加入程序:“我们有一些水平相当高的测试员。有一个基本上是程序员的组。他们通常十分清楚自己想要什么并问道:‘我们可不可以随意进入你们的代码并任意插入一些内置测试,这样我们可以更好地测试它?’当然我们会说:‘好。’”
代码复查在完成特性以前,微软组有限地做一些正式的、高强度的设计复查。因为今后的测试要求有能运行的版本,所以代码复查是为了在此之前就发现遗漏的代码或一致性问题。但是一般而言,微软组不为它们的产品做深入的、广泛的设计或代码复查,因为它们在开始编写代码之前对产品功能只做了大致的描绘(见第4章中关于概要说明方法的讨论)。更由于在项目过程中说明和代码也会有很大的改变,确定复查的时间也是困难的。
在短开发周期的压力下,微软人没有时间去研究是在最开始就多做一些复查和设计工作比较好,还是迟些时候再为这些问题设计更好的检查方式比较好。比如乔恩·德·沃恩会在要完成一项特性时试图考虑所有可能引起问题的偶然性,但在他刚刚开始设计这项特性时就不必这么做:“我们从某种意义上讲是放弃了认为我们可以预见所有情况的那种想法。这就是为什么我们为实际的特性完成的阶段留下了相当的灵活性我们也许可以通过进行更多的、正式化的设计复查来减少案例的数量,但是没有办法可以根除它们。所以问题就变为你怎样更快地找到它们。你是通过做测试来更快地找到它们呢?还是通过事前的思考?”
高级开发员倾向于通过复查新人或从事产品的一个新领域的人的代码来了解他们的素质以及帮助他们学习。通常,最好的专家——有时是小组长,有时是新人的指导者——会在新人记录之前复查他的代码。代码复查提供了一种有效的同伴对同伴的机制来学习所完成代码的技巧,就如德·沃恩所说的:“对于新人,我们复查他们的代码。当有人开始进行新任务时,也许其他人曾是这一领域的专家,那么他们在记录代码之前会让最好的专家复查他们的代码。他会告诉他们‘这做得不错’或者‘这的确是较好的做法。’思路是人们应当在实践中学习,而他们确实如此。指导者通过回答问题和做其他事务来帮助他们前进。然后,当他们认为他们已做好了代码时,他们能通过一次代码复查来获得技巧。”
测试版本和测试版本文档前面我们提到,项目定期向产品的测试组提供一份演进中产品的测试版本。测试员把测试版本中发现的错误记录在一个数据库里,以汇总各种出错情形。这个数据库是用RAID工具管理的,而经理们利用这些出错数据进行进程追踪。项目过程中,一个组通常每周构造一份测试版本,并把一份叫作TRD的测试版本文档附加到版本上。比如Excel5.0在每星期一晚上构造它的每周测试版本并在星期二提供给大家。
测试版本文档描绘了初次完成和能被移交去测试的每一项特性。开发员为他们的测试员生成这一文件,并且开发员一般是在测试他们的私人版本之前编写这一文件中他们有关的部分。文件的规模因特性的规模而异,它向测试员解释特性是如何工作的,以使他们可以对之进行有效地测试。测试员还把说明文件作为测试信息的另一来源。爱德·弗莱斯解释了测试版本文档所扮演的角色:“有些文件是我们要求程序员必须生成的,这关系到程序员如何与其他组协调行动。当他们创造了一项新特性并准备转向下一项时,就必须把它提交去测试并配合测试工作。他们需要提交一份测试版本文档,向测试员解释这一特性工作得如何,他们应该看什么东西、应该测试什么东西。”
可用性测试微软项目主要在产品开发阶段进行的一种完全不同类型的测试,是在一个正式的实验室环境里进行的对新特性的可用性测试(也可见第6章中我们对此的讨论)。这类测试尤其适用于应用软件产品中的用户界面和特性,因为它们都应强调使用的方便。微软拥有一个专门的由30到35个专家组成的班子,运用不同的可用性标准对几乎所有的应用软件产品和一些系统产品进行评估。由于这一测试是持续进行的,它代表了一项对时间的巨大投资,可在项目期间,它生成了价值难以估量的顾客信息反馈。比尔·盖茨这样评论Office的广泛的可用性测试过程:“我们得到基于所有这项工作而构造的原型,然后把它送入我们的可用性实验室,在这里我们观察那些真正坐在应用软件前面的人们。再一次我们获得信息反馈——我们用这个来一次又一次地改进。对于Office4.0我们在确定已把它做好并准备把它投入市场之前进行了超过8000个小时的可用性测试。”
其他公司的可用性实验室仅用来测试新的产品思路或者改进已有的产品。然而微软把这一思路推到了极致,把评估特性的可用性作为了开发过程的一个常规部分。当开发员认为他们已经或接近完成一项特性,程序经理就安排这一特性进入可用性实验室。有时如果开发员尚未完成特性,但希望在实验室里测试一下其原型,就为特性做一个特别版本或一个实体模型。一般而言,应用软件中每一项会影响用户的特性都要通过可用性实验室。每一项特性的小组长管理小组的日程安排,整个项目还有一份主日程安排;程序经理利用这一进度的信息来安排特性进入可用性实验室的时间。
在一个典型的可用性测试中,实验室人员招募一些外部人员,每十人组成一组来代表不同类型的用户。他们坐在特别的单人房间里。实验室人员要求他们用一种产品来完成一项特定的任务(如创造一份新文件或把金融数据组织成季度简报),然后可用性专家从后面的单向镜中观察和拍摄用户的行为。经常人们得不到什么指导,所以如果产品不容易理解,他们就难以完成任务。可用性测试员记录用户的问题和那些无需手册指导就可以成功完成每一项任务的用户的比例。测试员对一种特定产品每周做两次实验室测试,在每次的半天会议上评估大约三项特性。开发人员阅读有关实验室中遇到问题的报告,并与程序经理和可用性测试员共同讨论解决办法。
可用性实验室一般不从整体上支持产品计划,比如帮助项目对行为作出主要决策,那是第4章所讨论的基于行为制订计划技术的目的。实际上微软主要是利用这些实验室提供的几乎是立刻的信息反馈,以帮助开发员决定如何使新的或已有的特性更容易理解。比如开发员经常不得不为了某种特性而牺牲掉不少外观或行为。他们还必须解决用户界面设计上的细节问题,或在一小套有类似功能的特性之间选择最容易使用的那种。绝大多数开发员会亲自访问实验室去观察处于实际使用中的特性。如爱德·弗莱斯所解释的,这使他们掌握第一手资料:“程序员倾向于当他们个人的特性正在进行可用性测试时到可用性实验室去。他们观察人们对它的使用,看他们有什么样的问题,从中得到如何做得更好的想法。所以可用性测试也许比其他测试对我们所生产的东西有更大的影响。”
然而实验室也有一些局限:它们对新用户的考虑远超过对有经验用户的考虑,而且它们在定义清晰的问题时最拿手。不过可用性测试毕竟有重要的优势。它不仅使微软可以保证新增特性容易使用,还使微软可以不断添加新的有力的特性和避免不太有用的特性(有时被苛刻地称为“特性爬行”)。可用性实验室比β测试有优势,后者通常很晚才提供信息,并且经常缺乏细节。如克里斯·彼得斯所描述的:
在那种实验室里,可以得到你要改变特性所需要的数据,而且还可以在尚在开发一项特性时经常这样做,而β测试则不行。在推出可用性实验室之前我们从未有过这些数据。经常有人来抱怨有些东西工作得不那么好;但是你不会知道为什么我们得经常注意那些自认为理解了问题的地方。我们会做好一项特性,然后没有人会用它。每个人都会对我们极其极其的愤怒,因为它只能完成任务的80%,却就此停止无法继续。这是因为我们从顾客的要求中过滤了太多东西在人们所说的他们做了什么与他们实际的所做之间经常有非常大的差距——这是我们注意到的另一件事。
虽然在开发过程中开发员会进行可用性测试,程序经理和开发员还是用了更多的时间来再定义:如果他们在开发的早期就进行这项测试的话,特定的特性会是什么样子?会如何工作?但是很多微软产品有大量的图形式用户界面,测试这些产品经常导致改变发生在生命周期中较晚的开发员较好地理解了用户如何与产品相互作用的时候。麦克·梅普尔斯观察到:“我们并不在项目一开始的时候就说,编写好说明,然后照着说明构造它。我们知道事情会改变。任何时候如果你拥有一件非常交互式的用户界面驱动的产品,事情在最终有时并不像你所想象的那么好地工作你最好还是修改它。”
性能的基准测试和大猩猩测试系统产品应该具有极高水平的性能(执行功能时对用户的答复时间、对内存的使用和对磁盘的使用都达到尽可能的小)和高水平的可靠性(执行功能时失误的频率或数量降为0或非常小)。性能测试的一个例子是使用行业标准化的评价标准——称为基准测试——来评价产品的执行速度。不同的计算机行业组织在某些类型的操作或计算顺序的重要性上达成了共识,并把它们设置成标准的基准测试以促进不同产品之间的细节的比较。布兰德·斯尔文伯格概括了在系统产品上基准测试的使用:“我们拥有完整的、非常广泛的性能测试以测试一个非常宽泛的范围,不管它是中断的等待时间(为进程之间的低层的转换)还是首尾相接(为完整的用户命令)。可以在一个非常微观的层次上——像中断的答复时间之类——或在一个非常宏观的层次上,运行这一整套应用软件。那里有Babco测试或Winstone测试,或一些著名的应用软件测试组。我们运行整个测试组和性能组,然后看我们做得如何。我们追踪它们并衡量我们的工作配置如何(内存的配置),然后我们就有了需要达到的某些配置的目标。”
系统项目所采用的一类可靠性测试是大猩猩测试。这里,专职的测试员设置并执行自动化测试,目标是试图通过在极高要求的层次上运作特性来“打垮”产品。大猩猩测试员作为常规测试员的补充,在系统产品中就像在应用软件产品中一样被经理人员以一比一的比例指定给开发员。娄·帕雷罗里回顾了在Windows NT3.0上的测试:“测试员对开发员的比例可能是非常接近一比一也许你还会有两或三个测试员测试安全性或测试文件系统,或测试存储管理功能。然后,你有大猩猩测试员或系统功能测试员,他们的工作是找出会被打垮的东西。所以他们会编写一个命令程序在一夜之间安装和拆卸同一驱动器,检查它在第二天是否仍正常运行。”
系统产品可靠性测试的另一个例子是对低层次的应用编程接口(API)的测试,API为应用软件产品提供了一种直接访问操作系统函数的机制。操作系统API函数的可靠性对于一个应用软件的可靠性非常重要。许多应用软件产品的卖主都希望像微软这样的公司在发布一个操作系统之前就做好可提供给他们的API产品。那么微软测试员必须在项目的早期就使操作系统API函数稳定下来并且可靠,这样,应用软件卖主就可以使用API来“转换”其应用软件。Windows95的测试经理乔纳森·曼海姆描述了他的产品的测试过程:
测试操作系统是一个多阶段的过程,并取决于你当时在观察哪个方面,可能在不同的领域有不同重点。比如,在开发过程的早期,你非常强调直接测试API。你对内核以及应用软件程序进行低层的测试,这部分地是由于这样就可以在它们出现在操作系统的较高层次的函数中之前找到那些低层次错误,部分地是由于这样你可以使开发接口被很好测试并可以提供给ISV们(独立软件开发员),以使他们能够在一个操作系统发送之前就开始为它开发应用软件然后,在过程的较晚阶段,再转向较高层次的测试、用户界面测试、应用软件测试——所有这些你在过程早期不可能真正做好的事在过程中的这一点,我们基本上处于较高层次应用软件测试的阶段,直接的API测试已不能再找到更多错误了。系统已经被很好地调整,我们的绝大多数错误是通过运行应用软件并在用户界面上找到的。
内部使用和自产自用持续测试的另一种极其重要的形式是在公司内部尽可能快地让各项目使用新产品,或尚处于开发中的新产品。微软人非常信赖这一做法,有时把这种内部使用的行为戏称作“自食其果”(见第6章)。对产品的第一轮测试而言,内部使用还被证明是极其节约成本的机制。微软有成千上万的每天都要使用计算机的雇员,所以为什么不让他们把使用产品的最新版本作为他们工作的一部分并报告他们所发现的任何错误呢?当项目使用它自己的产品来构造它自己的作品时,微软人把这称为“自产自用”。这一做法与操作系统特别有关联。例如Windows95的开发员在他们的机器上运行最新的内部发行版本,所以他们是用Windows95来构造Windows95的更多特性的。
布兰德·斯尔文伯格解释道:自产自用使开发员可以与他们的最终用户保持紧密的接触。他把这与“交叉开发”相对比,后者是指项目使用与它的目标平台不同的平台来开发产品(比如,用OS/2来开发Windows95):“我们是完全自产自用的。我们确保开发员使用他们在构造的系统,而不是交叉开发。我认为,我所看到的由于交叉开发而引起的产品失败多于任何一种其他开发技术所引起的失败。你失去了与目标环境的完全接触;你失去了与用户所看到的东西的完全接触。”但是正如我们在第6章所进一步讨论的,自产自用和利用微软雇员进行内部测试的好处是有局限性的,因为他们并不总是最具代表性的顾客。即使是比尔·盖茨也承认这一点:“微软并不是所有微软产品的最典型用户。并且,如果你只在这些组内进行测试,你会遗漏某些东西。”
β测试由于产品可能用途的组合数量庞大,所以微软采用的用以在发布前评估产品的多种测试方式仍不足以发现所有的错误。不同的命令、数据输入和所依赖的系统结构可能引起几乎无限的组合。比如,作这样的假设:一件系统产品有100项特性,每项特性有1条正常的执行完成,以及所能生成的2条错误信息。这一产品在20个不同卖主的磁盘驱动器上运行。它将运行于4、8、16或32兆字节的内存中。产品要与15台不同卖主的打印机以及五台不同卖主的视频卡相联。它要支持100个最流行的应用软件,每个应用软件有50条用得最多的命令。仅仅是为了开始测试这一产品,测试员就必须建立一个可以支持超过90亿种用途方案组合的实验室(因为100×3×20×4×15×5×100×50大于90亿)。即使这样一个实验室是可行的,它也将是很不合算的——而且这份组合的清单也远非完全。
一种被称为β测试的大区域测试被用来测试那些具有多种可能的用途组合的产品。系统产品特别常用这种类型的测试,往往会花费6个月到一年,甚至更长的时间进行β测试。布兰德·斯尔文伯格回忆了微软如何开始认识到对系统产品进行大范围β测试的必要性:“另一件我们在系统产品组里做的真正的事是我们的β测试程序,它改变了软件开发的完成途径。我们从Windows3.0开始,然后从MS-DOS5以及Windows3.1认识到系统软件需要在如此多的机器和如此多迥异的配置上使用。在这一行业你根本没有办法预测人们会把什么插到PC里。我们不可能在内部测试所有情况下的所有配置。我们只能极度依赖非常宽泛的β测试。”
的β测试员有7000人,Windows3.1有15000人,而Windows NT3.0有75000人。1994年6月Windows95的β-1版本的β测试员的人数为15000人,而微软计划到1995年8月以前使这一领域内Windows95的β测试拷贝达到400000份。斯尔文伯格回忆起使他的微软同事们提高β测试的规模是多么的困难。但是由于大型测试的好处如此显而易见,微软扩大了这一实践,至少是在系统软件上:
过去以及现在的应用软件中仍是如此,一个β测试有300点、500点,也许1000点。对一个应用软件而言,有1000点的β测试程序就是一个庞大的不可思议的β了。但是我们意识到为了达到一定的覆盖,我们必须真正依赖于外部的β测试员在MS-DOS5,我们达到7500个β点。当我初到委员会时,最初的β测试计划是300点。而这时充满错误因而是巨大灾难的MS-DOS4刚刚出品。我向史蒂夫·鲍尔莫建议也许我们可以做到2000或3000点,然后这就像“什么,你疯了吗?——3000β点?没人曾这么做过。你到底在想什么?”而它工作得如此之好,对β有如此多的需求,最终我们结束时有7500点。结果是产品绝对的稳定。在Windows3.1上它也工作得非常好,我们最终达到15000点。结果产品非常非常稳定,而且与硬件和软件的兼容性都很好。
很多软件公司都使用β测试,当然由于其产品销售规模的庞大,微软动用的β测试人数远大于其他厂商。微软还在随时间流逝增加β测试员的数量。由于β测试的技术上的好处,这种情况并不奇怪。而且β测试还在用户中激发起对一种新产品的狂热,这有助于在行业的主要业者中获得支持——例如鼓励应用软件生产者提早开始为像Windows NT或Windows95这样的新操作系统开发产品。许多微软的批发商、二手商以及独立的软件开发员,包括像Lotus这样的公司,都是微软系统软件的常规β测试员。
在β测试中,参加者收到一份发布前的产品版本(称为β版本)并自己使用它。这些β测试员成为测试小组的非正式扩展人员。他们评估和测试β版本,然后向微软报告任何错误或问题。一般而言微软的β测试员没有报酬,也对软件不付或只付很少的费用;他们还需订一份合约,许诺不把产品的特性透露给竞争者。Windows NT的75000个β测试员无需签订合约,但必须为β版本支付一小笔费用(所以他们实际上是购买了软件)。Windows95的β测试员签订了合约,而只有后期的测试员才必须为软件付一小笔费用。
急切的PC用户是积极的β测试员,因为他们希望在购买之前试出最新的产品特性并对软件在他们的特定机器上工作的状况作出评价。然而微软努力使β测试员的范围更为广泛,以便能够充分代表众多的不同硬件、软件和网络的组合。测试员在微软完成其正式的产品版本之前提交错误报告。在最近这几年,β测试员使用电话拨号账户和Compu Serve上的E-mail来接收β版本,以及向微软发回他们发现的错误和建议。
微软项目指派专门人员来监控E-mail邮路交通并直接答复β测试员。斯尔文伯格指出:“我希望确保论坛是被妥善管理的,这样,每一个从事β测试的人员都在Compu Serve上的一个私人E-mail论坛中有一个账户。我们任命很多人去管理和回答问题,并且非常鼓励开发员在论坛里花些时间,这样他们既可以自己看到被提出来的问题是什么,又可以亲自回答问题。”斯尔文伯格自己也会花些时间来阅读从β测试员信息反馈回来的E-mail:“而且我自己在一件主要产品出品前的最后两三个月里,不管它是MS-DOS5、MS-DOS6或Windows3.1,差不多每天要在Compu Serve上花两到三个小时的时间,只是回答问题,只是获得对产品的一种感觉。你可以把它想象成系统其他部分的最新消息发布。我从我的测试经理和开发经理那儿得到错误报告。但是当我上到Compu Serve上,我能看到报告的第一手资料。所以如果它们之间没有联系,我就对我们该做点什么心中有数了。”
的数据显示了β测试可以非常有效。最终的β测试发现了产品发布前所找到的全部错误的22%。16Windows3.1所有的外部测试,包括最终β和所有较早的β版本,发现了全部错误的27%(“外部”这里指任何微软外部的人员,如β测试员,或微软内部非Windows3.1项目的人员)。17然而微软在对某些产品的测试上仍有麻烦。由于顾客用来与一个操作系统共同使用的不同的计算机品牌、应用软件以及外围设备(特别是打印机)的组合极为庞大,Windows NT和Windows95,甚至还有MS-DOS6(特别是数据压缩特性)要在发布前充分测试是极其困难的。结果就是微软项目还要依赖于多种方法进行测试。他们还持续地评估自己的工具和技术,以找到更好的测试方法。
比较不同测试技术的效果微软报告展现了项目用来发现错误的不同测试技术的效果。比如,对于Windows NT3.0项目,效果最好的技术是用途测试,它发现错误的比例最高(15.0%)。18用途测试通过模拟产品的日常使用来发现错误。应用程序编程接口(API)测试发现错误的比例次高(12.8%)。这种测试通过直接调用而执行一个特别的API函数。API测试对项目早期错误的发现贡献颇大,但其贡献会随项目进展而日渐削弱。应用软件测试,特别是16位应用软件(Apps-16)测试开始的时候对发现错误的效果不大,但它们的效率会在项目后期随着测试员更倚重运行应用软件而提高。有些测试技术是非常有用的,因为它们发现了相对较多的严重的错误;比如压力测试仅发现了全部错误的,但这些错误一般都是相当严重的。
Windows NT3.0中所采用的产品测试技术用途测试在对系统的每天使用中发现错误。API测试被编写来通过直接调用而运行一个特定的API函数的测试。其他专门测试不与其他任何标准相吻合的测试。Apps-16测试使用16位应用软件的测试。大猩猩测试人们通过在极高要求条件下用来使系统崩溃的测试。用户界面测试对用户界面的测试,包括命令行、文件管理器等等。压力测试压力测试方案,不包括也是压力测试的API测试,但是包括那些使用它们的方案。Apps-32测试使用本地的32位应用软件,无论大小。NT证明测试在定期地构造以试图达到可用质量层次的过程中发现的错误。小应用软件测对不同的系统小应用软件的测试,它们是很小的应用软试件,特别是控制面板小应用软件片。非NT测试NT测组外的测试,但仍局限于NT项目内部。包括开发员单位试测试,文档编写员整理内容,开发员发现代码被分割,以及程序经理确保产品被正确包装。“臭虫聚会”测通过举行发现错误的集会而进行的测试。试SGA测试用SGA(合成图形用户界面应用软件)工具发现的错误。
自动记录16位应用软件与操作系统之间的函数调用并转化它们,以使Win32位操作系统函数的执行可以用来代替Win16位程序的执行。
测试用RATS工具发现的错误。RATS是一种自动的测试“机器”(工具),它运行API测试并把结果存储在一个集中的地方。
非特定技术在错误报告表中未明确指出所使用的发现错误的测试技术的部分。
原则五:使用度量数据来决定重大的阶段成果和产品的发布与如何评估测试技术的效果相类似,微软项目通常使用被称为度量数据的经验测量和统计数据来理解、追踪和显示项目的进展情况。例如,程序经理和开发员必须决定是否要转向下一个里程碑,或按原定的发布日期还是延期发布一件产品。为了作出这些决策,他们不仅依赖于累积的关于错误(Bug)的趋势和它们的严重性的数据,还依赖于特别的度量数据。他们还依赖于用以解释这些数据的个人经验和判断。错误数据引导着里程碑的估计、完成、以及最终出品的决策,测试经理还必须同意开发员、程序经理和其他人所做的决策。依赖于度量和数据可以防止一个人——比如营销经理、开发组长、甚至是产品单位经理——在产品准备好之前就推出它。目的是为了避免或尽量减少那些曾在过去发生过的灾难,那时产品存在致命的缺陷,以至于微软不得不收回它们。
使用错误数据来评估里程碑的完成微软经理使用错误数据,一般被称为“臭虫数据”,做基础来判断一件产品何时达到完成一个主要里程碑所要求的质量标准。任何时候,当一个可执行程序没有达到说明书中提出的标准时,一只“臭虫”就出现了。错误的出现是软件开发过程的一个自然部分,开发员只是在发布产品的最终版本之前尽可能多地修改它们。错误数据与产品的整体质量之间有一种正向联系,也与开发和测试过程中产品质量提高的程度和速度正相关。项目人员把错误分为已找到(称为“打开的”)、已改正(称为“修改的”)、已解决(表示推迟的、重复的等等)、以及活跃的(表示错误已被找到,但还没有被修改或解决)。开发员修改测试员报告的错误,并遵循每日构造过程来把他们的改变综合进源代码的主版本。每日构造的使用非常鼓励开发员每天完成新的错误修改。
展示了关于错误情况的一张典型的柱状图。错误数据反映了Excel5.0(也包括Graph5.0)在第二里程碑的综合阶段所打开的、修改的、解决的以及活跃的错误的情况。图中显示了项目在它们接近一个里程碑的结束时会持续地测试产品、找到错误和修改错误。有斜线的柱反映新找到的错误,实心的柱反映已修改的错误。经理人员和开发员密切注意活跃错误数目的稳定减少,以之作为进展的标志。有实心小格的连线显示了活跃错误的数量的下降趋势,这一数量即使在所进行的测试仍在持续找到新错误时也是减少的(活跃错误的数量并不是一个简单的总和,因为它还可能包括或排除从其他来源获得的信息)。为了评估里程碑的实际完成情况,可能引起系统中止或破坏数据的活跃的错误的数量应该在测试仍在继续时就降为0(或非常接近0)。
专职发现和修改错误开发周期中在代码完成里程碑之后和所计划的出品日期之前的这一段,对于项目小组而言是一段紧张的时间。他们试图发现和改正尽可能多的缺陷,然后还要按计划的日期发布产品。爱德·弗莱斯回忆了Word6.0的最后阶段:
为了完成Word6.0,我们还有一大堆错误要修改。这永远是一段紧张的时间。你努力想在出品前减少错误的数量你有两个坚定不移的目标。首先,你有一大堆错误放在那儿,它们必须被修改——而你在这时除了接受现实努力修改外,已别无他法。然后,你还有一个希望实现的发布日期。你希望那两件事能一起做好,因为你所能做的全部就是:尽你所能,迅速地修改错误并希望进展顺利但它会好的,我们等它好了才发布它。我们很有希望能在想发布它的时候就发布它。
在项目过程中,测试员持续地向开发员报告他们所发现的错误。在代码完成里程碑之后,开发员专事修改错误的工作。有些开发员可能会修改掉相当大一部分错误,因为他们更有经验,或因为错误影响着他们个人所开发的代码,或只是因为他们是优秀的错误修改员。比如Excel4.0,37个开发员中的8个修改了全部错误的50%,而其中一个开发员修改了几乎10%的错误。20(但是并非所有的开发员在项目上都是专职的)。Windows NT3.0的开发员在1993年春天专职修改错误,而每处错误的修改平均需要改变三至五个文件。许多错误源于与Windows3.1以及已有应用软件的兼容性问题。娄·帕雷罗里作出如下描述:
我们在好的情况下一周可以修改1200个错误你知道,修改一处错误平均需要改变三到五个文件,但我们还有一些大个的你得记录对16个、20个也许30个文件的小改变现在坏消息来了。大约1000处错误被打开了。问题是,怎么可能有人开发出有如此多错误的软件?
这被证明是所谓的“兼容性”的问题。如果我们不是必须要与兼容,我们不会有这么多错误。所以如果你的打印结果看起来不完全像Windows,那么就有一处错误,因为不管怎样,Windows总是正确的。如果这个Wo W16位应用软件——我们称之为Wo W,因为它是Windows上的Windows——不能运行,那么就有一个错误。
特性和产品完成的基于度量的检查清单微软项目还编译基于度量的检查清单以帮助确定特性和产品的完成。比如,Excel有一份长达6页的关于评判标准的清单,名为“我做好了吗?”,它把完成的标准分为26项内容,如菜单命令、打印、与操作系统的互联,以及应用软件的互操作性。一个程序经理或一个开发员,或一个测试员,利用这份检查清单来帮助评价一项特性是否完成了。另一份基于度量的检查清单帮助确定一件产品是否已准备好发布给顾客。概括了Excel5.0的准备出品的标准的检查清单。这份检查清单列举了诸如不同的测试类型都必须100%地完成,以及最优先考虑的错误必须被改正和至少再测试两次(这被称为回归测试)。还有,发现错误的速度Excel/Graph5.0第二里程碑的错误数据和每日构造“我做完了吗?”检查清单的范例部分——与操作系统的互联:
·激活/不能激活(用键盘和鼠标)?
·非标准的控制面板窗口的色彩?
·当应用软件运行时是否出现色带?
·系统的个单探测器和复探测器?
·操作系统文件名长度的约束?
·的桌面附件?
·无色彩的?
·上的双显示器?
第6章讨论微软如何建立一个组织来向它自己和向它的顾客学习。人们不仅思考过去的错误,还学会了如何把顾客的信息反馈直接联入开发过程以及在不同的产品组之间共享更多的构件。