分享

S140 《软件开发的201个原则》读书笔记

 过河卒冲 2022-03-12

原则是关于软件工程的基本原理、规则或假设,不管所选的技术、工具或语言是什么,这些原则都有效。

关于软件质量,业界普遍认为有3个决定性要素:人、过程和工具。如何基于这些要素提升代码的质量和开发效率,是软件工程研究者和实践者一直在努力的方向。

2017年,中国率先制定DevOps的评定标准并开展评定,研发和运维从此接轨。DevOps和敏捷开发的最大区别在于:工具链成为软件工程管理的核心,过程和工具链配套,软件工程师利用工具链提升开发效率和质量。与此同时,成熟的软件框架让内聚和耦合原则不再重要,复用成为主旋律。

从以过程为中心到以人为中心,再到以工具链为中心,这些年软件工程经历了令人眼花缭乱的变革。未来,软件新技术、新架构和新业务还会不断涌现,软件工程仍然会变革,但不变的是Alan这本书中介绍的201个原则。

如果软件工程真的是一门工程学科,那么它是对经过验证的原则、技术、语言和工具的智慧的运用,用于有成本效益地创造和维护能够满足用户需求的软件。本书是有史以来第一本成册的软件工程原则集。原则是关于软件工程的基本原理、规则或假设,不管所选的技术、工具或语言是什么,其都有效。

理解和实践一门学科的所有原则是否可以预防所有灾难?绝对不是。它只会大大降低因你而导致灾难的可能性。正如亚历山大·波普所说,“犯错是人之常情。”只有通过犯错,我们才能学习,并制定新的原则。

对于一个软件工程师,具备正确的意识比掌握具体的知识更重要。如果有正确的意识,即使不记得具体的知识点,也可以在需要的时候查阅相关资料,而反过来则不是这样的。

前言

Manny Lehman已经充分说明,为什么软件工程的基本原则和人类探索的其他领域的基本原则存在根本性的不同。

原因是,一方面不像物理学或生物学,软件开发过程是由人来管理和实现的;这样,从长远看,软件开发的行为是不可预测的,它依赖于人的判断、奇想和行动。另一方面,软件似乎展现出很多有规律和可预测的特征。这使得很多基本原理可以被列出,并可以被没有经验的或有经验的软件工程师和管理者使用,以增强软件工程过程和软件产品的质量。

第1章

引言

本书包含一系列软件工程的原则。这些原则代表了我们所认为的软件开发过程中的最先进、最正确的理念。其他工程领域有基于物理学、生物学、化学或数学定律的原则,然而,由于软件工程的产物是非实体的(nonphysical),所以实体的定律(laws of the physical)并不能轻易地成为软件工程领域坚实的基础。

原则(Principle)是工作的准则;原则代表了许多人从经验中总结出来的集体智慧。

技术(Technique)是一种按部就班的流程,它帮助软件开发者执行一部分软件工程过程。技术倾向于强制遵循基本原则的一个子集。

语言(Language)由一组基本元素(如单词或图形符号)、规则和语义组成。规则可以让人们用基本元素构造出更复杂的实体(如句子、图表、模型),语义则赋予每个实体组合以意义。

工具(Tool)是软件程序,可帮助软件工程师执行软件工程中的某些步骤。

第2章

一般原则 GENERAL PRINCIPLES

原则1 质量第一

QUALITYIS #1

质量必须被放在首位,没有可商量的余地。

原则2 质量在每个人眼中都不同

QUALITYIS IN THE EYES OF THE BEHOLDER

软件质量没有唯一的定义。项目必须确定各因素的优先级,并清晰地传达给所有相关方。

原则3 开发效率和质量密不可分

PRODUCTIVITYAND QUALITY ARE INSEPARABLE

开发效率与质量之间存在明显的关系。对质量要求越高,开发效率就越低。对质量要求越低,开发效率就越高。

原则4 高质量软件是可以实现的

HIGH-QUALITYSOFTWARE IS POSSIBLE

作为软件开发人员,应该学习和了解已被验证、可以极大提高软件质量的方法。这些方法包括:让客户参与(见原则8)、原型设计(在全面开发之前验证需求;见原则11~13)、保持设计简单(见原则67)、审查代码(见原则98)和雇用最优秀的人(见原则130和131)。作为客户,在追求卓越的同时,要意识到随之而来的高额成本。

原则5 不要试图通过改进软件实现高质量

DON'TTRY TO RETROFIT QUALITY

质量无法通过软件的改进来获得。这适用于质量的任何定义:可维护性、可靠性、适应性、可测试性、安全性等。

原则6 低可靠性比低效率更糟糕

POORRELIABILITY IS WORSE THAN POOR EFFICIENCY

如果软件执行效率不高,通常可以分离出消耗大部分执行时间的程序单元,重新设计或编码以提高效率(见原则194)。低可靠性问题不仅难以发现,而且难以修复。

原则7 尽早把产品交给客户

GIVEPRODUCTS TO CUSTOMERS EARLY

在需求阶段,无论你多么努力地试图去了解客户的需求,都不如给他们一个产品,让他们使用它,这是确定他们真实需求的最有效方法。

原则8 与客户/用户沟通

COMMUNICATEWITH CUSTOMERS/USERS

永远不要忽视开发软件的原因:满足真正的需求,解决真正的问题。解决真正需求的唯一方法,是去跟有真正需求的人沟通。客户或用户是你的项目的最重要参与者。

原则9 促使开发者与客户的目标一致

ALIGNINCENTIVES FOR DEVELOPER AND CUSTOMER

项目经常会因为客户和开发人员的目标不同(或不兼容)而失败。

原则10 做好抛弃的准备

PLANTO THROW ONE AWAY

对一个项目来说,最关键的成功因素之一,是它所涉及的领域是否是全新的。在全新领域(可能涉及应用程序、体系结构、接口、算法等)研发的程序很少能第一次就成功。

原则11 开发正确的原型

BUILDTHE RIGHT KIND OF PROTOTYPE

一次性原型应该在关键需求特性没有被很好理解时使用。演进式原型应该在关键特性已被充分理解,但很多其他需求特性没被充分理解时使用。如果对大多数功能都不了解,则应首先构建一个一次性原型,然后从零开始构建一个演进式原型。

原则12 构建合适功能的原型

BUILDTHE RIGHT FEATURES INTO A PROTOTYPE

当建立一次性原型时,只需要开发那些没有被充分理解的特性。

当建立演进式原型时(见原则13),要优先开发那些已经被充分理解的特性。

原则13 要快速地开发一次性原型

BUILDTHROWAWAY PROTOTYPES QUICKLY

如果你已经决定开发一次性原型,那么就要用最快的方式。不用担心质量。

原则14 渐进地扩展系统

GROWSYSTEMS INCREMENTALLY

渐进地扩展系统,是降低软件开发风险的最有效方法之一。从一个小的可用系统开始,只实现少数功能。然后逐步扩展,覆盖越来越多的最终功能子集。

这样做的缺点是:如果过早地选择了一个不合适的系统架构,则可能需要全部进行重新设计才能适应后续的变更。在开始增量开发之前,开发一次性原型(见原则11、12和13),可以降低这种风险。

原则15 看到越多,需要越多

THEMORE SEEN,THE MORE NEEDED

提供给用户的功能(或性能)越多,用户想要的功能(或性能)就越多。

当然,这与原则7(尽早把产品交给客户)、原则14(渐进地扩展系统)、原则185(软件会持续变化)以及原则201(系统的存在促进了演变)互相支持。但更重要的是,你必须为不可避免的情况做好准备。在管理和工程处理流程的每个方面都应该做好准备,一旦用户看到产品,他们就会想要更多的东西。

原则16 开发过程中的变化是不可避免的

CHANGEDURING DEVELOPMENT IS INEVITABLE

爱德华·伯索夫(Edward Bersoff)等人将系统工程的第一定律定义为:无论你处在系统(开发)生命周期中的何处,系统都将发生变化,并且对其进行改变的愿望将在整个生命周期中持续存在。

为变化做好准备,要确保:软件开发涉及的所有产品之间的相互引用都是适当的(见原则43、62和107);变更管理流程已就位(见原则174、178~183);预算和开发时间有足够的余地,不会为了满足预算和开发时间而忽略必要的变更(见原则147、148和160)。

原则17 只要可能,购买而非开发

IFPOSSIBLE,BUY INSTEAD OF BUILD

要降低不断上涨的软件开发成本和风险,最有效的方法就是,购买现成的软件,而不是自己从头开发。

原则18 让软件只需简短的用户手册

BUILDSOFTWARE SO THAT IT NEEDS A SHORTUSERS' MANUAL

衡量软件系统质量的一种方法是查看其用户手册内容的多少。手册中的内容越少,软件质量越好。

原则19 每个复杂问题都有一个解决方案

EVERYCOMPLEX PROBLEM HAS A SOLUTION

Wlad Turski说,“每一个复杂的问题,都有一个简单的解决方案……但这是错误的!”无论任何人向你提出“只要遵循这10个简单步骤,软件质量问题就会消失”,或是其他类似建议,都要保持高度怀疑。

原则20 记录你的假设

RECORDYOUR ASSUMPTIONS

