软件开发的201个罪恶之源

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件开发的201个罪恶之源相关的知识,希望对你有一定的参考价值。

在过去的将近两年的时间里,我阅读了好多关于开发/编程相关的书,其中《软件开发的201个原则》这本书是是我最喜欢的书籍之一,它是由计算机科学家 Alan M· Davis(伊利诺伊大学厄巴纳-香槟分校计算机科学博士)在1995年所著。以我过去多年的开发工作经验来看,该书中提到的很多原则都十分符合实际的开发场景且都切身体会过,这让我有一种似曾相识又相见恨晚的感觉。

当你阅读完这些原则之后,你会惊奇的发现,即便距离该书出版至今已经过去了将近30年的时间,但该书中超过 95% 的原则至今仍然十分受用,它们就像物理学中的基本法则一样,经受住了时间的考验,这是一个不变的事实。我想这也是为什么很多人会推荐该书的原因。

另外,我感觉这本书不仅仅适用于软件开发的程序猿们,它同样非常适合那些几乎不怎么写代码的以下人员:

  • 产品经理
  • 项目经理
  • 测试人员
  • 开发组长
  • TeamLeader
  • 技术经理
  • 其他任何在公司中拥有对产品和软件开发的决策权及管理权的领导者

因为这些原则涉及到了整个软件开发生命周期中的各个阶段,而其中的很多原则其实讨论的是关于产品、用户、开发者三者之间的需求关系,所以基本上与整个软件的开发、管理、维护相关的人员都有必要了解这些原则。并且其中很多的原则的最终实现可能需要在公司企业中拥有决策权的那些人做出决定而不是程序猿。

大家都知道在一个软件公司中,处于最底层干活的往往是日以继夜加班的程序猿们,作为一枚程序猿,该书列出的大多数原则其实大家都已经深有体会,只是在很多情况下都敢怒不敢言,或者自己没有话语权,说了也没有什么实际的效果,因为自己本身就不是整个产品体系决策层的参与者,而只是最终负责搬砖的那个人。

所以我其实更加希望前面提到那些人能够好好的学习并理解这本书中所提到的所有原则,这样程序猿们的日子或许会好过一些。

什么是编程原则?

  1. 原则在技术和工具的支持下落地。
  2. 技术使用语言,并得到工具的支持。
  3. 语言得到工具的支持。

语言(Language)由一组基本元素(如单词或图形符号)、规则和语义组成。规则可以让人们用基本元素构造出更复杂的实体(如句子、图表、模型),语义则赋予每个实体组合以意义。语言用于表达所有软件工程的产出,无论是过程中的还是最终的。那些通过技术创建或分析的文档和程序通常也会用某种语言来表达。

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

  • 作为工程师的顾问(例如,基于知识的需求助理)。
  • 分析某些内容是否符合某种技术(例如,数据流图检查器)或原则的子集。
  • 使软件工程中的一些工作实现自动化(例如,编译器)。
  • 辅助工程师完成一些工作(例如,编辑器)。

一个学科的原则集合,会随着学科的发展而发展。现存的原则会发生改变,新的原则会被加进来,旧的原则也许将不再适用。实践和从实践中获得的经验,促使我们发展了那些原则。

下面,请欣赏来自这本书的 201 个软件开发的 “罪恶之源”。

注意,这个词儿只是我个人喜欢的一个叫法而已,因为在我看来,这些原则所描述的问题的反例也正是软件开发中产生问题的罪恶之源。

第一部分:面向用户的罪恶之源

原则1:质量第一

无论如何定义质量,客户都不会容忍低质量的产品。质量必须被量化,并建立可落地实施的机制,以促进和激励质量目标的达成。即使质量没达到要求,也要按时交付产品,这似乎是政治正确的行为,但这是短视的。从中长期来看,这样做是自杀。质量必须被放在首位,没有可商量的余地。Edward Yourdon建议,当你被要求加快测试、忽视剩余的少量bug、在设计或需求达成一致前就开始编码时,要直接说“不”。

Yourdon,E.,Decline and Fall of the American Programmer,Englewood Cliffs,N.J.: Prentice Hall,1992(Chapter 8).

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

软件质量没有唯一的定义。对开发者来说,质量可能是优雅的设计或优雅的代码。对在紧张环境中工作的客户来说,质量可能是响应时间或高吞吐量。对成本敏感的项目来说,质量可能是低开发成本。对一些客户来说,质量可能是满足他们所有已知和未知的需求。这里的难题是,以上要求可能无法完全兼顾。当优化某人关注的质量可能会影响其他人关注的质量(这就是温伯格的“政治困境”原则) 。项目必须确定各因素的优先级,并清晰地传达给所有相关方。

Weinberg,G.,Quality Software Management,Vol.1: Systems Thinking,New York: Dorset House,1992,Section 1.2.

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

