
1.3 软件工程
软件工程自20世纪60年代末出现以来,已经取得了绝对性的成功。今天,很少有专业的程序员还认为编码是一件很难的事情,但是在编码刚刚出现时,对每个人都是一个很大的挑战。现代的程序员认为理所当然的一些概念——例如结构化编程、适当的程序布局(如缩进)、注释和良好的命名方式,这些都来自对软件工程的不断研究。事实上,对软件工程几十年来的研究已经极大地影响了现代的编程语言和其他编程工具。
软件工程已经存在很长时间,并且对计算机编程的各个方面都产生了巨大的影响,以至于许多人认为软件工程师就是指计算机程序员。当然,任何专业的软件工程师也应该是一个有编程能力的计算机程序员,但是编程仅仅是软件工程的一小部分。软件工程主要会涉及经济和项目管理方面。有趣的是,那些真正负责管理项目、维护时间进度表、选择软件方法论等的人,往往并不是软件工程师,他们被称为项目经理、项目负责人,以及其他暗示权威地位的头衔。同样,我们称为软件工程师的人实际上并不是做软件工程的——他们只是编写由那些真的软件工程师(项目经理和项目负责人)所规定的代码。也许,这就是大家对“软件工程”这个词理解如此混淆的原因。
1.3.1 一个正式的定义
似乎没有一个对软件工程的定义能够满足所有人的要求。不同的人会对这个词添加自己的“理解”,使得他们的定义与其他的定义略有(或者有很大的)不同。之所以将这本书命名为《软件工程化》,是因为我想避免在软件工程这个词上添加另一个定义,让大家更加困惑。顺便提醒一下,IEEE组织对软件工程这个词的定义如下:
使用一种系统化的、有理可寻的、可量化的方法来开发、运行和维护软件,即工程化在软件方面的应用。
而最初的软件工程定义,也是我经常使用的定义如下:
软件工程是一门研究如何开发和管理大型软件系统的学问。
这里的关键词是大型。在软件工程的发展过程中,以前大部分系统都是国防系统或者类似的项目,所以软件工程被等同于大型系统也就不足为奇了。虽然IEEE的定义可以适用于几乎任何规模的系统,但是因为软件工程的大部分研究都是关于如何开发非常大型的系统的,所以我更喜欢第二个定义。
注意:为了避免与软件工程的“通用定义”相混淆,我使用了一个更专业的术语——个人软件工程,来描述那些个体程序员在小型项目或者大型项目的其中一小部分工作中使用的流程和方法。我的目的是希望告诉计算机程序员,他们应该相信的是软件工程的本质,而不是任何与编写出卓越的代码无关的细节。
当我们谈到软件开发时,每个人对“大型”的定义其实都完全不同。计算机科学专业的本科生可能会认为,一个包含了几千行源代码的程序就是一个大型系统。而对于波音公司(或者其他大型公司)的项目经理来说,一个大型系统应该包含超过一百万行代码。根据我最近一次的统计(虽然那也是很久以前了),微软Windows操作系统(OS)的源代码已经超过5000万行,没有人会怀疑Windows是一个大型系统!
因为传统的软件工程定义通常只适用于大型软件系统,所以我们需要对大型(和小型)的软件系统给出一个合理的定义。尽管代码行数(Lines of Code,LOC)是软件工程师经常用来描述软件系统大小的一个度量指标,但是它其实非常不准确,方差几乎在两个数量级[6]。本书会经常使用LOC或者千行代码(KLOC)这两个度量指标。但是,将一个正式的定义建立在这样一个糟糕的度量指标上,并不是一个好主意,这样会降低定义的准确性。
1.3.2 项目规模
一个小型项目指的是一个普通程序员能够在合理的时间内(一般不超过两年)完成的项目。一个中型项目指的是一个人无法在合理的时间内完成,但是一个由2~5个程序员组成的小团队可以完成的项目。一个大型项目则需要一个较大的程序员团队(超过5个成员)来完成。
就LOC而言,一个小型项目会包含50~100 KLOC,一个中型项目会在50~1000 KLOC(约100万行代码)范围内,而一个大型项目少则包含500~1000 KLOC。
小型项目管理起来很简单。因为小型项目不需要程序员之间的交流,也不需要程序员与外部环境之间的交互,所以生产力几乎完全取决于程序员个人的能力。
中型项目会带来新的挑战。由于多个程序员在项目中一起工作,沟通可能会成为一个问题,但是又因为团队足够小,所以这种开销还是可管理的。不过,因为团队之间的互动需要额外的资源支持,所以会增加编写每一行代码的成本。
大型项目需要一个大型的程序员团队。团队成员之间的沟通和其他开销经常会消耗掉每个工程师50%的生产力。因此,对于大型项目而言,有效的项目管理至关重要。
软件工程涉及成功管理需要大型程序员团队完成的项目的各种方法、实践和策略。遗憾的是,个人或者小型团队的良好实践经验无法被扩展到大型团队中,而大型项目的方法、实践和策略也无法被应用到中小型项目中。在大型项目中应用良好的实践经验,通常会给中小型项目带来不合理的开销,从而降低那些小团队的生产力。
让我们来仔细看看不同规模项目的一些优点和缺点。
1.3.2.1 小型项目
在小型项目中,一个软件工程师会全面负责系统设计、实现、测试、调试、部署和文档编写。在这样的项目中,单独的工程师要比中型项目或者大型项目中的一个工程师负责更多的任务。但是这些任务都很小,因此是可管理的。因为小型项目只有一个人来执行各种任务,所以这个程序员必须拥有各种技能。个人软件工程涵盖了一个开发人员在小型项目中所有的行为活动。
小型项目可以最有效地利用工程资源。工程师可以使用最有效的方法来解决问题,因为他们不必与其他工程师就项目达成一致意见。工程师还可以优化他们花在每个开发阶段的时间。在一个结构化的软件设计过程中,需要花费相当多的时间来记录开发过程,但是当项目中只有一个程序员时,这样做就没有意义了(尽管在产品的生命周期后期,可能需要另一个程序员来编写代码)。
小型项目存在的缺点和陷阱在于,工程师必须能够处理所有不同的任务。许多小型项目失败(或者开发成本太高)的原因,就是工程师没有接受过完成整个项目的适当培训。与其他的目标相比,《编程卓越之道》系列图书的主要目标就是教会程序员如何正确地完成一个小型项目。
1.3.2.2 中型项目
在中型项目中,个人软件工程会涉及一个工程师要负责的各个方面,通常包括系统组件的设计、实现(编码)和编写该模块的文档。通常,他们还负责测试各个组件(单元测试),然后整个团队来测试整个系统(集成测试)。通常,有一个工程师(项目负责人或者主程序员)会负责整个系统设计,以及项目部署。根据项目的不同,可能会需要一个技术作者来编写系统文档。因为在一个中型项目中,工程师之间是共享任务的,所以可以形成专业分工,而且项目不要求每个工程师都有能力执行所有任务。主程序员可以指导那些缺乏经验的人,从而保证整个项目的质量。
在一个小型项目中,工程师可以看到项目的整体情况,并且可以根据其对项目的理解来优化某些行为活动。但是在一个大型项目中,工程师无法了解其所负责的任务以外的情况。中型项目则结合了这两种极端情况:个人既可以了解整个项目的大部分内容,并以此来调整其实现方式,也可以专门研究系统的某些方面,而不会被其他细节牵扯大量的精力。
1.3.2.3 大型项目
在大型项目中,不同的团队成员都有其专门的角色,从系统设计到实现、测试、文档编写、系统部署,以及系统优化和维护。与中型项目一样,在大型项目中,个人软件工程只涉及单个程序员所负责的行为活动。大型项目中的软件工程师通常只做很少的工作(例如,编码和单元测试),因此他们不需要具备小型项目中程序员所具备的广泛技能。
除了行为活动,项目的规模大小还会影响到工程师的生产力。在一个大型项目中,工程师会变得非常专业,并且专注于自己的专业领域,这使得他们能够比使用普通技术更有效地完成工作。但是,为了达到这种效果,在大型项目中必须使用一种通用的软件开发方法,如果其中一些工程师不喜欢这种方法,那么他们可能就没有那么高效了。
1.3.3 软件工程的问题
将工程领域的技术经验应用到软件开发中,然后以一种更加经济有效的方式来开发应用程序是可能的。但是,正如Pete McBreen在Software Craftsmanship:The New Imperative一书中所说的,软件工程最大的问题是假设“系统化的、有理可寻的、可量化的方法”是唯一合理的方法。事实上,他提出了一个非常好的问题,即是否有可能让软件开发变得系统化和可量化?在controlchaos网站上,McBreen这样写道:
如果一个流程可以被完全定义,你对它的所有事情都很清楚,这个流程就是可被设计的,重复运行该流程可以产生可预测的结果,那么这样的流程就被称为已定义流程,并且可以被自动化。如果你对一个流程中的所有事情都不完全清楚,即当你输入一堆东西时只知道大概会发生什么,并且不确定如何测量和控制结果,那么这样的流程就被称为经验性流程。
软件开发不是一个已定义流程,而是一个经验性流程。因此,软件开发不能被完全自动化,并且通常将工程领域的经验应用到软件开发中是很困难的。部分问题在于,实际的工程实现在很大程度上依赖于对现有设计的重用。尽管在计算机编程中也存在大量的重用,但是它比其他工程领域需要更多的定制化。
我们在本书的前言中介绍过软件工程的另一个重要问题,就是软件工程把软件工程师当作一种商品资源,项目经理可以随意地在项目中更换软件工程师,从而忽略了个人才能的重要性。这个问题并不是说工程技术没有价值,而是说管理层试图将它们统一应用到每个人身上,并且鼓励在软件开发中使用一些所谓的“最佳实践”。虽然这种方法可以产生高质量的软件,但是它限制了我们跳出思维定式,以及创造更好的实现方式的可能。