系统运行的环境在本质上是无限的,不可能被完全理解。当我们开发一个系统,宣称要解决某个环境中的一个问题时,我们会对该环境进行假设。

对需求工程、设计、编码和测试期间所做的所有假设,始终保持觉察是不可能的。尽管如此,我还是建议,对你有意识做出的假设做个记录。

原则21 不同的阶段,使用不同的语言

DIFFERENTLANGUAGES FOR DIFFERENT PHASES

对于需求工程,应该选择一组最优的技术和语言(见原则47和48)。对于设计工作,应该选择一组最优的技术和语言(见原则63和81)。对于编码,应该选择一种最适合的语言(见原则102和103)。一方面,在不同阶段之间转换是困难的。使用同一种语言并没有帮助。另一方面,如果一种语言从某方面在两个阶段都是最优选择,就务必使用它。

原则22 技术优先于工具

TECHNIQUEBEFORE TOOLS

在使用工具前,你应该先要“有规矩”(即理解并遵循适当的软件开发方法)。当然,你也要了解如何使用工具,但这和“有规矩”相比是第二位的。

原则23 使用工具,但要务实

USETOOLS,BUT BE REALISTIC

一些软件工具(如CASE)会让用户的工作更加高效。,艰难的工作(思考)都不是由工具完成的。

使用CASE工具,要切实考虑其对开发效率的影响。请注意,70%的CASE工具在购买后从未被使用过。

原则24 把工具交给优秀的工程师

GIVESOFTWARE TOOLS TO GOOD ENGINEERS

我们想把CASE工具只提供给优秀的工程师,而不想把CASE工具提供给平庸的工程师:我们希望他们尽量少(而非多)地开发出质量低劣的软件。

原则25 CASE工具是昂贵的

CASETOOLS ARE EXPENSIVE

CASE工具对软件开发来说是必需的。它们应该被视为业务成本的一部分。在做投资回报分析时,不仅需要考虑购买工具的高额费用,还需要考虑没有购买工具带来的更高代价(更低的开发效率、更高的客户失望率、延迟的产品发布、增加的重复工作、更差的产品质量、增加的员工流动)。

原则26 “知道何时”和“知道如何”同样重要

“KNOW-WHEN”ISAS IMPORTANT AS KNOW-HOW

一名优秀的工程师了解很多不同种类的技术,并且知道每种技术何时适合项目或项目的一部分。一名优秀的工程师知道多种工具的用法,知道很多不同的技巧,而且,最重要的是,知道什么时候该用哪一种。

原则27 实现目标就停止

STOPWHEN YOU ACHIEVE YOUR GOAL

不要太过于陷入具体的方法,而忘记了目标本身。不要为更换目标而感到内疚。

此外,你需要对整个软件过程有很好的认识,因为基于本原则所抛弃的某个方法的后续步骤可能会对未来软件的使用产生重要影响。

原则28 了解形式化方法

KNOWFORMAL METHODS

在每个项目中,至少应该有一个人能熟练使用形式化方法,以确保不会错过提升产品质量的机会。

很多人以为,使用形式化方法的唯一途径,就是完全使用它们来定义系统。其实并非如此。实际上,最有效的方法之一,是先用自然语言描述。然后再尝试用形式化方法去写其中某些部分。尝试用更形式化的方式书写,会帮助你发现在自然语言中存在的问题。修正自然语言表达中的问题,你会得到一个更好的文档。在完成之后,如果有需要,可以再把形式化的描述去掉。

原则29 和组织荣辱与共

ALIGNREPUTATION WITH ORGANIZATION

一般而言,当任何人发现你在产品中犯的错误时,你应该心存感激,而不是试图辩解。人非圣贤,孰能无过。过而能改,善莫大焉!

产品中的缺陷是公司的耻辱;软件工程师引起的公司耻辱,是工程师的耻辱。这种观念在日本比在美国更深入人心,因为日本劳动者倾向于一辈子只服务一家公司。

原则30 跟风要小心

FOLLOWTHE LEMMINGS WITH CARE

大家都做的事情,对你来说也不一定是正确的。也许它是正确的,但你也应该评估它对你所处环境的适用性。

当你学习“新”技术时,不要轻易接受与之相关的不可避免的炒作(见原则129)。要仔细阅读,理性考虑它的收益和风险。在大规模应用之前要进行试验。但同时也绝对不要忽略“新”技术(见原则31)。

原则31 不要忽视技术

DON'TIGONRE TECHNOLOGY

有两种方式可以让你紧跟技术潮流:阅读正确的杂志,和正确的人交谈。

原则32 使用文档标准

USEDOCUMENTATION STANDARDS

创新!即遵循标准,同时理智地执行。

原则33 文档要有术语表

EVERYDOCUMENT NEEDS A GLOSSARY

所有术语的定义都应该以这样的方式编写:定义中使用的任何单词,都应该尽量避免再去术语表中查找含义。一种技巧是首先用日常用语解释,然后再使用术语解释。

原则34 软件文档都要有索引

EVERYSOFTWARE DOCUMENT NEEDS AN INDEX

索引通常是文档所使用的所有术语和概念的列表,包括一个或多个页码,用于标记术语或概念在哪里被定义、使用或引用。对于需求、设计、编码、测试、用户的和维护文档来说都是如此。索引可以帮助读者快速查找信息,对于文档后续的维护和优化也很重要。

原则35 对相同的概念用相同的名字

USE THE SAME NAME FOR THE SAME CONCEPT

在技术文档中,必须使用相同的术语来表示相同的概念,使用相同的语句结构来表述相似的信息。

原则36 研究再转化,不可行

RESEARCH-THEN-TRANSFERDOESN'T WORK

要实现从研究所到开发机构的最成功的成果转化,从一开始双方就要紧密合作。需要使用工业界的环境作为萌发想法并验证效果的实验室,而不是在想法成形后再做技术转化。

原则37 要承担责任

TAKERESPONSIBILITY

不要有任何借口。如果你是一个系统的开发者,把它做好是你的责任。要承担这个责任。要么做好,要么就压根不做。

十六年后,作者复盘:

对于第2章中介绍的一般原则,全部依然有效。一些额外说明如下。

■ 在原则 23~25 中,“CASE”这个词已经不流行了。今天,主要的软件开发工具支持问题跟踪、版本控制、虚拟机模拟、项目管理和调试。

■ 对于原则 28,我必须承认,在过去 26 年里,据我所知,没有一个为我工作过的软件工程师使用过形式化方法本身,尽管其中很多人还拥有高等数学学位,也就是说,他们在本质上,是知道如何以形式化的方式思考的。

第3章 需求工程原则

REQUIREMENTS ENGINEERING PRINCIPLES

需求工程包括以下活动:(1)提出或研究需要解决的问题;(2)具体说明一个能解决该问题的系统外部(黑盒)行为。需求工程的最终产出是需求规格说明(Requirement Specification)。

原则38 低质量的需求分析,导致低质量的成本估算

POOR REQUIREMENTS YIELD POOR COSTESTIMATES

可以使用原型,来降低需求不准确的风险。可以使用配置管理,来控制需求变更。应该为将来的发布,规划好新的需求。应该使用更正式的方法,进行需求分析和编写需求规格说明。

原则39 先确定问题,再写需求

DETERMINE THE PROBLEM BEFORE WRITINGREQUIREMENTS

当面对他们认定的问题时,大多数工程师都会匆忙提供解决方案。如果工程师对这个问题的看法是正确的,那么解决方案可能奏效。然而,问题往往是难以捉摸的。

任何一个方案生效,都取决于特定的场景。在试图解决问题前,针对面临问题的人及问题的本质,要确保深入分析了所有的可能选择。在解决问题时,不要被最初方案带来的潜在兴奋所蒙蔽。方案的变化总是比构建系统的成本低。

原则40 立即确定需求

DETERMINE THE REQUIREMENTS NOW

立刻不计代价、尽可能多地获取需求信息。应使用原型的方法。要和更多的客户交谈。可以与客户一起工作一个月,以获得客户使用情况的第一手信息。要收集数据。要使用所有可能的手段。现在就把你所理解的需求记录下来,并规划构建一个满足这些需求的系统。如果你预期需求会发生很大变化,那也没关系。可以用增量的方式开发(见原则14),但这并不是在任何一个增量开发上做不好需求规格说明的借口。

原则41 立即修复需求规格说明中的错误

FIX REQUIREMENTS SPECIFICATION ERRORSNOW

原则42 原型可降低选择用户界面的风险

PROTOTYPES REDUCE RISK IN SELECTINGUSER INTERFACES

在全面开发之前,以低风险、高回报的方式对用户界面达成一致,没有什么方法比原型更有效。

原则43 记录需求为什么被引入

RECORD WHY REQUIREMENTS WERE INCLUDED

当做出需求决策时,记录一个指向其来源的标识。例如,如果决策是在与客户交谈时做出的,需要记录日期、时间及访谈的参与者。理想情况下,应明确所参考的文字、录音或录像记录。只有基于这样的档案记录,才能(1)随后扩展需求,或(2)在已完成的系统不能满足需求时做出响应。

原则44 确定子集

IDENTIFY SUBSETS

在编写需求规格说明时,要清晰识别有用的需求的最小子集。同时,还要识别使最小子集越来越实用的最小增量。这种识别为软件设计者提供了洞察最佳软件设计的视角。

