软件维护与演化

1 维护

各个工程领域都会在将产品交付给用户之后进行维护工作:主要是为了保证产品的正常运转而进行使用帮助、故障解决和磨损处理等工作。

软件的维护主要是“修改”

  • 软件不会磨损
  • 软件维护只需要完成少量的使用帮助、故障解决和磨损处理等工作
  • 软件特性上是易于修改的
  • 软件只有持续修改才能保持价值

软件维护的定义:IEEE定义软件维护是在交付之后修改软件系统或其部件的活动过程,以修改缺陷、提高性能或者其他的属性、适应变化的环境

1.1 软件变更的常见场景

  1. 问题发生了变化:随着时间的发展,形势可能会发生变化,导致用户的问题发生变化。这些使得软件的需求发生变化,出现新的需求,否则软件将减小甚至失去服务用户的作用
  2. 环境发生了变化:随着软件产品的生命周期越来越长,在软件生存期内外界的环境发生变化的可能性越来越大,因此,软件经常需要修改以适应外界环境的改变
  3. 软件产品中的缺陷:软件开发的理想结果当然是建立一个完全无缺陷的软件产品,但这是一个不可能达到的目标。最终的软件产品总是或多或少的会遗留下一些缺陷。当这些缺陷在使用中暴露出来的时候,必须给予及时的解决。

1.2 软件维护类型划分

  1. 完善性维护(Perfective maintenance):为了满足用户新的需求、增加软件功能而进行的软件修改活动。
  2. 适应性维护(Adaptive maintenance):为了使软件能够适应新的环境而进行的软件修改活动
  3. 修正性维护(Corrective maintenance):为了排除软件产品中遗留缺陷而进行的软件修改活动
  4. 预防性维护(Preventive maintenance):为了让软件产品在将来可维护,提升可维护性的软件修改活动

理想情况下,为了满足一些变更而执行的维护活动应该不会降低软件产品的质量,尤其是可维护性,否则,本次的维护活动将会使未来的维护活动更加困难。但是在实践中发现,软件维护活动弄的确会降低软件产品的质量,甚至导致一个软件产品在进行一系列维护活动之后会失去可维护性。[Lehman1980, 1984] 将这种现象表述为:在一个程序发生变更的时候,它的结构倾向于变得更加复杂,因此需要投入一些额外的资源以在保持功能的同时简化程序结构。预防性维护就是为了简化维护后的软件结构以提高软件可维护性的额外投入。

软件维护的成本远远超过开发,且从事软件维护工作的人员比率正在上升,维护的高代价主要是因为软件需求的频繁变更以及维护工作的困难所造成的,在软件维护过程中,需求的变更可能导致需要重新理解之前实现的程序(此时就体现了注释的重要性,好的注释能够减少理解程序的时间),或者需要分析此次维护改动对程序造成的影响.

  1. 程序理解的困难性:软件维护人员通常不是程序代码的编写者,不同人的思维方式不同,维护人员不仅要读懂程序逻辑还要理解编写者的思路,实践中很多软件项目的文档不全或者更新不及时,维护人员无法获得足够的帮助,只能单纯依赖代码片段拼接来形成对系统的整体理解
  2. 分析的困难性:在开发软件时,具体功能和需求并不是各自独立实现的,既不是每一个需求都被单独实现为一段代码。通常,每条需求会被实现为相互联系的多个程序代码片段,而且每个程序代码片段要同时承载多个具体需求的实现。程序代码片段与具体需求之间是多对多的复杂关系。而且软件的程序代码也是相互影响的,维护人员在修改一部分程序代码时,可能会影响到其他部分

软件维护中的困难和问题的根源在软件开发阶段,在设计和开发时期应该就要有合理且具备良好可修改性的程序,能够大大减少软件维护时耗费的成本。在开发时期可以通过以下的两种方式来进行开发:

  1. 考虑软件的可变更性:预测变更并将其独立封装,便于修改时的程序定义与理解,防止修改时的连锁反应
    1. 分析需求的易变性,尽可能发现和预测可能的变更
    2. 为变更进行设计,开发人员需要进行关注点分离,使用信息隐藏等设计思想为可能的变更进行设计,将其封装起来
  2. 为降低维护困难而开发:为后期理解程序和进行影响分析提供额外的便利
    1. 编写详细的记述文档并保持及时更新
    2. 保证代码的可读性
    3. 维护需求跟踪链:需求跟踪链从正反两个方向记录“需求、设计、编码、测试”之间的跟踪与回溯关系
    4. 维护回归测试基线:回归测试基线包含了系统修改之前的有效测试用例集合,因此只需要根据修改情况对回归测试基线进行简单的修正。