开发效率与质量之间存在明显的关系(开发效率可以用人月完成的代码行数或功能点数来度量)。对质量要求越高,开发效率就越低。对质量要求越低,开发效率就越高。越是强调提高开发效率,最终的质量就越低。贝尔实验室发现,在要求每千行代码有1~2个bug时,人月的效率通常为150~300行代码 [参见 Fleckenstein,W.,“Challenges in Software Development”,IEEE Computer,16,3 (March 1983),pp.60-64]。当试图提高开发效率时,bug的密度就会增加。

Lehman,M.,“Programming Productivity—A life Cycle Concept,” COMPCON81,Washington,D.C.: IEEE Computer Society Press,1981,Session 1.1.

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

尽管我们的行业中有一些表现不佳、包含bug,或者根本无法满足客户需求的软件系统的例子,但仍然有一些成功的例子。大型软件系统可以以非常高的质量构建,但价格昂贵: 每行代码高达1000美元。例如,IBM为美国宇航局的航天飞机开发的机载飞行软件,总共约300万行代码,源于严谨的软件开发过程,产品发布后每万行代码中发现的错误少于一个。

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

Joyce,E.,“Is Error-Free Software Achievable?” Datamation (February 15,1989).

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

质量无法通过软件的改进来获得。这适用于质量的任何定义:可维护性、可靠性、适应性、可测试性、安全性等。即使我们在开发过程中十分努力,使软件具备高质量也是十分不易的。如果我们不努力,又怎么可能期望获得高质量呢?这就是绝不能将 “一次性原型” 转换成产品的主要原因(见原则11)。

Floyd C.,“A Systematic Look at Prototyping,” in Approaches to Prototyping,R.Budde,et al.,Berlin,Germany: Springer Verlag,1983,pp.1-18,Session 3.1.

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

如果软件执行效率不高,通常可以分离出消耗大部分执行时间的程序单元,重新设计或编码以提高效率(见原则194)。低可靠性问题不仅难以发现,而且难以修复。系统的低可靠性问题可能会在系统上线多年后才暴露出来——甚至可造成人员伤害。一旦低可靠性问题显现,通常难以隔离其影响。

Sommerville,I.,Software Eginnering,Reading,Mass.: Addision Wesley,1992,Session 20.0.

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

在需求阶段,无论你多么努力地试图去了解客户的需求,都不如给他们一个产品,让他们使用它,这是确定他们真实需求的最有效方法。如果遵循传统的瀑布式开发模型,那么在99% 的开发资源已经耗尽之后,才会第一次向客户交付产品。如此一来,大部分的客户需求反馈将发生在资源耗尽之后。

和以上方法相反,可在开发过程的早期构建一个快速而粗糙的原型。将这个原型交付给客户,收集反馈,然后编写需求规格说明并进行正规的开发。使用这种方法,当客户体验到产品的第一个版本时,只消耗了 5%~20% 的开发资源。如果原型包含合适的功能,就可以更好地理解和把握最有风险的客户需求,最终产品也就更有可能让客户满意。这有助于确保将剩余的资源用于开发正确的系统。

Gomaa,H.,and D.Scott,“Prototyping as a Tool in the Specification of User Requirements,” Fifth International Conference on Software Engineering,Washington,D.C.: IEEE Computer Society Press,1981,pp.333-342.

原则8:与客户/用户沟通

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

如果你是一个商业开发人员,那么应经常和客户交谈,让他们参与进来。当然,闭门造车式的开发更容易,但是客户会喜欢这样的结果吗?如果你是软件外包的生产商,在开发过程中很难找到“客户”,那就进行角色扮演。在你的组织中指定3~4人作为潜在的客户,征求他们的意见:如何能让他们持续成为客户,并使他们满意。如果你是政府项目的承包商,要经常与签约官员、技术代表以及(如果可能的话)产品的客户交谈。政府里的人和事经常会发生变化,跟上变化的唯一方法就是沟通。忽视上述变化可能在短期内会让生活看起来更容易,但最终的系统将无法使用。

Farbman,D.,“Myths That Miss,” Datamation (November 1980),pp.109-112.

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

项目经常会因为客户和开发人员的目标不同(或不兼容)而失败。一个简单的案例是,客户希望在特定日期前获得特性1、2、3,而开发人员希望最大化营收或利润。为了最大化营收,开发人员可能会尝试完整地开发这三个特性,即使会导致项目延期。与此同时,客户可能宁愿放弃其中一个特性的一部分功能,只要能按时交付其他特性。

为使双方的目标达成一致,有如下方法:
(1) 按优先级对需求排序(见原则50),以便开发人员了解它们的相对重要性。
(2) 根据需求的优先级奖励开发人员(例如,所有高优先级的需求必须完成;每完成一个中优先级的需求,开发人员可获得一些额外的小奖励;每完成一个低优先级的需求,可获得的奖励非常小)。
(3) 对逾期交付实行严厉的处罚。