记录子集的一种非常有效的技巧,是在软件需求规格说明中的每个需求旁边加上几列。每列对应不同的版本。这些版本可以代表一个产品的多种功效,每种功效对应一个不同的客户或场景,它们也可以代表产品随时间日益提高的层级。在上述两种情况下,在适当的列中放置一个“X”,以指示哪些版本将具有哪些功能。

原则45 评审需求

REVIEW THE REQUIREMENTS

许多相关方都对产品的成功有影响:用户、客户、市场营销人员、开发人员、测试人员、质量保证人员等。所有这些人也对需求规格说明的正确性和完整性有影响。在进行设计或编码之前,应该对需求规格说明进行正式的评审。

原则46 避免在需求分析时进行系统设计

AVOID DESIGN IN REQUREMENTS

需求阶段的目标是明确系统的外部行为。这些外部行为需要足够明确,以保证当使用需求规格说明作为指引时,所有设计人员都能对系统的目标行为做出同样的理解。但在需求阶段不应该去明确软件架构或者算法,因为这是设计人员的工作范畴。后续设计人员会选择能够最好地满足需求的架构和算法。

原则47 使用正确的方法

USE THE RIGHT TECHNIQUES

复杂软件的需求,需要使用多种方法才能被充分理解,要使用对于你的软件来说最合适的一种或一组方法。

原则48 使用多角度的需求视图

USE MULTIPLE VIEWS OF REQUIREMENTS

任何单一的需求视角,都不足以理解或描述一个复杂系统的预期外部行为。比起使用结构化分析、面向对象分析或状态图,应选择并使用一个有效的组合。

原则49 合理地组织需求

ORGANIZE REQUIREMENTS SENSIBLY

我们通常需要有层次地组织需求。这有助于读者理解系统功能,也有助于需求编写者在需求变更时定位章节。组织需求有很多方式,选择哪种最合适的方式取决于具体产品。

原则50 给需求排列优先级

PRIORITIZE REQUIREMENTS

原则51 书写要简洁

WRITE CONCISELY

原则52 给每个需求单独编号

SEPARATELY NUMBER EVERY REQUIREMENT

需求规格说明中的每条需求能很容易地被引用,这很重要。这对后续在设计中追踪需求(见原则62)和在测试中追踪需求(见原则107)是必要的。

原则53 减少需求中的歧义

REDUCE AMBIGUITY IN REQUIREMENTS

大多数需求规格说明用自然语言编写。由于词、短语和句子的语义不严密,自然语言存在固有的歧义问题。尽管消除需求中所有歧义的唯一方法是使用形式语言,但是通过仔细地评审和重写有明显或微妙歧义的文字,可以在一定程度上减少歧义。

原则54 对自然语言辅助增强,而非替换

AUGMENT,NEVER REPLACE,NATURALLANGUAGE

原则55 在更形式化的模型前,先写自然语言

WRITE NATURAL LANGUAGE BEFORE A MOREFORMAL MODEL

原则54说,要创建同时包含自然语言和形式化模型的需求规格说明。一定要先写自然语言的描述。如果先基于形式化模型描述,会倾向于用自然语言描述模型,而非描述解决方案系统。

原则56 保持需求规格说明的可读性

KEEP THE REQUIREMENTS SPECIFICATIONREADABLE

需求规格说明必须可被大范围的个人或组织阅读和理解:用户、客户、市场营销人员、需求作者、设计师、测试人员、管理人员等。文档必须使所有人充分领会所需要的并在开发中的系统,这样才不会出现意外。

仅当你可以保证各个版本之间的一致性时,创建多个需求规格说明(每个对应一个相关方的子集)才是可行的。一种更有效的方法是,保留自然语言(见原则54),同时结合更形式化的多视角(见原则48和53)。

原则57 明确规定可靠性

SPECIFY RELIABILITY SPECIFICALLY

软件的可靠性很难被准确说明。不要因为含糊其词,让问题变得更难以被解决。

原则58 应明确环境超出预期时的系统行为

SPECIFY WHEN ENVIRONMENT VIOLATES'ACCEPTABLE' BEHAVIOR

需求规格说明通常会定义系统环境的特征。这些信息被用于做理智的设计决策。这通常意味着,开发人员有义务来考虑并容纳这些特性。但当系统环境超出这些限制时,系统部署后将会发生什么?

原则59 自毁的待定项

SELF-DESTRUCT TBD'S

通常来讲,需求规格说明中不应包含待定项(TBD,To Be Determined)。

原则60 将需求保存到数据库

STORE REQUIREMENTS IN A DATABASE

需求是复杂和非常不稳定的。出于这些原因,应将它们保存到电子设备,最好是数据库中。这将方便进行修改、排查修改带来的影响、记录特定需求的细节属性等。

十六年后,作者复盘:

对于第3章中介绍的需求工程原则,全部依然有效。一些额外说明如下。

■ 对于原则 40、41、43、45、47、48、49 和 54,在过去26 年的大部分时间里,我一直致力于创业,在这种环境下,向客户提供一系列不断增大的最小可行产品(MVP)以获取他们的反馈至关重要。我们只是在问题跟踪工具中以自然语言维护我们的需求,我们可以轻松地用优先级、目标版本、状态和注解对它们进行注释。当然,这正是原则 60 所体现的精神。

第4章 设计原则

DESIGN PRINCIPLES

设计包括以下活动:(1)定义满足需求的软件架构(architecture),(2)具体说明架构中的各个软件组件的算法。架构包括:软件中所有模块的定义,它们之间如何提供接口,它们之间如何组装,组件的拷贝如何实例化(即在内存中创建并执行的组件拷贝)和销毁。设计的最终产出是设计规格说明(Design Specification)。

原则61 从需求到设计的转换并不容易

TRANSITION FROM REQUIREMENTS TO DESIGNIS NOT EASY

需求工程最终会形成需求规格说明,是一个系统外部行为的详细描述。设计的第一步,是综合形成一个理想的软件架构。在软件工程领域,从需求到设计的转换,并不比在其他任何工程学科中更容易。设计很难。从外部视角到内部最优设计的转换,从根本上说是一个难题。

原则62 将设计追溯至需求

TRACE DESIGN TO REQUIREMENTS

当设计软件时,设计者必须知道,哪些需求能被每个组件满足。当选择软件架构时,重要的是所有需求都能被覆盖。软件部署后,当检测到故障时,维护人员需要快速分离出那些最有可能包含故障原因的软件组件。在维护期间,当一个软件组件被修复时,维护人员需要知道哪些需求可能会受到不利的影响。

原则63 评估备选方案

EVALUATE ALTERNATIVES

在所有工程学科中,一个重要的思想是:详细列出多种方法,在这些方法之间权衡分析,并最终采用一种。在需求达成一致后,你必须充分考虑各种架构和算法。你当然不想直接使用那种在需求规格说明中提到的架构(见原则46)。毕竟,在需求规格说明中选择这种架构是为了优化系统外部行为的可理解性。你需要的架构是与需求规格说明中包含的需求保持最优一致的那种架构。

原则64 没有文档的设计不是设计

DESIGN WITHOUT DOCUMENTATION IS NOTDESIGN

设计,是在纸或其他媒介上,对恰当的体系结构和算法的选择、抽象和记录。

原则65 封装

ENCAPSULATE

封装,指的是一组统一的规则集,其是关于哪些类型的信息应该被隐藏的。

原则66 不要重复造轮子

DON'T REINVENT THE WHEEL

软件工程师经常一次又一次地重新发明组件。他们很少修补已有的软件组件。有趣的是,软件业称这种罕见的实践为“复用”,而不是“工程”。

原则67 保持简单

KEEP IT SIMPLE

一个简单的架构或者一个简单的算法,在实现高可维护性方面,有很长的路要走。记住KISS原则。

原则68 避免大量的特殊案例

AVOID NUMEROUS SPECIAL CASES

在你设计算法时,无疑会发现存在许多例外情况。例外情况会使得特殊案例被加入算法。每一个特殊案例都会使你更难调试,并使其他人更难修改、维护和增加功能。

如果你发现有太多的特殊案例,那么你可能设计了一个不合适的算法。应重新思考并重新设计算法(见原则67)。

原则69 缩小智力距离

MINIMIZE INTELLECTUAL DISTANCE

艾兹格·迪科斯彻(Edsger Dijkstra)将智力距离(Intellectual Distance)定义为,现实问题和对此的计算机解决方案之间的距离。理查德·费莱(Richard Fairley)认为,智力距离越小,维护软件就越容易。

为了做到这一点,软件的结构应该尽可能地接近模仿现实世界的结构。面向对象设计和杰克逊系统方法(Jackson System)等设计方法,将最小的智力距离作为主要的设计驱动。但是你可以使用任何设计方法去缩小智力距离。当然,要意识到“现实世界的结构”并不是唯一的。正如杰威德·西迪奇(Jawed Siddiqi)在1994 年 3 月发表在IEEE Software上的文章(标题为“Challenging Universal Truths of Requirements Engineering”,挑战需求工程的普遍真理)中所指出的,不同的人在审视同一个现实世界时,往往会感知到不同的结构,这样就构造出了相当多样化的“构造的现实”。