2 演化

软件维护过程

  1. 问题/修改的标识、分类
  2. 分析
  3. 设计
  4. 实现
  5. 回归/系统测试
  6. 验收测试
  7. 移交

2.1 问题/修改的标识、分类与划分优先级

该步骤的主要任务是进行变更管理(Change Management)

  1. 用户、客户或其他人员提出变更请求
  2. 维护人员为变更请求建立变更记录(Change Record),赋予标识,进行变更类别分类,确定其优先级
  3. 初步评估变更的可能影响,并据此确定是否接受改变更请求
  4. 如果决定执行变更,就为其安排修改意见。通常多个小的修改会安排到一个时间内批量完成

2.2 分析

该步骤的主要任务是为后续的修改(设计、实现、测试、交付发布等)确定一个基本的规划,包括2个阶段:

  1. 可行性分析
    1. 该步骤的任务是提出候选方案,并分析方案的可行性,建立可行性报告
    2. 可行性报告的内容包括:变更的影响范围、候选方案、需求变化分析、对安全性和保密性的影响、人的因素、短期和长期成本、修正的价值与效益等
  2. 详细分析
    1. 该步骤的任务是准确定义修改的需求,表示需要修改的元素、标识修改中的安全与保密因素、确定一个测试策略和建立一个实现计划

2.3 设计

该步骤的主要任务是依据变更分析的结果和已有系统的信息,完成对系统设计的变更。具体的工作包括:标识被影响的软件模型、修改软件设计文档、为新的设计创建测试用例、更新回归测试集、更新需求文档。

  1. 在进行完善性维护和适应性维护时,设计步骤要针对新的功能需求执行一个完整的详细设计过程
  2. 在进行修正性维护时,设计要防止程序修改带来连锁的负面效应
  3. 在进行预防性维护时,设计要重点关注软件结构的质量,以此为依据修改程序代码和软件系统结构

2.4 实现

该步骤的主要任务是根据变更的设计,完成代码的实现。具体的工作包括:编码与单元测试、集成新修改代码、集成测试、风险分析和代码评审

2.5 回归/系统测试

该步骤的主要任务是确保对变更的修改不会带来连锁的负面效应,要保证系统仍然能够满足其他未被修改的需求。具体工作包括:针对变更情况进行功能测试界面测试、对整个系统进行回归测试、验证系统是否准备好进行验收测试

2.6 验收测试

该步骤的主要任务是由用户、客户或客户指定的第三方来验证系统是否满足用户的变更请求。具体的工作包括:针对变更请求的功能测试、针对用户使用环境的兼容性测试、对整个系统进行回归测试

2.7 移交

该步骤的主要任务是将修正的系统发布用于安装和运营。具体工作包括:进行配置审计;通知用户团体、为了备份系统而开发一个阶段性版本、在客户的设施上进行安装和培训。其中配置审计是要通过配置管理系统确定一个系统的发布包,包活文档、软件程序、培训文档、以及其他相关文档。

3 软件维护与演化的技术

维护和演化的区别:经常被作为等价词使用,意指软件交付后的“修改”活动,但有时软件演化拥有特殊的含义,意指软件初期交付后,一边“修改”已有软件,一边开发新的增量需求软件部分。

即烟花有时候是开发与维护的综合

软件演化定律(Lehman),总共有八条,下面是其中的三条:

  1. 持续变化(1974,Continuing Change):一个大型软件系统要么进行不断的变化,要么用处会越来越少
  2. 不断增加的复杂度(1974,Increasing Complexity):随着软件系统的发展,它的复杂性会不断增加,除非进行一定的工作来维持或降低复杂度。
  3. 质量降低(1196,Declining Quality):系统的质量将出现下滑,除非进行一定的工作来适应环境变化。