原则10:做好抛弃的准备

对一个项目来说,最关键的成功因素之一,是它所涉及的领域是否是全新的。在全新领域(可能涉及应用程序、体系结构、接口、算法等)研发的程序很少能第一次就成功。弗雷德·布鲁克斯(Fred Brooks)在《人月神话》中明确建议:“无论如何,你一定要做好抛弃的准备。”这个建议最初由温斯顿·罗伊斯(Winston Royce)在1970年提出,他说一个人应该做好准备—第一个被完整部署的系统,往往是第二个被创建的系统。第一个系统至少可用于验证关键的设计问题和操作概念。此外,罗伊斯建议,应该使用大约25%的资源开发这样的预发布版本。

作为一个全新定制产品的开发人员,在开始全面开发之前,要规划开发一系列“一次性原型”(见原则11、12和13)。作为商用大规模系统的开发人员,可以预期第一个版本的产品在一定年限内将能够被修改,之后它将被完全替换(见原则185、186、188和201)。作为产品的维护者,请注意,在程序变得不稳定以至于必须被替换之前,你对程序可以调整的地方还有很多(请参阅相关原则186、191、195和197)。

Royce,W.,“Managing the Development of Large Software System,” WESCON′ 70,1970; reprinted in 9th International Conference on Software Engineering,Washington D.C.: IEEE Conputer Society Press,1987,pp.328-338.

原则11:开发正确的原型

有两种原型:一次性(throwaway)原型和演进式(evolutionary)原型。一次性原型用快速而粗糙的方式构建,交给客户用以获得反馈,在得到期待的信息后即被废弃。获得的信息被整理进需求规格说明,用于正规的产品开发。演进式原型用高质量的方式构建,交给客户用以获得反馈,获得期待的信息便开始进行修改,以更加贴近客户的需求。重复此过程,直到产品收敛到所期望的样子。

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

Davis,A.,“Operational Prototyping: A New Development Approach,” IEEE Software,9,5 (September 1992),PP.70-78.

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

当建立一次性原型时,只需要开发那些没有被充分理解的特性。如果你开发已充分理解的特性,最终除了浪费资源,将一无所获。当建立演进式原型时(见原则13),要优先开发那些已经被充分理解的特性。(注意,它们可能已经被充分理解,因为之前已使用一次性原型进行过验证。)你的希望是,通过体验这些特性,用户能更好地确定其他需求。如果基于模糊的需求(高质量地)开发了一个演进式原型,一旦需求搞错了,你将不得不抛弃这个“高质量”的软件,从而浪费了资源。

Davis,A.,“Operational Prototyping: A New Development Approach,” IEEE Software,9,5 (September 1992),PP.70-78.

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

如果你已经决定开发一次性原型,那么就要用最快的方式。不用担心质量。可使用“一页纸”的需求规格说明。不用担心设计或编码中的文档。可以使用任何工具。可以使用任何编程语言,只要能够方便程序的快速开发。不用担心编程语言的可维护性。

Andriole,S.,Rapid Application Prototyping,Wellesley,Mass.: QED,1992.

原则14:渐进地扩展系统

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

这样做的好处是:(1)降低每次开发的风险;(2)看到一个产品版本,通常可以帮助用户想象出他们想要的其他功能。

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

Mills,H.,“Top-Down Programming in Large Systems,” in Debugging Techniques in Large Systems,R.Ruskin,ed.,Englewood Cliffs,N.J.: Prentice Hall,1971.

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

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

这意味着,所产生的每个文档都应该以有利于更改的方式进行存储和组织。这意味着,配置管理流程(见原则174)必须在距离交付很长时间之前就就位。这也意味着,在软件部署后不久,你就应该准备好,以应对用户口头或书面请求的冲击。这还意味着,你选择的设计方案应使容量、输入速率和功能都很容易更。

Curtis,B.,H.Krasner,and N.Iscoe,“A Field Study of the Software Design Process for Large Systems,” Communications of the ACM,31,11 (November 1988),pp.1268-1287.

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

爱德华·伯索夫(Edward Bersoff)等人将系统工程的第一定律定义为:无论你处在系统(开发)生命周期中的何处,系统都将发生变化,并且对其进行改变的愿望将在整个生命周期中持续存在。与原则185和201(强调软件部署后,需求可能发生巨大变化)不同,本原则想表达,在开发过程中,软件也可能发生巨大变化。这些变化可能体现在编写新的代码、新的测试计划或新的需求规格说明上。这些变化可能意味着,要去修复某个被发现是不正确的中间产品。或者它们可能反映了完善或改进产品的自然过程。

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

Bersoff,E.,V.Henderson,and S.Siegel,Software Configuration Management,Englewood Cliffs,N.J.: Prentice Hall,1980,Section 2.2.

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