70 将设计置于知识控制之下

KEEP DESIGN UNDER INTELLECTUAL CONTROL

如果设计是以能使其创建者和维护者完全理解的方式创建和记录的,那么这个设计就是在知识可控范围内的。

这种设计的一个基本属性是,它是分层构建的和多视角的。层次结构使读者能够抽象地理解整个系统,并在向更深层次移动时,可理解越来越多的细节。在每个层次上,组件应该仅从外部视角描述(见原则80)。此外,任何单个组件(在层次结构的任何级别中)都应该展现出简单和优雅。

原则71 保持概念一致

MAINTAIN CONCEPTUAL INTEGRITY

概念一致是高质量设计的一个特点。它意味着,使用有限数量的设计“形式”,且使用方式要统一。

原则72 概念性错误比语法错误更严重

CONCEPTUAL ERRORS ARE MORE SIGNIFICANTTHAN SYNTACTIC ERRORS

软件开发中,不论是写需求规格说明、设计文档、代码还是测试,我们都要花费大量精力来排除语法错误。这是值得赞许的。然而,构建软件真正的困难来自概念性错误。

在开发的各个阶段,问自己一些关键的问题。在需求阶段,问自己,“这是客户想要的吗?”在设计阶段,问自己,“这个架构在压力下可以正常工作吗?”或者“这个算法真的适用于各种场景吗?”在编码阶段,问自己,“这段代码的执行和我想的一样吗?”或者“这段代码是否正确实现了这个算法?”在测试阶段,问自己,“执行这段测试能让我确信什么吗?”

原则73 使用耦合和内聚

USE COUPLING AND COHESION

耦合,是对两个软件组件之间关联程度的度量。内聚,是对一个软件组件内功能之间相关程度的度量。我们要追求的是低耦合和高内聚。高耦合意味着,当修改一个组件时,很可能需要修改其他组件。低内聚意味着,难以分离出错误原因或者难以判断为满足新需求而要修改的位置。

原则74 为变化而设计

DESIGN FOR CHANGE

在软件开发中,我们经常会遇到错误、新需求或早期错误沟通导致的问题。所有这些都会导致设计的变化,甚至在设置基线之前(见原则16)。而且,在设置基线和交付产品后,会出现更多的新需求(见原则185)。这些都意味着,你必须选择架构、组件和规范技术,以适应重大和不断的变化。

原则75 为维护而设计

DESIGN FOR MAINTENANCE

对于非软件产品,设计后的最大成本风险是制造。对于软件产品,设计后的最大成本风险是维护。对于前者,为制造而设计是主要的设计驱动力。不幸的是,为维护而设计并不是软件的标准。但它本应该是。

原则76 为防备出现错误而设计

DESIGN FOR ERRORS

将健壮性融入设计并不容易。

原则77 在软件中植入通用性

BUILD GENERALITY INTO SOFTWARE

一个软件组件的通用性体现在,它在不同的场景下不做任何修改就能执行预期功能。通用的软件组件要比不太通用的组件更难设计。此外,它们通常执行更慢。

当把一个系统拆分成子组件时,要注意其潜在的通用性。很明显,当多个地方都需要一个相似的功能时,只需构建一个通用功能组件,而非多个相似功能的组件。同样,在开发只在一个地方需要的功能时,要尽可能植入通用性,以便日后扩展。

原则78 在软件中植入灵活性

BUILD FLEXIBILITY INTO SOFTWARE

一个软件组件的灵活性体现在,它很容易被修改,以在不同的场景下执行其功能(或者相似功能)。灵活的软件组件比不太灵活的组件更难设计。

原则79 使用高效的算法

USE EFFICIENT ALGORITHMS

了解算法复杂度理论是成为一名优秀设计者的必要前提。给定任何问题,你都应该给出无限多种可选算法来解决它。“算法分析”理论让我们知道,如何区分本来速度就慢的算法(不管编码如何优秀)和速度快几个数量级的算法。

原则80 模块规格说明只提供用户需要的所有信息

MODULE SPECIFICATIONS PROVIDE ALL THEINFORMATION THE USER NEEDS AND NOTHING MORE

设计过程中的一个关键部分,是对系统中每个软件组件的精确定义。这个规格说明将成为组件“可见”或“公开”的部分。它必须包含用户(这里的“用户”是指,另一个软件组件,或另一个组件的开发者)需要的全部内容,如用途、名字、调用方法、如何同所在环境通信的细节等。任何用户不需要的内容,都要明确排除在外。

原则81 设计是多维的

DESIGN IS MULTIDIMENSIONAL

在设计一座房子时,建筑设计师需要以多种方式来描述,以方便建筑工人、建筑原材料购买者,以及房屋购买者来充分了解房屋的本质。这些描述方式包括:立面图、平面图、框架、电气图、管道图、门窗细节,以及其他。对于软件设计,也是一样的道理。

原则82 优秀的设计出自优秀的设计师

GREAT DESIGNS COME FROM GREAT DESIGNER

优秀设计的特征是:简洁(Clean)、简单(Simple)、优雅(Elegant)、快速(Fast)、可维护(Maintainable)、易于实现(Easy to Implement)。优秀的设计源于灵感和洞察力,而不仅是努力工作或按部就班的设计方法。对于最好的设计者要重点支持。他们才是未来。

原则83 理解你的应用场景

KNOW YOUR APPLICATION

无论需求文档写得多好,架构和算法的最优选择,都应主要基于对应用场景特质的理解。

原则84 无须太多投资,即可实现复用

YOU CAN REUSE WITHOUT A BIG INVESTMENT

要复用软件组件,最有效的方法是:从一个包含精心制作和挑选的组件的代码库开始,这些组件是专门为重用而定制的。

原则85 “错进错出”是不正确的

“GARBAGE IN,GARBAGE OUT” IS INCORRECT

很多人引用“错进错出”,好像软件这样运行是可以接受的。它是不可以接受的。如果一个用户提供了非法的输入数据,程序应该返回一个好理解的提示,解释为什么这个输入是非法的。如果一个软件组件收到非法的输入数据,不应继续处理,而应给发出错误数据的组件返回一个错误码。这样的思维方式,可以帮助减少软件错误带来的多米诺效应,并且更容易定位错误。因为这样可以尽早捕获错误,并阻止进一步的数据污染。

原则86 软件可靠性可以通过冗余来实现

SOFTWARE RELIABILITY CAN BE ACHIEVEDTHROUGH REDUNDANCY

根据相同的需求规格说明,(让两个不同的设计团队)设计出两套软件系统,然后并行部署。用以上方案,开发成本会翻倍,可靠性会呈指数级提升。

十六年后,作者复盘:

对于第4章中介绍的设计原则,全部依然有效。一些额外说明如下。

■ 对于原则 73,如今耦合和内聚变得不那么重要,已经被重用所取代。今天,我们通过从大量经过验证的组件库中选取许多组件来构建系统,并在必要时进行定制开发。库所依赖的框架倾向于鼓励弱耦合和强内聚,但我们不再需要考虑它了。

■ 原则 76 和 84 可能是所有原则中最重要的原则。当然,我们在过去26年见证了框架的出现,它使软件重用变得更加容易。事实上,不再称之为重用,我们就简称其为软件开发。

■ 随着硬件变得更快、更便宜,原则 79 变得越来越不重要。

■ 原则 80,如果实施更正,可能已经避免了阿丽亚娜 5 型火箭的灾难。

第5章 编码原则

CODING PRINCIPLES

编码是包含以下行为的集合:1)将设计阶段确定的算法转换为用计算机语言编写的程序。2)将程序(通常是自动化的)转换为可被计算机直接执行的语言。编码的主要输出结果就是程序清单。

原则87 避免使用特殊技巧

AVOID TRICKS

避免编写使用特殊技巧的代码,以向世界展示你有多聪明!

很多程序员喜欢写带有特殊技巧(trick)的程序。这些程序虽然可以正确地执行功能,但是使用了非常晦涩难懂的方式。典型的表现是,他们利用一个函数的副作用来实现一个主要功能。

原则88 避免使用全局变量

AVOID GLOBAL VARIABLES

全局变量便于程序编写。不幸的是,“全局”意味着,任何人都可能错误地修改它的值。

作为替代方案,可以将重要数据封装在对应模块中(见原则65),这样任何人都必须通过指定方式来访问或者修改它。此外,可以显式地给需要特定数据的程序传递参数。如果发现参数过多,那么可能需要改造你的设计。

原则89 编写可自上而下阅读的程序

WRITE TO READ TOP-DOWN

人们阅读程序代码通常都是从上(即,第一行)到下(即,最后一行)。要编写有助于读者理解的程序。

原则90 避免副作用

AVOID SIDE-EFFECTS

程序的副作用,是指程序的某些操作不是其主要目的,并且这些操作对程序外部可见(或其结果能被外部感知)。副作用是软件中许多细微错误的来源。即,这些错误是潜伏最深的,一旦它们的症状表现出来是最难排查的。

原则91 使用有意义的命名

USE MEANINGFUL NAMES

原则92 程序首先是写给人看的

WRITE PROGRAMS FOR PEOPLE FIRST

