-
No Silver Bullet
Fred Brooks在1987年所发表的一篇关于软件工程的经典论文《No Silver Bullet》中谈到:所有软件活动包括:
- 根本任务—— 打造构成抽象软件实体的复杂概念结构;
- 次要任务—— 使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言。
其中软件工程师在次要任务上花费了过多的时间,然而次要任务占软件工作的比例有限,所以一味地从次要任务方面进行优化无法带来软件生产率数量级上的提高,需要开始关注软件的根本任务。
人们在软件开发中试图寻找一种能够使得软件能够像硬件中的电子管、晶体管那样期望每两年有两倍的增长。然而并不存在任何技术或管理上的进展,能够独立地保证软件在生产率、可靠性或简洁性上取得数量级的提高。这是由软件的自身特性决定的,但是却拥有一些令人振奋的革新。这些方法的规范化、持续地开拓、发展和传播确实可以使生产率产生数量级的提高。由此,产生了现在的软件工程。
现代软件系统中有一些无法规避的内在特性:复杂度、一致性、可变性和不可见性。
- 复杂度。软件实体可能比任何由人类创造的其他实体更复杂,软件系统的状态又比计算机的状态多若干个数量级,软件实体的扩展不仅仅是相同元素重复添加,导致软件复杂度要比非线性增长多得多。
-
一致性。软件工程师所要面对很多情况,比如:必须要遵循许多人为惯例。这些惯例随接口的不同而不同,然而,这些变化并不是必需的,仅仅由于它们是不同的人设计的结果。在这种情况下,软件的复杂性来自:需要保持与其他接口的一致性,对软件自身的任何再设计,都无法简化这一复杂特性。
- 可变性。软件实体经常会遭受到持续的变更压力。功能扩展的压力主要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们。此外,软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比软件开发时所用的硬件平台要长。软件必须与各种新生硬件保持一致。
- 不可见性。软件是不可见的和无法可视化的。软件的客观存在不具有空间的形体特征。因此,没有自身的几何表达方式。
在软件领域中取得的最富有成效的三次进步体现在对于次要困难的解决包括:高级语言,分时,同一编程环境。
- 高级语言。软件生产率、可靠性和简洁性上最有力的突破是使用高级语言编程。大多数观察者相信开发生产率至少提高了5倍,同时可靠性、简洁性和理解程度也大为提高。
- 分时。分时在提高程序员的生产率和产品质量方面起到了很大作用,分时保证了及时性,缩短了系统的响应时间。随着它接近于零,超过人类可以辨识的基本能力—— 大概100毫秒时,所获得的好处就接近于零了。
- 统一编程环境。集成开发环境通过提供集成库、统一文件格式、管道和过滤器,解决了共同使用程序的次要困难。这样,概念性结构理论上的相互调用、提供输入和互相使用,在现实中可以非常容易地实现。集成开发环境的广泛应用使生产率实现提高。
然而,如之前所述,一味地从次要任务方面进行优化无法带来软件生产率数量级上的提高,需要开始解决软件的根本而非次要问题,可以从以下几个方面来考虑:
- 快速原型化系统的方法和工具。软件系统的快速原型对重要的系统界面进行模拟,并演示待开发系统的主要功能。
- 增量开发—— 增长,而非搭建系统。这种开发模式对士气的推动是令人震惊的。
- 卓越的设计人员。关键的问题是如何提高软件行业的核心,一如既往的是人员。
在我们的团队项目中,我们选择了C#,以及统一了VS开发环境,在次要任务上保证了软件开发效率,此外在主要任务方面我们采用增量式的开发方式,在初步实践得到效果后,团队成员都特别兴奋,增加了对写一个相对完善软件的信心。
-
Big Ball of Mud
大泥球是指一个随意化的杂乱的结构化系统,只是代码的堆砌和拼凑,往往会导致很多错误或者缺陷。
在个人项目中,曾经出现过部分函数功能相似只有少量功能不同,然后相似的功能直接采用代码复制,不仅导致代码冗余度大,而且产生了许多潜在的bug,比如有少部分数组下标忘记修改导致结果偶发性错误,而且前期设计不够充分,之后使得系统越来越复杂,最终代码写了1000+,然而感觉如果之前一个好的设计的话,代码最多400行左右。概括总结避免大泥球的方法如下:在软件设计阶段首先需要关注软件的特性和功能,然后集中在架构和性能,使得软件设计之初就避免产生问题或者方向的偏差。其次,在编写软件时要及时解决出现的小问题或者原型概念等等,这样不会使得问题堆积导致后期的无法修改。
-
The Cathedral and the Bazaar
- 大教堂模式(The Cathedral model):源代码在软件发行后公开,但在软件的每个版本开发过程中是由一个专属的团队所控管的。
- 市集模式(The Bazaar model):源代码在开发过程中即在互联网上公开,供人检视及开发。
大教堂模式的软件开发让程式除错的时间大幅增加,因为只有少数的开发者可参与修改工作。市集模式则相反。
我们的团队项目采用的是大教堂模式,由于我们选用TFS进行代码管理,在一定程度上决定了代码只能属于大教堂模式,这样也就导致我们的软件在开发过程中只能由开发人员来保证软件质量,在一定程度上导致除错时间的增加,在之后的软件开发中可以考虑将代码由github进行管理,即时接受其他开发者的检视以及增量式开发。
-
A Generation Lost in the Bazaar
作者文中无情的揭示了集市模式导致的一个悲哀的现实:一坨脓包似的权宜代码,被一群盲目的根本不知IT架构为何物的所谓IT“专业人士”永无休止地复制着,粘贴着。这事儿放在今天你也许很难相信,但就是在这令人无比尴尬的混沌之下,沉睡着美轮美奂的Unix大教堂的遗迹,而Unix恰恰是以设计简约、功能实用、执行优雅而著称于世的。(世间荣耀就此消失……)我们可以精确地指出Unix开始走向碎片化的时间点:1990年代初,AT&T抛弃Unix,将其商业化,抢走其架构师的那一刻。参考:有人负责,才有质量:写给在集市中迷失的一代。
而作者认为针对这一现象最有效的解决方式是:所谓质量,只有在某人对它负责时才有意义,而这个“某人”只能是一个人,不能是几个人——二重奏除外。我的理解是针对软件的架构师必须要负责整个软件的生命周期,因为,软件的开发人员可能更迭,如果架构师不负责软件的维护,那么后续的开发人员对于软件很难有一个良好的把控,只能不明所以地沿用前人的代码,而无法对于已经过时甚至错误的代码做出适当的删改,导致软件的碎片化,以及文中指出的那样对于早已过时的Fortan编译器兼容性的检测。
我们的团队项目采用增量式的方式进行开发,由于之前学长没有使用那些晦涩难懂的语法或者语言现象,我们对于代码都能够清楚地理解和把握,所以我们在团队开发中没有遇到类似的问题。我认为这是由于软件的规模所决定的,我们的软件规模跟Unix这样的大型软件规模是没法相提并论的,软件的设计也只存在于开发者的头脑中,不存在架构师,这种问题也就相应地没有出现。
-
Waterfall Model
1970年WinSTon Royce提出了著名的"瀑布模型",直到80年代早期,它一直是唯一被广泛采用的软件开发模型。
瀑布模型将软件生命周期划分为制定计划、需求分析、软件设计、程序编写、软件测试和运行维护等六个基本活动,并且规定了它们自上而下、相互衔接的固定次序,如同瀑布流水,逐级下落,最终得到软件产品。
瀑布模型有以下优点:
- 为项目提供了按阶段划分的检查点。
- 当前一阶段完成后,只需要去关注后续阶段。
- 可在迭代模型中应用瀑布模型。增量迭代应用于瀑布模型。迭代1解决最大的问题。每次迭代产生一个可运行的版本,同时增加更多的功能。每次迭代必须经过质量和集成测试。
- 它提供了一个模板,这个模板使得分析、设计、编码、测试和支持的方法可以在该模板下有一个共同的指导。
瀑布模型有以下缺点:
- 各个阶段的划分完全固定,阶段之间产生大量的文档,极大地增加了工作量。
- 由于开发模型是线性的,用户只有等到整个过程的末期才能见到开发成果,从而增加了开发风险。
- 通过过多的强制完成日期和里程碑来跟踪各个项目阶段。
- 瀑布模型的突出缺点是不适应用户需求的变化。
在我们的团队项目开发中采用的就是这种“瀑布模型”,由于我们的用户需求相对固定,此外,瀑布模型给出了阶段的划分完全能够适应我们项目开发的模式需求,故而采用这种模式。
-
Agile Methods
敏捷型方法的合理性,着重点并不是放在其“轻重”上,而是于它们的适应性(adaptive〕性质和以人优先的理念。
多数软件开发仍然是一个显得混乱的活动,即典型的“边写边改” (code and fix〕。设计过程充斥着短期的、即时的决定,而无完整的规划。软件行业中最初的一场运动是要改变这种情况,而引入了“正规方法” (methodology〕的概念。敏捷型方法(agile methodologies)的发展是对这些工程方法的反叛。
敏捷型与工程型方法有一些显著的区别。其中一个显而易见的不同反映在文档上。敏捷型不是很面向文档,对于一项任务,它们通常只要求尽可能少的文档。 从许多方面来看,它们更象是“面向源码”(code-oriented〕。事实上,它们认为最根本的文档应该是源码。
文档减少仅仅是个表象,它其实反映的是两个更深层的特点:
- 敏捷型方法是“适应性”而非“预见性”。 工程方法试图对一个软件开发项目在很长的时间跨度内作出详细的计划, 然后依计划进行开发。这类方法在一般情况下工作良好,但(需求、环境等)有变化时就不太灵了。因此它们本质上是拒绝变化的。而敏捷型方法则欢迎变化。其实,它们的目的就是成为适应变化的过程,甚至能允许改变自身来适应变化。
- 敏捷型方法是“面向人”的(people-oriented) 而非“面向过程”的 (process-oriented)。 工程型方法的目标是定义一个过程,不管是谁用都工作。而敏捷型方法则认为没有任何过程能代替开发组的技能,过程起的作用是对开发组的工作提供支持。
不可预见过程的控制 - 迭代
对付一个不可预测的世界呢?最重要,也是最困难的是要随时 知道我们在开发中的情形处境,这需要一个诚实的反馈机制来不断准确地告诉 我们。这种机制的关键之点是“迭代式”(iterative〕开发方法。
虽然迭代式开发也可用于可见性环境,但它基本上还是用作“适应性” (adaptive〕过程,因为适应性过程能及时地对付需求变更。需求变更使得长期计划是不稳定的,一个稳定的计划只能是短期的,这通常是一个“迭代周期”(iteration〕。迭代式开发能让每个迭代周期为下面的开发计划提供一个坚实的基础。
一些敏捷开发的方法
- XP(Extreme Programming -- 极限编程〕
XP始于五条基本价值观(values):交流,反馈,简洁,勇气和尊重(Communication, Feedback, Simplicity, courage, and Respect)。在此基础上细化出了十四条原则(principles)和二十四条实践法(practices)。其基本思想是: 实践是项目组的日常的具体活动,而价值观是根本性的知识和理念,其构成了该方法基石。没有实践法的价值观是难于应用的,或失之于过于宽泛而不知从何着手。没有价值观的实践法则是一堆杂乱无章的活动。价值观和实践法都是需要的, 但中间还有一个空档--而原则正是用来连接价值观和实践的。许多XP的实践法都是以前就存在的并经过实践检验的,而常常被许多过程,包括那些计划型过程给忽略了。XP重新建立了这些准则,并把它们编织成了一个和谐的整体, 使得每一项准则都能在其他准则里得以强化。
XP有一个最具冲击力的是它对测试的极端重视。诚然,所有的过程都提到测试,但一般都不怎么强调。可是XP将测试作为开发的基础,要求每个程序员写一段源码时都得写相应的测试码。这些测试片段不断地积累并被整合到系统中。这样的过程会产生一个高度可靠的 建造平台,为进一步开发提供了良好的基础。这种方法常被描述成 Test Driven Development(TDD)(测试驱动开发),它对许多不是完全采用XP的方法都有很大的影响。
- SCRUM
SCRUM的着重点是在软件开发的管理方面。它把一个项目分成若干个为期三十天的迭代阶段,每一阶段称之为一“冲”(sprint〕。每天有一个短会, 称之为一个scrum,这样管理者能对项目有近距离的观察与控制。SCRUM对工程实践方面强调少一些,许多人在开发中把SCRUM的项目管理和XP的工程实践相结合。
- 水晶(Crystal)系列
所有水晶方法都有三个需考虑的优先因素(priorities):安全性(safety) (项目的结果),效率(efficiency),和习惯性(habitability)(即开发人员 多大程度上愿意使用水晶方法)。其他共同特征有:频繁发布,反思改进,紧密交流 (Frequent Delivery,Reflective Improvement, and Close Communication)。
- 相关环境驱动测试(Context Driven Testing)
敏捷开发运动最初是由软件开发人员来推动的。但是,参与软件开发的其他方面 的一些人士也受到这个运动的影响。一个明显的群体是测试人员,他们通常是生活 在由瀑布式开发所限定的世界里。一般来说,测试的作用是保证软件与开始的设计 相符合。而在敏捷世界里,测试人员的角色还很不清楚。这导致了一个称之为“相关环境测试”(context driven testing)的群体。
- Lean Development(精悍开发)
Lean movement(精悍生产运动)是由丰田公司(Toyta)Taiichi Ohno首创,并以 丰田生产系统(Toyota Production System)著称。精悍生产的理念对许多早期的 敏捷论者多有启发。
- (Rational)Unified Process
RUP最主要的特征是用例驱动开发(Use Case Driven)(即,开发是以用户可见 的系统功能特征来驱动的),迭代,和以架构为中心(需要优先考虑的一点是, 尽早设计出一个架构以贯穿项目始终)。
在我们的团队项目中,对于需求变化以及其他的不可预见性采用了迭代的方式进行予以适应性的变化,采用的敏捷开发方式是scrum,总共进行了两次为期两周的冲刺。团队编程中也使用部分极限编程的思想。其中最重要的就是交流和反馈思想的使用,这使得在团队成员之间规避了许多理解上的分歧,从而少走了许多弯路。