要降低不断上涨的软件开发成本和风险,最有效的方法就是,购买现成的软件,而不是自己从头开发。确实,现成的软件也许只能解决75% 的问题。但考虑一下从头开发的选择吧:支付至少10倍于购买软件的费用,且要冒着超出预算100% 且延期的风险(如果最后能够完成!),并且最终发现,它只能满足75% 的预期。

对一个客户来说,新的软件开发项目似乎最初总是令人兴奋的。开发团队也是“乐观的”,对“最终”解决方案充满了希望。但几乎很少有软件开发项目能够顺利运行。不断增加的成本通常会导致需求被缩减,最终研发出的软件可以满足的需求也许跟现成的软件差不多。作为一个开发者,应该复用尽可能多的软件。复用是“购买而非开发”原则在较小范围内的体现。可参考原则84。

Brooks,F.,“No Silver Bullet: Essence and Accidents of Software Engineering,” IEEE Computer,20,4 (April 1987),pp.10-19.

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

衡量软件系统质量的一种方法是查看其用户手册内容的多少。手册中的内容越少,软件质量越好。设计良好的软件,用法应该不言而喻。不幸的是,太多的软件设计师将自己塑造成人机界面设计专家。而大量的用户手册内容充分证明,大多数界面设计师并不像他们宣称的那样出色。(顺便说一句,当我说“用户手册”时,也包括在线帮助文档。因此,把用户手册发布到网上,软件并不会在一夜之间突然变得更好。)

应使用标准的界面。让行业专家设计浅显易懂的图标、命令、协议和用户场景。要记住:软件开发人员“喜欢”某种用户界面,并不意味着你的用户就会知道怎么使用它。许多软件开发人员喜欢带有内置技巧(可作为快捷方式)的用户界面。通常,用户需要简单、干净、清晰的用户界面,而不是那些技巧。

Hoare,C.A.R.,“Programming: Sorcery or Science?” IEEE Software,1,2 (April 1984),pp.14-15.

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

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

Turski,W.,oral comments made at a conference in the late 1970s.

原则20:记录你的假设

系统运行的环境在本质上是无限的,不可能被完全理解。当我们开发一个系统,宣称要解决某个环境中的一个问题时,我们会对该环境进行假设。Manny Lehman提出:“大约对于每10行代码,我们就会做出一个假设,即使偏差了两三倍,每20~30行代码也会做出一个假设”。这些关于无限世界的有限假设会使你陷入麻烦。Lehman描述了一种表现不如预期的直线加速器。一位物理学家提出,也许月球的相位会产生影响,对此每个人都说:“你一定是在开玩笑吧!”然而,在考虑了月球的因素后,得到的方程式解释了大多数看似“不正确”的行为。这是一个假设(没有月球效应)无效的例子。

对需求工程、设计、编码和测试期间所做的所有假设,始终保持觉察是不可能的。尽管如此,我还是建议,对你有意识做出的假设做个记录。即使这个假设是显而易见的或其他选项很荒谬,也要这样做。还要记录它们的影响,也就是说,在产品中,这些假设是如何体现的?在理想情况下,你应该可以通过封装每个假设来隔离这些影响(见原则65)。

Lehman,M.,“Software Engineering,the Software Process and Their Support,” Software Engineering Journal,6,5(September 1991),pp.243-258,Section 3.6.

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

业界对“用简单方法解决复杂问题”的永恒渴望(见原则19),促使许多人宣称:最佳的软件开发方法,是在整个开发生命周期中使用相同的符号表达方法。既然在任何其他工程领域都并非如此,为什么在软件工程领域会是这样呢?在不同的设计活动中,电力工程师会使用不同的表达方法:方框图、电路图、逻辑图、时序图、状态转换表、柱状图等。这些表达方法为我们提供了在思维中可操纵的模型。使用越多的符号、越丰富多样的表达方法,我们就越能更好地对开发中的产品进行可视化。除非对所有阶段都是最优选择,否则为什么软件工程师想要将Ada用于需求、设计和代码?除非对所有阶段都是最优选择,否则为什么要在所有阶段都使用“面向对象”的方法?

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

Matsubara,T.,"Bringing up Software Designers,"American Programmer,3,7 (July-August 1990),pp,15-18.

注:Ada,是一种程序设计语言。

原则22:技术优先于工具

一个没规矩的木匠使用了强大的工具,会变成一个危险的没规矩的木匠。一个没规矩的软件工程师使用了工具,会变成一个危险的没规矩的软件工程师。在使用工具前,你应该先要“有规矩”(即理解并遵循适当的软件开发方法)。当然,你也要了解如何使用工具,但这和“有规矩”相比是第二位的。

我强烈建议,在投资于工具以对某项技术“自动化”之前,先手工验证这项技术,并说服自己和管理层:这项技术是可行的。在大多数情况下,如果一项技术在手工操作时不灵,那么在自动操作时也不灵。