除了非常少数的例外场景,程序员应该首先考虑的是,后续需要尝试理解和适配软件的人员。任何能够帮助他们的事情都应该去做(原则 87 到 91 会提供一些帮助)。执行效率也很重要(见原则63、79、94),但它们并不是互斥的。如果你需要执行效率,这没问题,但要提升程序的可读性,以免在这个过程中对相关人员造成负面影响。

原则93 使用最优的数据结构

USE OPTIMAL DATA STRUCTURES

当你准备编写程序时,应该将算法和数据结构一起考虑。在选择最佳组合之前,请尝试两个或三个或更多不同的组合。应确保将数据结构封装在一个组件内(见原则65),这样当发现更好的数据结构时,可以轻松地进行修改。

原则94 先确保正确,再提升性能

GET IT RIGHT BEFORE YOU MAKE IT FASTER

原则95 在写完代码之前写注释

COMMENT BEFORE YOU FINALIZE YOUR CODE

原则96 先写文档后写代码

DOCUMENT BEFORE YOU START CODING

第95个原则解释了为什么应该在写完代码前加注释。第96个原则更进一步:在开始写代码之前,就应该这么做!

原则97 手动运行每个组件

HAND-EXECUTE EVERY COMPONENT

手动执行一些简单的测试用例,一个软件组件或许会花30分钟的时间。但一定要做这件事!

原则98 代码审查

INSPECT CODE

原则99 你可以使用非结构化的语言

YOU CAN USE UNSTRUCTURED LANGUAGES

目前似乎只有在使用汇编语言的场景,才有可能用到这个原则。目前使用的绝大多数编程语言都已经是结构化的编程语言了。

原则100 结构化的代码未必是好的代码

STRUCTURED CODE IS NOT NECESSARILYGOOD CODE

对高质量的程序,结构几乎是必要条件,但远不是充分条件。

原则101 不要嵌套太深

DON'T NEST TOO DEEP

嵌套超过三层就会严重降低可理解性。

原则102 使用合适的语言

USE APPROPRIATE LANGUAGES

原则103 编程语言不是借口

PROGRAMMING LANGUAGES IS NOT AN EXCUSE

使用任何语言都能写出高质量的程序。事实上,如果你是一个好的程序员,对任何一种编程语言来说你都应该是一个好程序员(见原则104),尽管不太理想的编程语言可能会给你的工作增加一些困难。

原则104 编程语言的知识没那么重要

LANGUAGE KNOWLEDGE IS NOT SO IMPORTANT

不管使用哪种语言,优秀的程序员都是优秀的。不管使用哪种语言,糟糕的程序员仍然是糟糕的。

原则105 格式化你的代码

FORMAT YOUR PROGRAMS

使用标准的缩进规则,可大大提高程序的可读性。选择遵循哪种规则无关紧要,但一旦选择了,就要保持一致。

原则106 不要太早编码

DON'T CODE TOO SOON

编写软件和盖房子类似。这两者都需要做很多准备工作。没有坚固稳定的混凝土地基,盖房子不会成功。没有坚固稳定的需求和设计作为基础,编码也不会成功。想一想当地基已经浇筑完成之后,对房子做修改有多么困难

十六年后,作者复盘:

对于第5章中介绍的编码原则,全部依然有效。一些额外说明如下。

■ 对于原则 94,我认为现代软件工程师不需要再担心这个了。

■ 对于原则 96,这仍然是我的最爱之一。我一直在实践它,我的软件工程师同事认为我疯了。

第6章 测试原则

TESTINGPRINCIPLES

测试是包含以下行为的集合:1)对独立的软件组件执行测试(即单元测试,Unit Testing),以确保其行为与组件设计规格说明中的定义足够接近。2)对执行过单元测试的组件集合执行测试(即集成测试,IntegrationTesting),以确保这些组件一起工作时的行为足够接近设计规格说明中的定义。3)对集成测试过的所有组件进行测试(即软件系统级测试,Software Systems-level Testing),以确保它们可以作为一个系统来运行,且行为足够接近软件需求规格说明中的定义。4)制订软件系统级测试的测试计划。5)制订软件集成测试的测试计划。6)制订单元测试的测试计划。7)建立测试装置(test harness)和测试环境(test environment)。

原则107 依据需求跟踪测试

TRACE TESTS TO REQUIREMENTS

理解哪些测试可以验证哪些需求是很重要的。有如下两个原因:(1)在生成测试用例时,你会发现,了解是否所有需求都被测试用例所覆盖是很有用的。(2)在执行测试时,你会发现,了解正在验证哪些需求是很有用的。此外,如果你的需求已经被排了优先级(见原则50),那么可以很容易得出测试的相对优先级;也就是说,一个测试的优先级是其对应的所有需求优先级的最大值。

原则108 在测试之前早做测试计划

PLAN TEST LONG BEFORE IT IS TIME TOTEST

对于软件系统测试,测试计划人员应在设定需求基线之前,从可测试性的角度对软件需求规格说明进行评审,并向需求编写者提供反馈。在设定需求基线后不久,应开始认真的测试开发。对于集成测试,测试计划人员应在对初步设计确定基线之前对其进行评审。他们还应该向项目经理和设计人员提供以下反馈:(1)合理的资源分配,以确保“正确的”组件(从测试的角度来看)以正确的顺序生产;(2)对设计的修改,以使设计本质上更容易测试。对初步设计确定基线后不久,应开始认真的集成测试开发。对于单元测试,可以在完成详细设计后立即开始制订单元测试计划。

原则109 不要测试自己开发的软件

DON'T TEST YOUR OWN SOFTWARE

软件开发人员永远不应成为自己软件的主要测试者。开发人员比较适合进行初始调试(即自测)和单元测试。

原则110 不要为自己的软件做测试计划

DON'T WRITE YOUR OWN TEST PLANS

你不仅不应该测试自己的软件(见原则109),而且也不应该负责为软件生成测试数据、测试方案或测试计划。

原则111 测试只能揭示缺陷的存在

TESTING EXPOSES PRESENCE OF FLAWS

无论测试得多么彻底和深入,测试只能揭示程序中存在缺陷,而并不能确保程序没有缺陷。它可以增加你对程序正确性的信心,但它不能证明程序的正确性。为了获得真正的正确性,必须使用完全不同的方法,即正确性证明。

原则112 虽然大量的错误可证明软件毫无价值,但是零错误并不能说明软件的价值

THOUGH COPIOUS ERRORS GUARANTEEWORTHLESSNESS,ZERO ERROR SAYS NOTHING ABOUT THE VALUE OF SOFTWARE

原则113 成功的测试应发现错误

A SUCCESSFUL TEST FINDS AN ERROR

成功的测试是能够发现错误的测试。

原则114 半数的错误出现在15%的模块中

HALF THE ERRORS FOUND IN 15 PERCENT OFMODULES

保守估算,在大型系统中,大约一半的软件错误出现在15%的模块中,80%的软件错误出现在50%的模块中。

原则115 使用黑盒测试和白盒测试

USE BLACK-BOX AND WHITE-BOX TESTING

黑盒测试使用组件外部行为的定义作为其唯一输入。必须要确定,软件是否做了应做的事情,以及没有做不应该做的事情。白盒测试使用代码本身来生成测试用例。

原则116 测试用例应包含期望的结果

A TEST CASE INCLUDES EXPECTED RESULTS

一个测试用例的文档必须包含期望的正确结果的详细描述。如果这点被忽略,测试者无法判断软件是成功还是失败。

原则117 测试不正确的输入

TEST INVALID INPUTS

为尽可能多的可接受的输入情况生成测试用例,是自然和常见的做法。同样重要但是不太常见的是,为所有不正确或者非期望的输入生成大量的测试用例。

原则118 压力测试必不可少

ALWAYS STRESS TEST

原则119 大爆炸理论不适用

THE BIG BANG THEORY DOES NOT APPLY

在一个项目接近交付期限,而软件还没有准备好的时候,开发者往往充满绝望的情绪。假设排期要求两个月的单元测试时间,两个月的集成测试时间,以及两个月的系统测试时间。现在距离计划的交付日期还有一个月。假设50%的组件已经完成了单元测试。通过简单的计算可以得知,你落后进度5个月。你现在有两个选择:1)向你的客户承认5个月的延误:请求推迟交付。2)把所有组件集成到一起(包括50%尚未进行单元测试的组件),期盼有好的结果。

在第一种情况中,你可能是在过早地承认失败。在你的经理眼中,你可能在竭尽全力解决问题之前就放弃了。在第二种情况中,可能存在0.001%的机会,当你把所有组件集成在一起时,它能够正常运行并如期交付。项目经理往往屈服于后者,因为这看起来似乎他们在承认失败之前竭尽全力。不幸的是,这很可能会让你的排期再延长6个月。你不能通过忽略单元测试和集成测试来节省时间。

原则120 使用 McCabe 复杂度指标

USE MCCABE COMPLEXITY MEASURE

虽然有很多度量方法可以用来报告软件的内在复杂度,但是没有一个像Tom McCabe用于衡量测试复杂度的圈数法那样直观和易用。虽然不是绝对可靠,但是它可以相对一致地预测测试难度。

原则121 使用有效的测试完成度标准

USE EFFECTIVE TEST COMPLETION MEASURE

