11.6 因此,为舍弃而计划,无论如何,你一定要这样做。11.7 “开发人员交付的是用户满意程度,而不仅仅是实际的产品。”(Cosgrove)11.8 用户的实际需要和用户感觉会随着程序的构建、测试和使用而变化。11.9 软件产品易于掌握的特性和不可见性,导致了它的构建人员(特别容易)面临着永恒的需求变更。11.10 目标上(和开发策略上)的一些正常变化无可避免,事先为它们做准备总比假设它们不会出现要好得多。11.11 为变更计划软件产品的技术,特别是细致的模块接口文档——非常地广为人知,但并没有相同规模的实践。尽可能地使用表驱动技术同样是有所帮助的。[现在内存的成本和规模使这项技术越来越出众。]11.12 高级语言的使用、编译时操作、通过引用的声明整合和自文档技术能减少变更引起的错误。11.13 采用定义良好的数字化版本将变更量子(阶段)化。[当今的标准实践。]- 144 ------------------------ Page 157-----------------------为变更计划组织架构11.14 程序员不愿意为设计书写文档的原因,不仅仅是由于惰性。更多的是源于设计人员的踌躇——要为自己尝试性的设计决策进行辩解。(Cosgrove)11.15 为变更组建团队比为变更进行设计更加困难。11.16 只要管理人员和技术人才的天赋允许,老板必须对他们的能力培养给予极大的关注,使管理人员和技术人才具有互换性;特别是希望能在技术和管理角色之间自由地分配人手的时候。11.17 具有两条晋升线的高效组织机构,存在着一些社会性的障碍,人们必须警惕和积极地同它做持续的斗争。11.18 很容易为不同的晋升线建立相互一致的薪水级别,但要同等威信的建立需要一些强烈的心理措施:相同的办公室、一样的支持和技术调动的优先补偿。11.19 组建外科手术队伍式的软件开发团队是对上述问题所有方面的彻底冲击。对于灵活组织架构问题,这的确是一个长期行之有效的解决方案。前进两步,后退一步——程序维护11.20 程序维护基本上不同于硬件的维护;它主要由各种变更组成,如修复设计缺陷、新增功能、或者是使用环境或者配置变换引起的调整。11.21 对于一个广泛使用的程序,其维护总成本通常是开发成本的40%或更多。11.22 维护成本受用户数目的严重影响。用户越多,所发现的错误也越多。11.23 Campbell指出了一个显示产品生命期中每月bug数的有趣曲线,它先是下降,然后攀升。11.24 缺陷修复总会以(20-50)%的机率引入新的bug。11.25 在每次修复之后,必须重新运行先前所有的测试用例,从而确保系统不会以更隐蔽的方式被破坏。11.26 能消除、至少是能指明副作用的程序设计方法,对维护成本有很大的影响。- 145 ------------------------ Page 158-----------------------11.27 同样,设计实现的人员越少、接口越少,产生的错误也就越少。前进一步,后退一步——系统熵随时间增加11.28 Lehman和Belady 发现模块数量随大型操作系统(OS/360)版本号的增加呈线性增长,但是受到影响的模块以版本号指数的级别增长。11.29 所有修改都倾向于破坏系统的架构,增加了系统的混乱程度。即使是最熟练的软件维护工作,也只是放缓了系统退化到不可修复混乱的进程,从中必须要重新进行设计。[许多程序升级的真正需要,如性能等,尤其会冲击它的内部结构边界。原有边界引发的不足常常在日后才会出现。]第12 章干将莫邪12.1 项目经理应该制订一套策略,以及为通用工具的开发分配资源,与此同时,他还必须意识到专业工具的需求。12.2 开发操作系统的队伍需要自己的目标机器,进行调试开发工作。相对于最快的速度而言,它更需要最大限度的内存,还需要安排一名系统程序员,以保证机器上的标准软件是即时更新和实时可用的。12.3 同时还需要配备调试机器或者软件,以便在调试过程中,所有类型的程序参数可以被自动计数和测量。12.4 目标机器的使用需求量是一种特殊曲线:刚开始使用率非常低,突然出现爆发性的增长,接着趋于平缓。12.5 同天文工作者一样,系统调试总是大部分在夜间完成。12.6 抛开理论不谈,一次分配给某个小组连续的目标时间块被证明是最好的安排方法,比不同小组的穿插使用更为有效。12.7 尽管技术不断变化,这种采用时间块来安排匮乏计算机资源的方式仍得以延续20年[在1975年],是因为它的生产率最高。[在1995年依然如此]- 146 ------------------------ Page 159-----------------------12.8 如果目标机器是新产品,则需要一个目标机器的逻辑仿真装置。这样,可以更快地得到辅助调试平台。即使在真正机器出现之后,仿真装置仍可提供可靠的调试平台。12.9 主程序库应该被划分成 (1)一系列独立的私有开发库;(2)正处在系统测试下的系统集成子库;(3)发布版本。正式的分离和进度提供了控制。12.10 在编制程序的项目中,节省最大工作量的工具可能是文本编辑系统。12.11 系统文档中的巨大容量带来了新的不理解问题[例如,看看Unix],但是它比大多数未能详细描述编程系统特性的短小文章更加可取。12.12 自顶向下、彻底地开发一个性能仿真装置。尽可能早地开始这项工作,仔细地听取 “它们表达的意见”。高级语言12.13 只有懒散和惰性会妨碍高级语言和交互式编程的广泛应用。[如今它们已经在全世界使用。]12.14 高级语言不仅仅提升了生产率,而且还改进了调试:bug 更少,以及更容易寻找。12.15 传统的反对意见——功能、目标代码的尺寸、目标代码的速度,随着语言和编译器技术的进步已不再成为问题。12.16 现在可供合理选择的语言是PL/I。[不再正确。]交互式编程12.17 某些应用上,批处理系统决不会被交互式系统所替代。[依然成立。]12.18 调试是系统编程中很慢和较困难的部分,而漫长的调试周转时间是调试的祸根。12.19 有限的数据表明了系统软件开发中,交互式编程的生产率至少是原来的两倍。- 147 ------------------------ Page 160-----------------------第13 章整体部分13.1 第4、5、6 章所意味的煞费苦心、详尽体系结构工作不但使产品更加易于使用,而且使开发更容易进行以及bug 更不容易产生。13.2 V.A.Vyssotsky 提出,“许许多多的失败完全源于那些产品未精确定义的地方。”13.3 在编写任何代码之前,规格说明必须提交给测试小组,以详细地检查说明的完整性和明确性。开发人员自己不会完成这项工作。(Vyssotsky)13.4 “十年内[1965~1975],Wirth 的自顶向下进行设计[逐步细化]将会是最重要的新型形式化软件开发方法。”13.5 Wirth 主张在每个步骤中,尽可能使用级别较高的表达方法。13.6 好的自顶向下设计从四个方面避免了bug。13.7 有时必须回退,推翻顶层设计,重新开始。13.8 结构化编程中,程序的控制结构仅由支配代码块(相对于任意的跳转)的给定集合所组成。这种方法出色地避免了bug,是一种正确的思考方式。13.9 Gold 结果显示了,在交互式调试过程中,第一次交互取得的工作进展是后续交互的三倍。这实际上获益于在调试开始之前仔细地调试计划。[我认为在 1995 年依然如此。]13.10 我发现对良好终端系统的正确使用,往往要求每两小时的终端会话对应于两小时的桌面工作:1小时会话后的清理和文档工作;1小时为下一次计划变更和测试。13.11 系统调试(相对于单元测试)花费的时间会比预料的更长。13.12 系统调试的困难程度证明了需要一种完备系统化和可计划的方法。13.13 系统调试仅仅应该在所有部件能够运作之后开始。(这既不同于为了查出接口bug所采取 “合在一起尝试”的方法;也不同于在所有构件单元的bug 已知,但未修复的情况下,即开始系统调试的做法。)[对于多个团队尤其如此。]13.14 开发大量的辅助调试平台(scaffolding 脚手架)和测试代码是很值得的,代- 148 ------------------------ Page 161-----------------------码量甚至可能会有测试对象的一半。13.15 必须有人对变更进行控制和文档化,团队成员应使用开发库的各种受控拷贝来工作。13.16 系统测试期间,一次只添加一个构件。13.17 Lehman和Belady 出示了证据,变更的阶段(量子)要么很大,间隔很宽;要么小和频繁。后者很容易变得不稳定。[Microsoft 的一个团队使用了非常小的阶段(量子)。结果是每天晚上需要重新编译生成增长中的系统。]第14 章祸起萧墙14.1 “项目是怎样延迟了整整一年的时间?…一次一天。”14.2 一天一天的进度落后比起重大灾难,更难以识别、更不容易防范和更加难以弥补。14.3 根据一个严格的进度表来控制项目的第一个步骤是制订进度表,进度表由里程碑和日期组成。14.4 里程碑必须是具体的、特定的、可度量的事件,能进行清晰能定义。14.5 如果里程碑定义得非常明确,以致于无法自欺欺人时,程序员很少会就里程碑的进展弄虚作假。14.6 对于大型开发项目中的估计行为,政府的承包商所做的研究显示:每两周进行仔细修订的活动时间估计,随着开始时间的临近不会有太大的变化;期间内对时间长短的过高估计,会随着活动的进行持续下降;过低估计直到计划的结束日期之前大约三周左右,才有所变化。14.7 慢性进度偏离是士气杀手。[Microsoft 的Jim McCarthy 说:“如果你错过了一个最终期限(deadline),确保制订下一条deadline。2”]14.8 进取对于杰出的软件开发团队,同优秀的棒球队伍一样,是不可缺少的必要品德。- 149 ------------------------ Page 162-----------------------14.9 不存在关键路径进度的替代品,使人们能够辨别计划偏移的情况。14.10 PERT 的准备工作是PERT 图使用中最有价值的部分。它包括了整个网状结构的展开、任务之间依赖关系的识别、各个任务链的估计。这些都要求在项目早期进行非常专业的计划。14.11 第一份PERT 图总是很恐怖的,不过人们总是不断进行努力,运用才智制订下一份PERT 图。14.12 PERT 图为前面那个泄气的借口,“其他的部分反正会落后”,提供了答案。14.13 每个老板同时需要采取行动的异常信息以及用来进行分析和早期预警的状态数据。14.14 状态的获取是困难的,因为下属经理有充分的理由不提供信息共享。14.15 老板的不良反应肯定会对信息的完全公开造成压制;相反,仔细区分状态报告、毫无惊慌地接收报告、决不越俎代庖,将能鼓励诚实的汇报。14.16 必须有评审的机制,从而所有成员可以通过它了解真正的状态。出于这个目的,里程碑的计划和完成文档是关键。14.17 Vyssotsky:我发现在里程碑报告中很容易记录“计划(老板的日期)”和“估计(最基层经理的日期)”的日期。项目经理必须停止对这些日期的怀疑。”14.18 对于大型项目,一个对里程碑报告进行维护的计划和控制(Plan andControl)小组是非常可贵的。第15 章另外一面15.1 对于软件编程产品来说,程序向用户所呈现的面貌与提供给机器识别的内容同样重要。15.2 即使对于完全开发给自己使用的程序,描述性文字也是必须的,因为它们会被用户-作者所遗忘。15.3 培训和管理人员基本上没有能向编程人员成功地灌输对待文档的积极态度—- 150 ------------------------ Page 163-----------------------—文档能在整个生命周期对克服懒惰和进度的压力起促进激励作用。15.4 这样的失败并不都是因为缺乏热情或者说服力,而是没能正确地展示如何有效和经济地编制文档。15.5 大多数文档只提供了很少的总结性内容。必须放慢脚步,稳妥地进行。15.6 由于关键的用户文档包含了跟软件相关的基本决策,所以它的绝大部分需要在程序编制之前书写,它包括了9 项内容(参见相应章节)。15.7 每一份发布的程序拷贝应该包括一些测试用例,其中一部分用于校验输入数据,一部分用于边界输入数据,另一部分用于无效的输入数据。15.8 对于必须修改程序的人而言,他们所需要程序内部结构文档,同样要求一份清晰明了的概述,它包括了5 项内容(参见相应章节)。15.9 流程图是被吹捧得最过分的一种程序文档。详细逐一记录的流程图是一件令人生厌的事情,而且高级语言的出现使它显得陈旧过时。(流程图是图形化的高级语言。)15.10 如果这样,很少有程序需要一页纸以上的流程图。[在这一点上,MILSPEC 军用标准实在错得很厉害。]15.11 即使的确需要一张程序结构图,也并不需要遵照ANSI 的流程图标准。15.12 为了使文档易于维护,将它们合并至源程序是至关重要的,而不是作为独立文档进行保存。15.13 最小化文档负担的3个关键思路:借助那些必须存在的语句,如名称和声明等,来附加尽可能多的“文档”信息。使用空格和格式来表现从属和嵌套关系,提高程序的可读性。以段落注释,特别是模块标题的形式,向程序中插入必要的记叙性文字。15.14 程序修改人员所使用的文档中,除了描述事情如何以外,还应阐述它为什么那样。对于加深理解,目的是非常关键的,但即使是高级语言的语法,也不能表达目的。15.15 在线系统的高级语言(应该使用的工具)中,自文档化技术发现了它的绝佳应用和强大功能。- 151 ------------------------ Page 164-----------------------原著结束语E.1 软件系统可能是人类创造中最错综复杂的事物(从不同类型组成部分数量的角度出发)。E.2 软件工程的焦油坑在将来很长一段时间内会继续地使人们举步维艰,无法自拔。