Kemerer,C.,“How the Learning Curve Affects Tool Adoption,” IEEE Software,9,3(May,1992),pp.23-28.

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

一些软件工具(如CASE)会让用户的工作更加高效。务必要使用它们。就像文字处理软件对作家而言是必需的助手,CASE工具对软件工程师来说也是重要的助手。它们各自将使用者的开发效率提高了10% 到20%;它们各自使用户修改和发展其产品的能力提高了25%到50%。但是在这两种情况下,艰难的工作(思考)都不是由工具完成的。使用CASE工具,要切实考虑其对开发效率的影响。请注意,70%的CASE工具在购买后从未被使用过。我认为,造成这种情况的主要原因是过度乐观和由此带来的失望,而不是工具的无效性。

Kemerer,C.,“How the Learning Curve Affects Tool Adoption,” IEEE Software,9,3(May,1992),pp.23-28.

注:CASE,是“电脑辅助软件工程”(Computer-Aided Software Engineering)的缩写。

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

软件工程师使用工具(例如CASE)会变得更多产,就像作家使用文字处理软件变得更多产一样(见原则23)。然而,就像文字处理软件不能让一个平庸的小说家(能写小说,但卖不出去)变得出色,CASE工具也不能让一个平庸的软件工程师(能写软件,但不可靠、不能满足用户需求等)变得出色。因此,我们想把CASE工具只提供给优秀的工程师,而不想把CASE工具提供给平庸的工程师:我们希望他们尽量少(而非多)地开发出质量低劣的软件。

原则25:CASE工具是昂贵的

在工作站或者高端个人电脑中配置一套CASE工具环境,花销在5000~15 000美元。CASE工具本身,每份花费在500到50 000美元。工具每年需要的授权和维护费用一般为售价的10%~15%。而且,还需要为每一位接受培训的员工支付两到三天的工资。因此,每套软件的预期总安装成本可能超过17 000美元(对于价格适中的CASE工具),而每套软件的常规性年度成本可能超过3000美元。

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

Huff,C.,“Elements of a Realistic CASE Tool Adoption Budget” Communications of the ACM,35,4 (April 1992),pp.45-54.

注:目前大量的软件工具已经可以免费获得。即使是收费软件,一般来说,其购买费用相比于软件工程师的人工成本,也是很低的。对于一般的软件开发场景,这个原则可能已经不合时宜。但关于软件工具的成本和收益的分析思路,仍然是可以借鉴的。

原则26:“知道何时用”和“知道怎么用”同样重要

在行业中经常发生这样的事情,一个工程师学习一项新技术后,判断这是“放之四海而皆准”的技术。同时,同组另一个人在学习另外一项新技术,一场情绪化的争辩随之而来。事实上,没有一方是正确的。知道如何很好地使用技术,既不会让技术本身成为好技术,也不会让你成为一名优秀的工程师。知道如何用好木工车床,并不能使你成为你一名好木匠。一名优秀的工程师了解很多不同种类的技术,并且知道每种技术何时适合项目或项目的一部分。一个好木匠知道多种工具的用法,知道很多不同的技巧,而且,最重要的是,知道什么时候该用哪一种。

在进行需求工程时,要了解哪种技术对问题的哪些方面最有用(见原则47)。当进行设计时,要理解哪些技术对系统的哪些方面最有用(见原则63)。当进行编码时,要选择最合适的编程语言(见原则102)。

原则27:实现目标就停止

软件工程师要遵循许多方法(也称为技术或流程)。每个方法都有各自的用途,通常对应软件开发的一个子目标。例如,结构化(或者面向对象)分析的目标是理解要解决的问题,DARTS的目标是处理架构,结构化设计的目标是理清调用层次结构。这些例子中的方法都包含一系列的步骤。不要太过于陷入具体的方法,而忘记了目标本身。不要为更换目标而感到内疚。例如,如果只执行了方法的一半步骤,你就理解了问题,那就停下来。此外,你需要对整个软件过程有很好的认识,因为基于本原则所抛弃的某个方法的后续步骤可能会对未来软件的使用产生重要影响。

注:有资料显示,DARTS是Design Approach for Real-Time Systems(实时系统设计方法)的缩写。

原则28:了解形式化方法

没有过硬的离散数学技能,使用形式化方法是不容易的。但是,使用它们(即便是很简单的使用),可以极大地帮助我们发现软件开发中许多方面的问题。在每个项目中,至少应该有一个人能熟练使用形式化方法,以确保不会错过提升产品质量的机会。

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

Hall,A.,“Seven Myths of Formal Methods,” IEEE Software,7,5 (September 1990),pp.11-19.

原则29:和组织荣辱与共