很多项目都在时间到期的时候宣布测试结束。这么做从政治角度来说是有意义的,但却是不负责任的。在做测试计划的时候,应该定义一个标准,以决定测试什么时候完成。如果在时间到期时没有达到目标,你仍然可以选择是交付产品还是错过里程碑,但至少你要清楚自己是否交付了一个高质量的产品。

原则122 达成有效的测试覆盖

ACHIEVE EFFECTIVE TEST COVERAGE

尽管测试不能证明正确性,但是做一个全面的测试还是很重要的。在测试计划生成或测试执行阶段,有一些指标可以用来确定代码执行测试的全面程度。这些指标易于使用,并且有工具用来监控测试覆盖水平。

原则123 不要在单元测试之前集成

DON'T INTEGRATE BEFORE UNIT TESTING

正常情况下,各个组件是被分别进行单元测试的。当它们通过各自的单元测试之后,一个单独的团队将它们集成到有意义的集合中,用以测试它们的接口。

原则124 测量你的软件

INSTRUMENT YOUR SOFTWARE

测试软件的时候,往往很难确定为何软件会失败。一个发现原因的方法是测量你的软件,也就是,将特殊的指令嵌入软件,用以报告执行轨迹、异常状况、过程调用等。当然,如果你的调试系统提供了这类能力,就不用手动测量了。

原则125 分析错误的原因

ANALYZE CAUSES FOR ERRORS

错误在软件中是很常见的,我们会花费大量的资源来发现和修复它们。从一开始就防止错误的发生,从而降低它们的影响,是更划算的。为此的一个方法是,当检测到错误的时候,分析错误的原因。错误的原因应该被告知给所有开发者,这么做是基于一个理念:如果一类错误的原因我们已经全面地分析和研究过了,那么就不那么容易再犯同类的错误了。

原则126 对“错”不对人

DON'T TAKE ERRORS PERSONALLY

编写软件需要的细节和完善程度,是任何人都无法达到的。我们应该致力于不断进步,而不是尽善尽美。当你或他人在你的代码中发现错误时,公开坦诚地讨论它。与其责骂自己,不如将它当作自己和他人的学习经历(见原则125)。

十六年后,作者复盘:

对于第6章中介绍的测试原则,全部依然有效。一些额外说明如下。

■ 对于原则 120,谨以此向我的朋友汤姆·麦克凯布致以崇高的敬意,我认为他的指标已经不再有用。我知道没有人再使用它了。对不起,汤姆。

■ 对于原则 124,所有现代测试工具都会自动执行此操作。

第7章

管理原则

MANAGEMENT PRINCIPLES

管理是围绕软件开发的所有工程活动,是进行计划(plan)、控制(control)、监视(monitor)和报告(report)的一组活动。

原则127 好的管理比好的技术更重要

GOOD MANAGEMENT IS MORE IMPORTANT THANGOOD TECHNOLOGY

好的管理能够激励人们做到最好,糟糕的管理会打击人们的积极性。所有伟大的技术(CASE工具、技术、计算机、文字处理器等)都弥补不了拙劣的管理。

原则128 使用恰当的方法

USE APPROPRIATE SOLUTIONS

技术问题需要使用技术的方法。管理问题需要使用管理的方法。政治问题需要使用政治的方法。切忌用不恰当的方法来解决问题。

原则129 不要相信你读到的一切

DON'T BELIVE EVERYTHING YOU READ

一般来讲,相信特定想法的人,会搜索支持这个想法的数据,而抛弃不支持的数据。要说服处于某个位置的某人,显然会使用支持的数据,而不是不支持的数据。

原则130 理解客户的优先级

UNDERSTAND THE CUSTOMERS' PRIORITIES

当你和客户沟通时,一定要确保你知道客户的优先级。这些可以很容易地记录在需求规格说明中(见原则50),但真正的挑战是,理解可能不断变化的优先级。此外,你必须理解客户对于“必要”(essential)、“期望”(desirable)和“可选”(optional)的说明。他们真的会对一个不满足任何期望和可选需求的系统感到满意吗?

原则131 人是成功的关键

PEOPLE ARE THE KEY TO SUCCESS

具备合适经验、才能、培训的能人,是在预算内按时完成满足用户需求的软件的关键。

原则132 几个好手要强过很多生手

A FEW GOOD PEOPLE ARE BETTER THAN MANYLESS SKILLED PEOPLE

原则133 倾听你的员工

LISTEN TO YOUR PEOPLE

你必须信任那些为你工作的人。如果他们不值得信赖(或者你不信任他们),你的项目将会失败。如果他们不信任你,你的项目也将会失败。你的员工很快就能看出你不信任他们,就跟你能很快发现你的老板不信任你一样。

信任的第一个原则就是倾听。

原则134 信任你的员工

TRUST YOUR PEOPLE

一般来讲,如果你信任你的员工,他们就是可信赖的。如果你不太信任他们,那么他们会给你不要去信任他们的理由。当你信任别人,而且也没有给他们理由不信任你时,他们也会信任你。相互信任是成功管理的要素。

原则135 期望优秀

EXPECT EXCELLENCE

如果你对员工有更高的期待,他们将表现得更好。

原则136 沟通技巧是必要的

COMMUNICATION SKILLS ARE ESSENTIAL

在为你的项目招募成员时,不要低估团队合作和沟通的重要性。如果他/她不能沟通、说服、倾听和妥协,最好的设计师也可能会变成差劲的资产。

原则137 端茶送水

CARRY THE WATER

当你的员工要工作很长时间来完成软件工程的工作时,你应该工作相同的时间。这样就树立了正确的榜样。如果你的员工知道你会和他们共同面对困境,那他们会愿意付出努力并且出色地完成工作。我的第一位工业界经理,汤姆林森·劳舍尔,正是这么做的。这对我们的态度的影响重大。在多次危机中,汤姆扮演“为他的员工效劳”的角色。这确实有效。

如果你不能帮助解决工作中的问题,可以让员工感受到你能够跑腿、订比萨、拿苏打水及任何他们需要的事情。给他们惊喜!在午夜给他们带比萨。

原则138 人们的动机是不同的

PEOPLE ARE MOTIVATED BY DEFFERENTTHINGS

有时要搞清激励个人时哪里用萝卜、哪里用大棒并不容易。众所周知,人各不同,负面或正面的激励都可能起作用,但是正面的激励经常被管理层忽视。要找出激励个人的因素,一个比较好的方法就是倾听(见原则133)。剩下的可能就是反复试验,不过不论你做什么,不要因为害怕选错而减少奖励。

原则139 让办公室保持安静

KEEP THE OFFICE QUIET

最有效率的员工和公司都拥有安静和私密的办公区。他们把电话静音或者设置呼叫转移。他们隔绝于日常的、非工作事项的干扰。

则140 人和时间是不可互换的

PEOPLE AND TIME ARE NOTINTERCHANGEABLE

只用“人月”来衡量一个项目几乎没有任何意义。如果一个项目能够由6个人在1年内完成,是不是意味着72个人能在一个月内完成呢?当然不是!

原则141 软件工程师之间存在巨大的差异

THERE ARE HUGE DIFFERENCES AMONGSOFTWARE ENGINEERS

从最好的软件工程师到最差的软件工程师,研发效率(按每人每月完成的代码行来衡量)可能相差25倍之多,质量(按每千行代码中发现的错误量来衡量)可能相差10倍之多。

原则142 你可以优化任何你想要优化的

YOU CAN OPTIMIZE WHATEVER YOU WANT

在产品开发过程中,有很多选择—不同的权衡—不得以而进行取舍。与你的员工一起工作,并帮助他们了解你和你的用户的优先级。

原则143 隐蔽地收集数据

COLLECT DATA UNOBTRUSIVELY

收集数据的最佳方法是自动进行,这样开发人员不会感到被干扰。显然,你不能在所有时间对所有数据都这么做,但是应尽可能自动地执行数据收集。

原则144 每行代码的成本是没用的

COST PER LINE OF CODE IS NOT USEFUL

使用高级语言时,总开发成本将大大降低。但是,由于软件开发的固定成本(例如,用户文档、需求和设计),如果我们选择高级语言,每行代码的成本实际上会增加!

原则145 衡量开发效率没有完美的方法

THERE IS NO PERFECT WAY TO MEASURE PRODUCTIVITY

接受这个事实:十全十美是不可能的。使用开发效率度量和成本估算模型来确认你的直觉和你的亲身经验。永远不要将它们作为唯一的衡量方法

原则146 剪裁成本估算方法

TAILOR COST ESTIMATION METHODS

许多成本估算方法可花钱获得。每种方法都基于从大量已完成项目中收集的数据。这些方法中的任何一种都可以用于为软件开发生成大致的估算。要使用它们生成更准确的估算,必须针对工作环境进行剪裁。这种剪裁使模型适应你的人员和应用类型,消除在你的环境中不变的变量,增加在你的环境中影响开发效率的变量

原则147 不要设定不切实际的截止时间

DON'T SET UNREALISTIC DEADLINES

不可避免的结局是,一个不切实际的截止日期将无法保证完成任务。设立这样的截止日期会削弱士气,将使你的员工不信任你,造成高离职率,还会产生其他不良影响。这些因素使不切实际的截止日期更加无法实现。