软件演化生命周期模型

  1. 初始开发
  2. 演化
  3. 服务
  4. 逐步淘汰
  5. 停止

3.1 初始开发

初始开发阶段按照传统的软件开发方式完成第一个版本的软件产品开发。第一版的软件产品可以实现全部需求,也可以是(通常是)只包含部分需求 – 对用户来说非常重要和紧急的最高优先级需求(即先做最小可行化产品,实现核心需求)

初始阶段的一个极其重要的工作是:建立一个好的软件体系结构

  • 具有很好的可扩展性,能够包容后续的演化增量
  • 具有很好的可修改性,能够处理后续阶段中的预期变更请求和未预期变更请求。
  • 比较坚实、可靠,能够在后续演化保持稳定的表现

在初始阶段的详细设计也要关注软件系统的可扩展性和可修改性,编程要关注程序的可读性以方便后续阶段的程序理解,技术文档的准备要充分以帮助后续阶段的程序理解。

总结一下:即要注意设计阶段对可扩展性可修改性的设计,要在开发阶段注意程序可读性(包括注释和方法模块的抽象化),同时要准备好技术文档以便后期的维护和演化

3.2 演化

在完成初始开发之后,软件产品就进入演化阶段。该阶段可能会有预先安排的需求增量,也可能完全是对变更请求的处理,它们的共同点都是保持软件产品的持续增值,让软件产品能够满足用户越来越多的需要,实现更大的业务价值

总结一下,该阶段可能的演化增量有:

  1. 预先安排的需求增量(瀑布模型或迭代式开发)
  2. 因为问题变化或者环境变化产生的变更请求
  3. 修正已有的缺陷
  4. 随着用户与开发者之间越来越互相熟悉对方的领域而新增加的需求

演化阶段的软件产品要具备以下两个特征

  1. 软件产品具有较好的可演化性:一个软件产品在演化过程中的复杂性会逐渐增高,可演化性会逐渐降低直至无法继续演化。演化阶段的软件产品虽然其可演化性低于初始开发阶段的软件产品,但是还没有到达无法演化的地步,还具有较好的可演化性
  2. 软件产品能够帮助用户实现较好的业务价值:只有这样,用户才会继续需要改产品,并持续提供资金支持。

如果某个演化产品不满足第二条特征,那么该产品就会进入停止阶段。如果满足第二条但不满足第一条特征,那么该产品就会进入服务阶段。有时也可能出现因为竞品的出现或者其他的市场考虑,会让满足上面两个特征的产品提前进入服务阶段

3.3 服务

服务阶段的软件产品不再持续的增加自己的价值,而只是周期性的修正已有的缺陷。

一个软件产品被置于服务阶段可能是因为它的软件结构已经无法继续演化,也可能是开发团队出于市场考虑,不再重点关注该产品。

服务阶段的产品还仍然能够被用户使用,因为它仍然能够给用户提供一定的业务价值,所以开发团队仍然需要修正已有缺陷或者进行一些低程度的需求增量,保证用户的正常使用。

3.4 逐步淘汰

逐步淘汰阶段,开发者已经不再提供软件产品的任何服务,即不再继续维护该软件。虽然在开发者看来软件的生命周期已经结束,但是用户可能会继续使用处于该阶段的软件产品,因为它们仍然能够帮助用户实现一定的业务价值。只是用户在使用软件时必须要容忍软件产品中的各种不便,包括仍然存在的缺陷和对新环境的不兼容。

对于该阶段的产品,开发者需要考虑该产品是否可以作为有用的遗留资源用于新软件的开发,用户需要考虑如何更换新的软件产品并转移已有的业务数据

3.5 停止

开发者不再维护,用户也不再使用

4 软件维护与演化的技术

// todo

4.1 遗留软件

4.2 逆向工程

4.3 再工程

Reference

  1. 南京大学软件学院2022春季学期《软件工程与计算二》
文章作者: ZY
文章链接: https://zyinnju.com/2022/06/07/软件维护与演化/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ZY in NJU