业界普遍认为:日本软件工程师对待bug的态度,和美国工程师不同。尽管有许多影响因素,但有一个日本的观念与此密切相关:产品中的缺陷是公司的耻辱;软件工程师引起的公司耻辱,是工程师的耻辱。这种观念在日本比在美国更深入人心,因为日本劳动者倾向于一辈子只服务一家公司。不管在一家公司工作的时间是长还是短,这种心态是很重要的。

一般而言,当任何人发现你在产品中犯的错误时,你应该心存感激,而不是试图辩解。人非圣贤,孰能无过。过而能改,善莫大焉!当发现一个错误时,导致错误的人应该使其被周知,而不是藏着掖着。将错误广而告之有两个好处:(1)帮助其他工程师,避免同样的错误,(2)对后续的错误修正,也可以不那么抵触。

Mizuno,Y.,“Software Quality Improvement”,IEEE Computer,16,3 (March 1983),pp.66-72.

原则30:跟风要小心

大家都做的事情,对你来说也不一定是正确的。也许它是正确的,但你也应该评估它对你所处环境的适用性。这样的例子包括:面向对象,软件度量(见原则142、143、149、150和151),软件复用(见原则84),过程成熟度(见原则163),计算机辅助软件工程(CASE,见原则22至25),原型设计(见原则11、12、13、42)。在所有案例中,以上这些方法都提供了非常积极的帮助,体现在提高质量、降低成本、提高用户满意度等方面。然而,这些好处只在它们能发挥作用的组织中才会显现出来。尽管回报显著,但是它们的作用常常被过度宣传,其实它们并不是那么必然或通用。

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

Davis,A.,“Software Lemmingineering,” IEEE Software,10,6 (September 1993),pp.79-81,84.

原则31:不要忽视技术

软件工程技术日新月异。对几年内新的发展视而不见,是绝对不行的。软件工程的发展像波浪一样。每一波都会带来大量的“潮流元素”和流行语。尽管每一波只持续5~7年,但它们并不是简单地消失。其后每一波都是基于前一波的最好特征。(理想情况,“最好”应该指“最有效”,但遗憾的是,它往往指“最流行”。)

有两种方式可以让你紧跟技术潮流:阅读正确的杂志,和正确的人交谈。IEEE Software期刊就是一个很好的渠道,可以了解未来5年内可能有用的技术。PC Week、MacWorld等是学习硬件、常见商用工具和语言的好地方。要通过和人交谈来学习,就要找到正确的人。虽然和同事交流很必要,但还不够。每年都应该努力参加1~2个关键会议。和参会者的交流,很可能比会议报告更重要。

原则32:使用文档标准

如果你的项目、组织或客户要求遵循一套文档标准,那就要遵循它。无论如何,永远不要抱怨标准,认为这是不需要的。所有我熟悉的标准,无论是政府标准还是商业标准,都提供了组织和内容方面的指导。

创新!即遵循标准,同时理智地执行。无论标准怎么规定,把你知道的应有的内容都包含进去。这意味着用清晰的语言来编写,意味着添加额外的有意义的组织层级。如果你的文档没有被要求遵循某个标准,至少应使用检查清单来检查是否有重大的遗漏。IEEE发布的文档标准,是我所知道的最广泛的可用软件文档标准之一。

IEEE Computer Society,Software Engineering Standards Collection,Washington,D,C.: IEEE Computer Society Press,1993.

原则33:文档要有术语表

当阅读文档遇到不懂的术语时,我们都会感到沮丧。但当我们在术语表中查到说明时,沮丧的情绪顷刻就烟消云散了。

所有术语的定义都应该以这样的方式编写:定义中使用的任何单词,都应该尽量避免再去术语表中查找含义。一种技巧是首先用日常用语解释,然后再使用术语解释。在术语的说明文字中,在其他地方定义的术语要用特殊字体(如楷体)标识。示例如下。

数据流图:是图形化符号,用于展示系统的功能、数据库、和系统有关的环境之间的信息流动。通常用于:结构分析,转换的组成(气泡表示),数据流(箭头表示)和数据存储(两条平行线表示),以及外部实体(三角形表示)。

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

这个原则对于所有的软件文档的读者来说都是不言自明的。令人惊讶的是,很多作者并没有意识到这一点(想想,每个作者有时也是读者)。索引通常是文档所使用的所有术语和概念的列表,包括一个或多个页码,用于标记术语或概念在哪里被定义、使用或引用。对于需求、设计、编码、测试、用户的和维护文档来说都是如此。索引可以帮助读者快速查找信息,对于文档后续的维护和优化也很重要。

现代文字处理软件提供将索引引用嵌入正文的指令,这使创建索引变得十分简单。文字处理软件还会对索引进行编译,然后按字母顺序排列,并将结果输出。大多数CASE工具也能生成可用的索引。

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