原则148 避免不可能

AVOID THE IMPOSSIBLE

这似乎是显而易见的建议。另外,许多项目承诺按时交付产品,这是100%不可能的。

原则149 评估之前先要了解

KNOW BEFORE YOU COUNT

“在你可以评估任何事情之前,你必须先了解一些东西”。

原则150 收集生产力数据

COLLECT PRODUCTIVITY DATA

所有成本估算模型的准确性都取决于这些模型为你的工作场景进行的剪裁。但是,如果你尚未从过去的项目中收集详细的数据,那么今天你就无法剪裁你的成本估算模型。因此,现在你有一个很好的借口不进行准确的成本估算。但是明天呢?如果你今天不开始收集详细数据,那么你将来也无法剪裁成本估算模型。那你还在等什么?还请记住Manny Lehman的建议:少量经过充分理解、认真收集、模型化及演绎的数据,要好于大量没有这些特性的数据。

原则151 不要忘记团队效率

DON' T FORGET TEAM PRODUCTIVITY

优化所有个体的生产力并不一定会产出最佳的团队生产力。

原则152 LOC/PM与语言无关

LOC/PM INDEPENDENT OF LANGUAGE

在启动项目时,你需要对程序员将使用的语言有所了解。这是必需的,以便你可以估算代码行数。代码行数(LOC)又可以用来计算项目的工作量(PM 人月)和工期。

原则153 相信排期

BELIEVE THE SCHEDULE

一旦建立了可行的排期(见原则146、147和148)并分配了适当的资源(见原则157),所有各方都必须相信排期。

原则154 精确的成本估算并不是万无一失的

A PRECISION-CRAFTED COST ESTIMATE ISNOT FOOLPROOF

原则155 定期重新评估排期

REASSESS SCHEDULES REGULARLY

原则156 轻微的低估不总是坏事

MINOR UNDERESTIMATES ARE NOT ALWAYSBAD

原则157 分配合适的资源

ALLOCATE APPROPRIATE RESOURCES

不管人员的质量如何,工具、语言或流程的可用性如何,人为强加的进度和不恰当的预算将会毁了一个项目。

原则158 制订详细的项目计划

PLAN A PROJECT IN DETAIL

一个没有计划的项目,在它开始之前就已经失控了。正如《爱丽丝梦游仙境》中柴郡猫对爱丽丝所说:“如果你不知道要去哪里,那你也就无法到达那里!”

原则159 及时更新你的计划

KEEP YOUR PLAN UP-TO-DATE

原则160 避免驻波

AVOID STANDING WAVES

遵循原则159(保持你的计划是最新的)的一个奇怪的副作用是驻波。在这种情况下,你总是计划“在未来几周内”可以“康复”的策略。由于落后于计划的项目往往会进一步落后于计划,因此这种“康复”的策略“在未来几周内”将会需要越来越多的资源(或奇迹)。如果不采取纠正措施,波动会变得越来越大。一般来说,重新安排时间和重新计划需要采取行动,而不仅仅是承诺很快就能解决问题。不要因为你只是落后了几天,就认为问题会消失。所有的项目都是“一天一天地落后”的。

原则161 知晓十大风险

KNOW THE TOP 10 RISKS

作为项目经理,当你开始一个项目时,你需要熟悉最经常导致软件灾难的情况。这些是你最可能遇到的风险,但很可能不是全部。根据Boehm的说法,它们是:

■ 人员短缺(见原则131)。

■ 不切实际的排期(见原则148)。

■ 不理解需求(见原则40)。

■ 开发糟糕的用户界面(见原则42)。

■ 当用户并不需要时尝试镀金(见原则67)。

■ 没有控制需求变更(见原则179和189)。

■ 缺乏可重用的或者接口化的组件。

■ 外部执行任务不满足要求。

■ 糟糕的响应时间。

■ 试图超越当前计算机技术的能力。

原则162 预先了解风险

UNDERSTAND RISKS UP FRONT

在任何软件项目中,都无法准确预测会出现什么问题。然而,总有地方会出现问题。在项目计划的早期阶段,要梳理与你的项目相关的最大风险列表。对于每个风险,要量化其真正发生时会带来的破坏程度,并量化这种损失发生的可能性。这两个数字的乘积,是你对特定风险的“风险敞口”。

原则163 使用适当的流程模型

USE AN APPROPRIATE PROCESS MODEL

要研究你的项目的特点,然后选择一个最适合的流程模型。例如,在构建原型时,你应该遵循最小化规约、促进快速开发和不需要担心分权制衡的流程。而在构建性命攸关的产品时,情况恰恰相反。

原则164 方法无法挽救你

THE METHOD WON'T SAVE YOU

作为一名管理者,要提防那些声称基于新的方法将大大提高质量或生产力的虚假的预言家。采用新的方法并没有错,但一个公司如果过去“失败”过(不论是在生产力还是在质量方面),在寻找解决方案之前,请尝试找出失败的根源。你现在使用的方法,很可能不是问题的根源!

原则165 没有奇迹般提升效率的秘密

NO SECRETS FOR MIRACULOUS PRODUCTIVITYINCREASE

你应该会对可削减几个百分点成本或提高几个百分点质量的工具、语言和方法感到满意。然而,如果不了解对客户满意度的影响,降低成本毫无意义。

原则166 了解进度的含义

KNOW WHAT PROGRESS MEANS

我经常听到项目经理报告,“我们比预算低25%”,或者“比排期提前25%”。两者都未必是好消息。“低于预算”通常意味着比预期花费更少的钱。这可能是好事,但是也不一定,除非工作也按期或比排期提前。相似地,“比排期提前”通常表示你比预期完成的工作多。这可能是好消息,但是也不一定,除非费用还符合或低于预算。

原则167 按差异管理

MANAGE BY VARIANCE

首先,没有详细的计划是不可能管理一个项目的(见原则158)。一旦你有了计划,就要在必要时更新它(见原则159)。既然有了最新的计划,你的责任就是根据这个计划来管理项目。

原则168 不要过度使用你的硬件

DON'T OVERSTRAIN YOUR HARDWARE

要注意硬件限制对软件开发成本的巨大影响。

原则169 对硬件的演化要乐观

BE OPTIMISTIC ABOUT HARDWARE EVOLUTION

原则170 对软件的进化要悲观

BE PESSIMISTIC ABOUT SOFTWAREEVOLUTION

原则171 认为灾难是不可能的想法往往导致灾难

THE THOUGHT THAT DISASTER ISIMPOSSIBLE OFTEN LEADS TO DISASTER

原则172 做项目总结

DO A PROJECT POSTMORTEM

每个项目都会有问题。原则125涉及记录、分析技术错误并从中学习。本原则用于对管理错误或者整体的技术错误进行同样的操作。在每个项目结束时,给所有的项目关键参与者3~4天的时间来分析项目中出现的每一个问题。例如,“我们延迟了10天开始集成测试;我们应该告诉客户”。或者,“我们早在知道最基本的需求之前就开始了设计”。或者,“大老板在错误的时间发布了一个'不加薪’的公告,影响了大家的积极性”。总的来说,主要思路是记录、分析所有不符合预期的事情并从中学习。同时,记录下你认为将来可以采取的预防问题发生的不同措施。未来的项目将会极大受益。

十六年后,作者复盘:

对于第7章中介绍的管理原则,全部依然有效。一些额外说明如下。

■ 对于原则 129、131、132、133、134、135、137、138、140、142 和 147,如果你不相信这些,请不要成为经理。

■ 对于原则 168,对当今的大多数应用程序来说不是一个真正的问题。

第8章 产品保证原则

PRODUCT ASSURANCE PRINCIPLES

产品保证是通过使用分权制衡(checks and balances)来确保软件质量的一系列工作。产品保证通常包括如下几项:1)软件配置管理(Software configuration management):是管理软件变更的过程;2)软件质量保证(Software quality assurance):是检查所有做法和产品是否符合既定流程和标准的过程;3)软件验证和确认(Software verification andvalidation):这个过程用于验证(verify)每个中间产品是否正确地建立在以前的中间产品的基础上,以及确认(validate)每个中间产品是否适当地满足客户的要求;4)测试(Testing):在前面的章节已介绍过。

原则173 产品保证并不是奢侈品

PRODUCT ASSURANCE IS NOT A LUXURY

产品保证包含软件配置管理(software configuration management),软件质量保证(softwarequality assurance),验证和确认(verification and validation),以及测试(testing)。在以上四点中,测试和评估的必要性是最常被承认的,哪怕预算不足。另外三点则经常作为奢侈品而被摒弃掉,如同它们只是大型项目或者昂贵项目的一部分。这些准则的分权制衡,提供了明显更高的可能性,以生产出满足客户期望的产品,并在更接近排期和成本目标的情况下完成任务。关键是要根据项目的规模、形式和内容去定制产品保证的准则。

原则174 尽早建立软件配置管理过程

ESTABLISH SCM PROCEDURES EARLY

有效的软件配置管理(SCM,Software Configuration Management)不仅仅是一个记录谁在什么时候对代码和文档进行了怎样修改的工具。它还包括深思熟虑地创建命名约定、策略和过程,以确保所有相关方都能参与软件的更改。

