第一章 焦油坑
并不是所有努力都是成功的。大项目的管理问题和小项目的管理问题区别很大-关键需要维持产品自身的概念完整性。我们想解决问题,就必须试图先去了解问题-任何一个单独的问题都能解决,但问题互相纠缠和累积在一起的时候,团队的行动会越来越慢。
对问题的麻烦程度,每个人似乎都会感到惊讶,并且很难看清问题的本质。编程系统产品的演进:
image.png
左上部分程序。它本身是完整的,可以由作者在所开发的系统平台上运行。它通常是车库中产出的产品,以及作为单个程序员生产率的评估标准。有两种途径可以使程序转变为更有用的,但是成本更高的产物[向右和向下]。
水平边界以下,程序转变成编程产品。这是可以被任何人运行、测试、修复和扩展的程序,可以在多种操作系统平台上运行,供多套数据使用。要成为通用的编程产品,程序必须按照普遍认可的风格来编写,特别是输入的范围和形式必须广泛地适用于所有可以合理使用的基本算法。接着,对程序进行彻底测试,确保它的稳定性和可靠性,使其值得信赖。这就意味着必须准备、运行和记录详尽的测试用例库,用来检查输入的边界和范围。此外,要将程序提升为程序产品,还需要有完备的文档,每个人都可以加以使用、修复和扩展。经验数据表明,相同功能的编程产品的成本,至少是经过测试程序的3倍。
垂直边界右边,程序转变为编程系统中的一个构建单元。它是在功能上能互相协作、具有规范的格式、可以进行交互的程序集合,并可以用来组装和搭建整个系统。要成为编程系统构件,程序必须按照一定的要求编制,使输入和输出在语法语义与精确定义的接口一致。同时程序符合预先定义的资源限制-内存空间、输入输出设备、计算机时间。最后,程序必须同其他系统构建单元的接口一道,以任何能想象到的组合进行测试-由于测试用例会随着组合不断增加,所以测试范围必须广泛。因为一些意向不到的交互会产生许多不易觉察的bug,测试工作量将会非常耗时,相同功能的编程系统构件成本至少是独立程序的3倍。
右下部分代表编程系统产品,其成本高达9倍。然而,只有它才是真正有用的产品,是大多数系统开发的目标。
编程的乐趣:快乐是一种创建事物的纯粹快乐、快乐来自开发对他人有用的东西、内心深处,我们期望我们的劳动成果能够被他人使用,并能对他们有所帮助、快乐来自于整个过程体现出一股强大的魅力-零件一直在一起精妙运行、持续学习的快乐,非重复性特性、在易于驾驭的介质上工作。编程的快乐在于它不仅满足了我们内心深处进行创造的渴望,而且还唤醒了每个人内心的情感。
职业的苦恼[过程并不全都是快乐-事先了解一些编程固有的苦恼,它真正出现时才能更加坦然地面对]:苦恼来自追求完美/苦恼来自由他人来设定目标、供给资源和提供信息[个人权威和他所承担的责任是不匹配的],对他人依赖是非常痛苦的事情[他人的程序设计得并不合理、实现拙劣、发布不完整-没有源代码或测试用例、或者文档记录得很糟],需要花时间去研究和修改/概念性设计是有趣的,但寻找琐碎的bug却只有一项重复性的活动[伴随创造性活动,往往是枯燥沉闷的时间和艰苦的劳动],调试和差错往往是线性收敛的,寻找最后一个错误比第一个错误将花费更多的时间/产品即将做完却已显得过时[更好的构思、替代方案]。诚然,产品开发所基于的技术在不断进步,一旦设计被冻结在概念上就已经开始陈旧了。不过实际产品需要一步步按阶段实现-实现落后与否的判断应根据其他已有的系统,而不是未实现的概念。因此,我们所面临的挑战和任务是在实际进度和有效的资源范围,寻找解决实际问题的切实可行方案。
第二章 人月神话
缺乏合理的进度安排是造成项目滞后最主要原因,它比其他所有因素加起来影响还要大。导致原因:对估算技术缺乏有效的研究,基于不真实的假设、采用估算技术隐含地假设人和月可以互换,错误地将进度与工作量相互混淆、对自己估算缺乏信心,软件经理通常不会有耐心持续地估算这项工作、对进度缺乏跟踪和监督-需要常规的追踪技术和常规监督程序、意识到进度偏移,下意识的反应是增加人力。
乐观主义:所有的编程人员都是乐观主义者。“这次它肯定会运行”“我刚刚找出最后一个错误”。
第一个错误假设:一切都将运作良好,每一项任务所花费它所“应该”花费的时间。《创造者的思想》将创造性活动分为三个阶段:构思、实现和交流。只有在实现过程中,才能发现我们构思的不完整性和不一致性。然而大型的编程工作,或多或少包含了很多任务,某些任务还具有前后的次序,从而一切正常的概率变得非常小,甚至接近于零。
第二个谬误思考方式:在估计和进度安排中使用的工作量单位人月-人月作为衡量一项工作的规模是一个危险和带有欺骗性的神话,它暗示人员数量和时间是可以互相替换的。其基于的假设是:某个任务可以分解给参与人员,并且他们之间不需要互相的交流。当任务由于次序上的限制不能分解时,人手的增加对进度没有帮助。对于可以分解,但子任务之间需要互相沟通和交流的任务,必须在计划工作中考虑沟通的工作量(沟通所增加由两个部分组成:培训和互相的交流-每个人员需要进行技术、项目目标、总体策略以及工作计划的培训。这种培训不能分解,该部分增加的工作量随人员的数量呈线性变化+沟通工作量可能延长了而不是缩短了时间进度)。
单元测试和系统测试受顺序限制,需要的时间依赖于所遇到的错误、缺陷的数量及其难以捕捉的程度,理论上缺陷数为零-实际上由于乐观主义,通常实际出现的缺陷比例比预料的要多很多。因此,系统测试进度的安全常常是编程中最不合理的部分,作者的经验法则:1/3计划、1/6编码、1/4构件测试和早期系统测试、1/4系统测试,所有的构件已完成。与传统的进度安全方法不同(很少有项目允许为测试分配一半的时间,大多数项目的测试实际上是花费进度一半的时间):分配给计划的时间比平常的多。这样只能勉强产生详细和稳定的计划规格说明,并不足以容纳对全新技术的研究和探索。
对所完成代码的调试和测试,投入近一半的时间,比平常的安排多得多。容易估计的部分,如编码,仅仅分配了1/6的时间。
许多项目在系统测试之前还能保持进度。或者说,除了系统测试,进度基本能保证。特别需要指出的是,不为系统测试安排足够的时间简直就是一场灾难-因为延迟发生在项目快完成的时候,此时此刻的延迟具有不寻常的、严重的财务和心理上的反应。早期规划的系统测试时间是必要的。更为严重的是,当软件用来支持其他商业活动(硬件运送、新设备操作等)时,在即将发布时出现延误,所付出的二次商业代价是非常昂贵的。实际上,上述的二次成本远远高于其他开销。
为满足顾客期望日期而造成的不合理进度安排,在软件领域中却比其他任何工程领域要普遍得多。而且,非阶段化方法的采用,少得可怜的数据支持,加上完全借助软件经理的直觉,这样的方式很难生产出有力的、看似可靠的和规避风险的估计-需要开发并推行生产率图表、缺陷图表、估算规则等,从共享数据中获益;或者项目经理坚持估算,凭借经验和直觉总比期望结果强得多。
软件落后于进度通常做法:加人(考虑培训时间,不考虑任务的重新划分和额外的系统测试)、重新安排进度、削减任务等。向进度落后的项目增加人手,只会使进度更加落后。项目的时间依赖于顺序上的限制,人员的最大数量依赖于独立子任务的数量-可以推算出进度表,人员安排少,花费时间也较长(唯一的风险是产品可能会过时)。总之,在众多的项目中,缺乏合理的进度安排是造成项目滞后的主要原因,它比其它所有因素加起来的影响还要大。
第三章 外科手术队伍
需要协作沟通的人员数量影响着开发成本,因为成本的主要组成部分是互相的沟通和交流,以及更正沟通不当所引起的不良结果-也暗示系统应该由尽可能少的人员来开发。最好的和最差的表现生产率可能达到10:1,一拥而上的开发方法是高成本、速度缓慢、低效的,开发出的是无法在概念上进行集成的产品。但小型、精干队伍概念上的问题:对于真正意义上的大型系统,它太慢了。对于效率和概念的完整性来说,最好由干练的人员来设计和开发;对于大型系统,则需要大量的人手,以使产品能在时间上满足要求。
如何调和这两方面的矛盾?建议大型项目的每一个部分由一个团队解决,类似外科手术方式组建。同每个成员截取问题某个部分的做法相反,由一个人来完成问题的分解,其他人给予他所需要的支持,以提高效率和生产力-很少人被包含在设计和开发中,其他许多人来进行工作的支持。如何运作(十个人有七个专业人士在解决问题,而系统是一个人或者最多两个人思考的产物,客观上达到概念的一致性,不一致则由外科医生单方面解决),外科医生和副手都了解所有的设计和全部的代码:
外科医生,首席程序员。他亲自定义功能和性能技术说明书,设计程序,编制源代码,测试以及书写技术文档。首席程序员需要极高的天分、十年的经验和应用数学、业务数据处理或其他方面的大量系统知识和应用知识。
副手。他是外科医生的后备,能完成任何一部分工作,但相对具有的经验减少。他的主要作用是作为设计的思考者、讨论者和评估人员。外科医生试图和他沟通设计,但不受他建议的限制。副手经常在于其他团队讨论有关功能和接口问题时代表自己的小组。他需要详细了解所有的代码,研究设计策略的备选方案。显然,他充当外科医生的保险机制。他甚至可能编制代码,但对代码的任何部分,不承担具体的开发职责。
管理员。外科医生是老板,必须在人员、薪酬、办公空间等方面具有决定权,但他决不能在这些事务上浪费时间。需要一个控制财务、人员、工作地点和办公设备的专业管理人员,该管理员充当与组织中其他管理机构的接口。建议仅在项目具有法律、合同、报表和财务的需求时,管理者才有全职责任。否则,一个管理员可以为两个团队服务。
编辑。外科医生负责文档的生成。无论对外部描述和内部描述。而编辑根据外科医生的草稿或口述,进行分析和重新组织,提供各种参考信息和书目,对多个版本进行维护,并监督文档生成的机制。
两个文秘。管理员和编辑每个人需要一个文秘。管理员的文秘负责项目的协作一致和非产品文件。
程序职员。他负责维护编程产品库所有团队的技术记录。该职员接受文秘性质的培训,承担机器码文件和可读文件的相关管理责任。记录或标识计算机输入汇集到该职员,由他进行归档和编制索引。从个人艺术到公共实践Mills-它向所有团队成员展现了所有计算机的运行和产物,并将所有的程序和数据看作是团队的所有物。程序职员可以对那些经常被忽视的杂事进行系统整理,确保他们的质量,并强化团队最有价值的财富-工作产品。
工具维护人员,保证所有基本服务的可靠性,以及承担团队成员所需要特殊工具的构建、维护和升级责任。即使已经拥有非常卓越的、可靠的集中式服务,每个团队仍然需要自己的工具维护人员。工具维护人员常常需要开发一些实用程序、编制具有目录的函数库以及宏库。
测试人员。外科医生需要大量合适的测试用例,用他来对编写工作片段,以及整个工作进行测试。
语言专家。一两个乐于掌握复杂编程语言的人。这些专家非常有帮助,很快大家会向他咨询。外科医生主要是系统设计者以及考虑系统的整体表现。语言专家则寻找一种简洁的、有效的使用语言的方法来解决复杂、棘手问题,他们通常需要对技术进行一些研究。通常一个语言专家可以为两到三个外科医生服务。
团队的扩建:决定设计的人员是原来的1/7或更少,外科医生思路。对于协调的问题,还是需要使用分解的技术。可以认为整个系统必须具备概念上的完整性,要有一个系统结构师从上至下地进行所有的设计。要使工作易于管理,必须清晰地划分体系结构设计和实现之间的界限,系统架构师必须一丝不苟地专注于体系结构。
第四章 贵族专制、民主政治和系统设计
作者主张概念完整性应该是系统设计最重要的考虑因素,为反映一系列连贯的设计思路,宁可省略一些不规则的特性和改进,也不提倡独立和无法整合的系统,哪怕它们其实包含着许多很好的设计。
Q:如何获得概念的完整性
A:易用性,功能与理解上复杂程度的比值才是系统设计的最终测试标准。单是功能本身或者简洁都无法成为一个好的设计评判标准。很多情况下,功能而非简洁总是被用来衡量设计人员工作中出色程度。但是,一旦使用易用性作为衡量标准,单独的功能和简洁都是不均衡的,都只达到了真正目标的一半。对于给定级别的功能,能用最简洁和最直接的方式来指明事情的系统是最好的。简洁和直白来自概念的完整性。每个部分必须反映相同的原理需求的一致平衡。在语法上,每个部分应使用相同的技巧;在语义上,应具有同样的相似性。因此,易用性实际上需要设计的一致性和概念上的完整性。
贵族专制统治和民主政治
概念的完整性要求设计必须由一个人,或者非常少数互有默契的人员来实现。进度要求很多人员来开发系统,解决方法包括:仔细地对设计方法和具体实现进行分工,或使用主治医生的组建编程开发团队。
系统的体系结构指的是完整和详细的用户接口说明。对于计算机,它是编程手册;对于编译器,它是语言手册;对于控制程序,它是语言和函数调用手册;对于整个系统,它是用户要完成自己全部工作所需要参考的手册集合。系统的结构师是用户的代言人。结构师的工作,是运用专业技术知识来支持用户的真正利益,而不是维护销售人员、制作者所鼓吹的利益。体系结构同实现必须仔细地区分开来。系统的概念完整性决定了其使用的容易程度-不能与系统基本概念进行整合的良好想法和特色,最好放在一边,不予考虑。如果出现了很多非常重要但不兼容的构想,就应该抛弃原来的设计,对不同基本概念进行合并,在合并的系统上重新开始。对于贵族专制统治的问题,必须回答是与否。就只能存在少数的结构师而言,答案是肯定的,他们的工作产物的生命周期比那些实现人员的产物要长,并且结构师一直处在解决用户问题,实现用户利益的核心地位。如果得到系统概念的完整性,那么必须有人控制这些概念,这实际上是一种无需任何歉意的贵族专制统治。
外部技术说明的编制工作并不比具体设计实现更富有创造性,它只是一项性质不同的创造工作而已。在给定体系结构下的设计实现,同样需要同编制技术说明一样的创造性、同样的新思路和卓越的才华。实际上,产品的成本性能比很大程度上依靠实现人员,就如同易用性很大程度上依赖结构师一样。外部体系结构规定实际上是增强,而不是限制实现小组的创造性。一旦他们将注意力集中在没有人解决过的问题上,创意就开始奔涌而出;在毫无限制的实现小组中,进行结构的决策时,会出现大量的想法和争议,对具体实现的关注反而比较少。
在等待时,实现人员应该做什么。当建议小型体系结构团队来编写计算机或编程系统的所有外部技术说明时,编程人员提出了三个反对意见:该说明中的功能过于繁多,而对实际情况中的成本考虑比较少;结构师获得了所有创造发明的快乐,剥夺了实现人员的创造力;当体系结构的队伍缓慢工作时,很多实现人员只能空闲地坐着等待。
整个创造性活动包括三个独立阶段:体系结构、设计实现和物理实现-可以同时开始和并发执行。在外部说明完成前,设计实现人员可以做的事情-定义时间和空间目标,了解产品运行的平台配置;开始设计模块的边界、表结构、路径和阶段分解、算法以及所有的工具,还需要花时间和体系结构师沟通。物理实现则在库的调整、系统管理以及搜索和排序算法上,有许多事情处理。垂直划分
第五章 画蛇添足
结构师的交互准则和机制:尽早交流和持续沟通能使结构师有较好的成本意识,以及使开发人员获得对设计的信心,并且不会混淆各自的责任分工。面对估算过高的难题,结构师有两个选择:削减设计或者建议用成本更低的实现方法来挑战估算的结果-后者是固有的主观感性反应。此时,结构师是在向开发人员的做事方式提出挑战(一般开发人员会反对体系结构上的修改建议。通常他是对的-当正在实现产品时,某些次要特性的修改会造成意料不到的成本开销)。想要成功,结构师必须:
牢记开发人员承担创造性和发明性的实现责任,所以结构师只能建议,而不能支配
时刻准备着为所指定的说明建议一种实现的方法,同样准备接受其他任何能达到目标的方法
对上述的建议保持低调和不公开
准备放弃坚持所做的改进建议
自律-开发第二个系统所带来的后果:开发第一个系统结构师倾向于精炼和简洁-他知道自己对任务不够了解,所以他会谨慎仔细地工作。在设计第一个项目时,他会面对不断产生的装饰和润色功能-这些功能都会搁置在一边,作为下一个项目的内容。第二个系统是设计师们所设计最危险的系统-而当他着手第三个或第四个系统时,先前的经验会互相验证,得到对此类系统通用特性的判断,而且系统之间的差异会帮助他识别出经验中不够通用的部分。一种普遍倾向是过分设计第二个系统,向系统中添加很多装饰功能和想法,它们曾在第一个系统中被小心谨慎地放在次要位置。开发第二个系统所引起的后果的另一个表现与纯粹的功能修饰和增强明显不同,也就说存在对某些技术进行细化、精炼的趋势。由于基本系统设想发生了变化,这些技术已经显得落后。
结构师如何避免开发第二个系统所引起的后果,从而避免画蛇添足?虽然无法跳过二次系统,但可以有意识关注这个系统的特殊危险,运用特别的自我约束准则,来避免那些功能上的过于修饰;根据系统基本理念及目的变更,舍弃一些功能。一个可以开阔的结构师眼界的准则是为每个小功能分配一个值:每次改进,功能x不超过m字节的内存和n微秒-这些值会在一开始作为决策的向导,在物理实现期间充当指南和对所有人的警示。
项目经理如何避免开发第二个系统所引起的后果,从而避免画蛇添足?他必须坚持至少拥有两个系统以上开发经验结构师的决定。同时,保持对特殊诱惑的警觉,他可以不断提出正确的问题,确保原则上的概念和目标在详细设计中得到完整体现。
网友评论