写小说时,保持读者的兴趣是第一目标;而在技术文档中,必须使用相同的术语来表示相同的概念,使用相同的语句结构来表述相似的信息。否则会令读者感到困惑,导致读者需要花费时间确认,在重述中是否有新的技术信息。应该把这个原则应用到所有技术文档的写作中,包括需求规格说明、用户手册、设计文档、代码中的注释等。

举个例子:“有三类特殊命令。常规命令有四种类型。”

不如写为:“有三类特殊命令。有四类常规命令。”

Meyer,B.,“On Formalism in Specifications,” IEEE Software,2,1 (January 1985),pp.6-26.

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

关于软件工程研究所中令人难以置信的技术成就,有大量报道。但它们很少能应用于软件开发实践,原因是:

  1. 一般来说,软件研究者很少有开发实际系统的经验。
  2. 软件研究者可能会发现,在解决一些技术问题的时候没有必要花费过多时间去“适配”真实场景,这样可使解决问题变得更快更容易。
  3. 研究者和实践者在用语上存在巨大的分歧,导致他们很难相互沟通。

于是研究者更愿意在越来越多的无实际意义的问题上演示他们的想法。

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

Basili,V.,and J.Musa,“The future Engineering of software: A Management Perspective”, IEEE Computer,24,9(September 1991),pp.90-96.

原则37:要承担责任

在所有工程学科中,如果一个设计失败,工程师会受到责备。因此,当一座大桥倒塌时,我们会问“工程师哪里做错了?”当一个软件失败了,工程师很少受到责备。如果他们被责备了,他们会回答,“肯定是编译器出错了”,或“我只是按照指定方法的15个步骤做的”,或“我的经理让我这么干的”,或“计划剩余的时间不够”。事实是,在任何工程学科中,用最好的方法也可能产出糟糕的设计,用最过时的方法也可能做出精致的设计。

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

Hoare,C.A.R.,“Software Engineering: A keynote Address,” IEEE 3rd International Conference on Software Engineering,1978,pp.1-4.

第二部分:需求分析的罪恶之源

需求工程包括以下活动:

  • (1)提出或研究需要解决的问题;
  • (2)具体说明一个能解决该问题的系统外部(黑盒)行为。

需求工程的最终产出是需求规格说明(Requirement Specification)。

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

造成低质量成本估算的前5个原因,都与需求分析流程有关:

  1. 频繁的需求变更
  2. 不完整的需求列表
  3. 不充足的用户沟通
  4. 低质量的需求规格说明
  5. 不充分的需求分析

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

Lederer,A.,and J.Prasad,“Nine Management Guidelines for Better Cost Estimating,” Communications of the ACM,35,2(February 1992),pp.51-59.

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

当面对他们认定的问题时,大多数工程师都会匆忙提供解决方案。如果工程师对这个问题的看法是正确的,那么解决方案可能奏效。然而,问题往往是难以捉摸的。例如,唐纳德·高斯(Donald Gause)和杰拉尔德·温伯格(Gerald Weinberg)描述了高层办公楼中的一个“问题”,里面的住户抱怨电梯等待时间太长。这真的是一个问题吗?这是谁的问题?从居住者的角度来看,问题可能是浪费了他们太多时间。从房主的角度来看,问题可能是入住率(及租金)可能会下降。

显而易见的解决办法是提高电梯的速度。但其他解决方法可能包括(1)增加新电梯,(2)错峰安排上班时间,(3)给快递保留一些电梯,(4)提高租金(这样业主可以接受降低后的入住率),(5)改进电梯使用的“归位算法”(homing algorithm),以便在闲置时移动到高需求楼层。这些解决方案的成本、风险和时间延迟差别巨大。而任何一个方案生效,都取决于特定的场景。在试图解决问题前,针对面临问题的人及问题的本质,要确保深入分析了所有的可能选择。在解决问题时,不要被最初方案带来的潜在兴奋所蒙蔽。方案的变化总是比构建系统的成本低。

Gause,D.,and G.Weinberg,Are Your Lights On? New York: Dorset House,1990.

原则40:立即确定需求

需求难以理解,更难以说明。对此,错误的解决方法是草率地完成需求规格说明,匆忙地进行设计和编码,然后徒劳地希望:

  1. 任何系统都比没有系统要好。
  2. 需求迟早会解决。
  3. 或者,设计师在开发的过程中会明确可以开发什么。

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

Boehm,B.,“Verifying and Validating Software Requirements and Design Specifications,” IEEE Software,1,1(January 1984),pp.75-88.

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

如果在需求规格说明中有错误,你将付出以下代价:

  • 如果错误保持到系统设计阶段,定位和修复要多花5倍的代价。
  • 如果保持到编码阶段,要多花10倍的代价。
  • 如果保持到单元测试阶段,要多花20倍的代价。
  • 如果保持到交付阶段,要多花200倍的代价。

这是“要在需求分析阶段修复错误”的最令人信服的证据。