原则175 使软件配置管理适应软件过程

ADAPT SCM TO SOFTWARE PROCESS

软件配置管理(SCM)并不是一套对所有项目一律适用的标准实践。SCM 必须根据每个项目的特点去定制:项目规模、易变性、开发过程、客户参与度等。不是所有情况都适用同一模式。

原则176 组织SCM独立于项目管理

ORGANIZE SCM TO BE INDEPENDENT OFPROJECT MANAGEMENT

软件配置管理(SCM)只有在独立于项目管理的情况下才能做好本职工作。

原则177 轮换人员到产品保证组织

ROTATE PEOPLE THROUGH PRODUCTASSURANCE

在很多组织中,在以下情况下人员会被转到产品保证组织:1)作为他们被分配的第一个工作;2)当他们在工程软件方面表现不佳时。然而,产品保证工作对于工程的质量和专业水平,与设计和编码工作有同等的要求。另一种选择是,轮换最好的工程人才到产品保证组织工作。一个好的指导方针可能是,每一个优秀的工程师每隔两到三年,要投入六个月的时间到产品保证上。所有这些工程师的期望是,他们可以在“访问”期间对产品保证做出重大改进。这样的政策必须明确说明,工作轮换是对表现优异的一种奖励。

原则178 给所有中间产品一个名称和版本

GIVE ALL INTERMEDIATE PRODUCTS A NAMEAND VERSION

软件开发过程中会有许多中间产出:需求规格说明、设计规格说明、代码、测试计划、管理计划、用户手册等。每个这样的产出都应该有唯一的名称、版本/修订号和创建日期。

原则179 控制基准

CONTROL BASELINES

软件配置管理(SCM)的职责,是保持商定的规格并控制对其的变更。

原则180 保存所有内容

SAVE EVERYTHING

软件从本质上来说是不断被修补的。由于修补会导致很多错误(见原则195),所以任何软件更改都很可能需要被回滚。做到这一点的唯一方法是确保在进行更改之前保存所有内容。

原则181 跟踪每一个变更

KEEP TRACK OF EVERY CHANGE

原则182 不要绕过变更控制

DON'T BYPASS CHANGE CONTROL

变更得到控制,每个人都会收益。(“控制”并不意味着“阻止”。)

原则183 对变更请求进行分级和排期

RANK AND SCHEDULE CHANGE REQUESTS

对于任何投入使用的产品,来自用户、开发人员和市场营销人员的变更请求都会集中到开发团队。这些变更请求可能反映了对新功能的需求、性能下降的报告或对系统错误的投诉。应该成立一个委员会,通常称为配置控制委员会(CCB,Configuration Control Board),以定期评审所有变更请求。委员会的职责是将所有这些变更进行优先级排序,并安排何时(或至少确定将在哪个即将来临的发布版本中)它们将会被解决。

原则184 在大型开发项目中使用确认和验证(V&V)

USE VALIDATION AND VERIFICATION (V&V) ON LARGE DEVELOPMENTS

开发大型软件系统需要尽可能多的制衡,以确保产品的质量。一种行之有效的技术是,让独立于开发团队的组织来进行确认和验证(V&V)。确认(Validation)是检查软件开发的每个中间产品以确保其符合之前产品的过程。

十六年后,作者复盘:

对于第8章中介绍的产品保证原则,全部依然有效。一些额外说明:

■ 我最近托运了一辆自行车,几乎横跨了整个国家,当收到时,货物箱子损坏而且很多零件不见了。我联系制造商购买缺少的零件,他们告诉我,每辆自行车都不同,虽然我有型号,但每辆自行车的每个实例都是不同的。不仅如此,他们也没有记录每辆运输的自行车组装了哪些零件,因此他们不可能将丢失的零件寄给我。我很震惊。软件配置管理(如今,通常称为版本控制)应该跟踪每个组件的每个版本,以及组件版本的哪些组合构成了可行的系统,以及哪些客户拥有哪些可行的版本。我认为这应该是一个新的原则。

■ 对于原则 176 和 177,可能不再那么重要了。

第9章 演变原则

EVOLUTION PRINCIPLES

演变是与修改软件产品相关的一系列工作,用于:1)满足新功能;2)更有效地运行;3)正常运行(当检测到原始产品中的错误时)。

原则185 软件会持续变化

SOFTWARE WILL CONTINUE TO CHANGE

任何正在使用的大型软件系统都将经历不断的变化,因为系统的使用会使人想出新的功能。它会一直变化,直到从头开始重写变得更划算。

原则186 软件的熵增加

SOFTWARE'S ENTROPY INCREASES

任何经历持续变化的软件系统都会变得越来越复杂,并且变得越来越杂乱无章。

原则187 如果没有坏,就不要修理它

IF IT AIN'T BROKE,DON'T FIX IT

当然,这个建议适用于生活中的许多方面,但它特别适用于软件。就像其名字那样,软件被认为是可塑的、易于修改的。不要误以为软件中的“失灵”很容易发现或修复。

原则188 解决问题,而不是症状

FIX PROBLEMS,NOT SYMPTOMS

当软件出错时,你的责任是彻底理解错误产生的原因,而不只是草草分析一下,并对你认为的原因进行一个快速的修复。

原则189 先变更需求

CHANGE REQUIREMENTS FIRST

如果各方都同意对软件进行增强,那么第一件事就是更新软件需求规格说明(SRS),并获得批准。只有这样,才比较有可能让客户、市场营销人员和开发人员对变更内容达成一致。有时由于时间限制无法做到这一点(这种情况不应该一直存在,否则管理层需要阅读本书中的管理原则)。在这种情况下,请先对SRS进行更改,然后再开始对设计和代码进行更改,并且在完成设计和代码的修改之前,批准对SRS的变更。

原则190 发布之前的错误也会在发布之后出现

PRERELEASE ERRORS YIELD POSTRELEASEERRORS

发布之前错误就比较多的组件,发布之后也会被发现会有比较多的错误。这对开发者来说是一个令人失望的消息,但确实是被经验数据所充分支持的(而且由原则114可知,假如你在一个组件中发现了很多错误,那将来还会发现更多错误)。最好的建议是废弃、替换、从头创建任何具有不良历史记录的组件。不要花钱填坑。

原则191 一个程序越老,维护起来越困难

THE OLDER A PROGRAM,THE MORE DIFFICULTIT IS TO MAINTAIN

在对软件系统进行更改(无论是维修还是增强)时,系统中必定有一些组件要被修改。随着程序变“老”,每次改动时,整个系统中需要修改的组件的比例就会随之增加。每次更改都会使所有后续的更改更加困难,因为程序的结构必然会恶化。

原则192 语言影响可维护性

LANGUAGE AFFECTS MAINTAINABILITY

如今关于重建(reengineering)、翻新(renovation)和逆向工程(reverse engineering)的讨论太多了,我们可能都开始相信这样做很容易。其实这很难做。有时这很有意义,值得投资。但有时这是对珍稀资源的浪费,从头开始设计和编码可能是更好的选择。举例来说,扪心自问,如果你制作了设计文档,维护者们真的会使用它们吗?

原则194 首先翻新最差的

RENOVATE THE WORST FIRST

原则193建议,重新开始有时可能是更好的选择。另一个不那么痛苦的方法是,完全重新设计和重新编码“最差”的组件。这里“最差”的组件是指那些消耗了最多改正性维护费用的组件。Gerald Weinberg曾说,在一个系统中重写一个800行的模块(占全部改正性维护成本的 30%),就可以为整体维护工作节省大量的资源。

原则195 维护阶段比开发阶段产生的错误更多

MAINTENANCE CAUSES MORE ERRORS THANDEVELOPMENT

维护期间对程序的修改(无论是改进功能还是修正缺陷)引入的错误远远超过最初的开发阶段。维护团队报告说,维护期间有20%到50%的改动会引入更多的错误。

出于这个原因,遵守“规则”更显重要:制定SCM计划(见原则174),控制基准(见原则179),并且不要绕过变更控制(见原则182)。

原则196 每次变更后都要进行回归测试

REGRESSION TEST AFTER EVERY CHANGE

归测试,是在变更发生后,对所有先前已测试过的功能进行的测试。大多数人绕过回归测试,因为他们认为自己的变更是没有影响的。

原则197 “变更很容易”的想法,会使变更更容易出错

BELIEF THAT A CHANGE IS EASY MAKES ITLIKELY IT WILL BE MADE INCORRECTLY

原则198 对非结构化代码进行结构化改造,并不一定会使它更好

STRUCTURING UNSTRUCTURED CODE DOES NOTNECESSARILY IMPROVE IT

原则199 在优化前先进行性能分析

USE PROFILER BEFORE OPTIMIZING

当需要优化程序以使其更快时,请记住80%的CPU周期将被20%的代码消耗(Pareto定律)。

原则200 保持熟悉

CONSERVE FAMILIARITY

保持产品发布版本之间的改动量相对稳定。

原则201 系统的存在促进了演变

THE SYSTEM'S EXISTENCE PROMOTESEVOLUTION

论你认为自己多么完美地实现了需求,都必须为部署之后必要的变更做好计划。

十六年后,作者复盘:

对于第9章中介绍的演变原则,全部依然有效

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多