Boehm,B.,“Software Engineering,” IEEE Transactions on Computers,25,12(December 1976),pp.1226-1241.

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

在全面开发之前,以低风险、高回报的方式对用户界面达成一致,没有什么方法比原型更有效。有许多工具可以帮助快速创建屏幕演示。这些“故事板”可以给用户提供一个真实系统的印象。它们不仅有助于确认需求,还能赢得客户和用户的心。

Andriole,S.,“Storyboard Prototyping for Requirements Verification,” Large Scale Systems,12(1987),pp.231-247.

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

在创建需求规格说明时,要完成很多工作:访谈、辩论、讨论、架构调研、工作机制描述、问卷、JAD/RAD环节、其他系统的需求规格说明、早期的系统层面的需求分析。需求规格说明描述了从以上这些工作获得的需求分析结果。假设客户后续要求做一个需求变更。我们需要知道原始需求的动机,以便确认是否可以安全地变更。同样,当系统无法满足某个需求时,我们需要知道需求的背景,才能决定是修改系统设计以满足需求,还是修改需求以匹配系统。

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

Gilb,T., Principles of Software Engineering Management,Reading,Mass.: Addision Wesley,1988,Section 9.11.

注:JAD,即“联合应用开发”(Joint ApplicationDevelopment)。RAD,即“快速应用开发”(Rapid Application Development)。

原则44:确定子集

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

  1. 更容易地使每个组件只包含一个功能。
  2. 选择更具内聚性和可扩展性的架构。
  3. 了解如何在日程或预算紧缩的情况下减少功能。

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

Parnas,D.,“Designing Software for Ease of Extension and Contraction,” IEEE Transactions on Software Engineering,5,
2(March 1979),pp.128-138.

原则45:评审需求

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

由于需求规格说明是用自然语言编写的,因此对其进行评审没有简单的方法;然而,Barry Boehm给出的指导可以使评审变得容易一些。当然,如果需求规格说明的某些部分是用更正式的语言编写的(见原则28、54和55),则可以对这些部分进行手工评审(因为它们没有歧义),并在某些情况下“执行”。可执行的需求[如Pamela Zave的PAISLey(“An Insider’s Evaluation of PAISLey,” IEEE Transactions on Software Engineering,17,3 (March 1991),pp.212-225)]可以用适当的工具进行演示,相关人员可以“看到”系统功能,而不只通过“阅读”了解系统如何运行。

Boehm,B.,“Verifying and Validating Software Requirements and Design Specifications,” IEEE Software,1,1(January 1984),pp.75-88.

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

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

如果撰写需求的人发现,很难在没有系统设计(例如,通过有限状态机描述系统行为)的情况下、毫无歧义地定义外部行为,应该留下这样的信息:

警告:这里包含的设计,仅用于辅助理解产品的外部行为。在系统外部行为相同的情况下,设计人员可以选择任何设计方案。

Davis,A.,Software Requirements: Objects,Functions and States,Englewood Cliffs,N.J.: Prentice Hall,1993,Section 3.1.

原则47:使用正确的方法

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

例如,对于数据密集型的软件,应使用实体关系图(Entity Relation Diagram);对于反应式(实时)系统,应使用有限状态机或者状态图;对于有同步难题的软件,应使用Petri网;对于决策密集型的软件,应使用决策表;诸如此类。

Davis,A.,“A Comparison of Techniques for the Specification of External System Behavior,” Communications of the ACM,31,9 (September 1988),pp.1098-1115.

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

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

例如,对于一个复杂的系统,你可能需要使用面向对象分析来评估那些与软件相关的重要实体。面向对象分析(OOA)可以帮助确定实体,并且理解它们之间的关系和相关属性。你可能需要使用有限状态机来描述用户操作界面的预期行为你可能需要使用决策树来描述在响应外部条件的复杂组合时系统的预期行为。诸如此类。

Yeh,R.,P.Zave,A.Conn,and G.Cole,Jr.,“Software Requirements: New Directions and Perspectives,” in Handbook of Software Engineering,C.Vick and C.Ramamoorthy,eds.,New York: Van Nostrand Reinhold,1984,pp.519-543.

原则49:合理地组织需求

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

要以一种对客户、用户或者市场营销人员最自然的方式来组织需求。这里有一些例子:从用户(类别)的角度,从激励(类别)的角度,从反馈(类别)的角度,从对象(类别)的角度,从功能(类别)的角度,从系统模式的角度等。举例来说,对于电话交换系统,可依次按照功能类别、功能、用户来组织需求:

1.单方通话
 1.1 呼叫转移<

以上是关于软件开发的201个罪恶之源的主要内容,如果未能解决你的问题,请参考以下文章

软件开发的201个原则

软件开发的201个原则

软件开发的201个原则

软件开发的201个原则

软件开发的201个原则

关于罪恶之源智学网的一些想法