演进类型:

  • 更新:让旧的应用依赖和环境不断更新,以免成为一个不可维护的遗留系统。
  • 迁移:在改变小量代码的情况下,让旧的应用可以运行在新的架构上。
  • 重构:对架构的重构,往往从模块、服务级别上对应用进行代码改善。
  • 重写:对系统中的部分功能、应用或者全部功能,使用新的技术栈、语言进行重写。
  • 重写架构:对系统的层面上,对应用进行重新设计、拆分。

迁移与重构、重写最大的不同之处在于,迁移只需要修改少量的代码,就可以使旧的项目系统恢复生机。

应用迁移

应用迁移可以分为如下几个步骤:

  1. 创建全新的运行环境。
  2. 准备接近线上环境的测试数据,如Staging
  3. 执行更细粒度的版本管理控制,以便于回滚。如每一个小的变更,每一个新特性的升级,都需要版本管理工具来记录。
  4. 优先升级次要组件版本,以方便向上兼容。
  5. 逐一升级核心框架,以查找对应的更新日志。
  6. 必要时自己编写依赖。在升级依赖的过程中,我们极有可能遇到的一个问题是,某个依赖不再更新,此时需要自己内联这个依赖。
  7. 清理掉不需要的代码和文件。
  8. 进行完整的用户验收测试。
  9. 上线前使用线上的环境进行预部署。

当然,这样的升级过程过于复杂,还有一种粗暴的迁移方式:

  1. 使用最新版本的依赖,创建一个新的 “hello world”。
  2. 将旧应用的部分代码直接复制到新项目中。
  3. 修改不合适的代码。

这种粗暴的方式并非总是不可取的方案。当然了我们还是比较推崇第一种方案。

架构迁移

架构迁移前

在使用新技术架构迁移旧的应用之前,我们需要考虑以下几个问题:

  1. 旧的系统出现了什么问题?
  2. 拆解部分应用是够能够解决问题?
  3. 有没有平滑迁移的策略?
  4. 是否可能带来新的业务价值?
  5. 如何降低未来的演进风险?

架构迁移中

决定迁移之后,我们可以尝试迁移应用,步骤如下

  1. 构建和提取基础设施,如组件库、代码库;
  2. 确认用于练手的边缘应用。如果失败了、不合适了,那么可以尝试使用其他的模式。
  3. 寻找合适的解耦方式,包含数据、时间等。
  4. 尝试对系统的其他部分进行拆分。
  5. 编写对应的文档及相应的培训。

值得一提的是,无论从零开发创建一个应用,还是迁移到微前端架构,我们都不是从一开就做最复杂的事情。而是不断地在边缘试探,直到觉得合适的时候才开始真正的动手去做。其过程无非是创建新项目,并编写一些 Demo ,再评估该项目是否适合我们。

架构迁移后

在架构迁移后我们应该有以下产出:

  1. 接入使用指南
  2. 团队培训文档
  3. 新架构特性以及不足分析

迁移方式之微前端

迁移到微前端架构的几个步骤:

  1. 寻找合适的微前端技术。
  2. 确认可行的微前端方案。
  3. 尝试使用其中的某些方案。
  4. 对比、讨论几种不同方案的利弊。
  5. 决定适合当前项目实施的方案。
  6. 尝试在一个项目中使用新架构开发。
  7. 编写架构决策记录及文档,记录实施过程中常见的问题。
  8. 对项目成员进行相关培训。

迁移方式之寻找容器

对于浏览器应用来说, iframe 是当前用的比较广泛的前端容器,它可以极大地方便我们迁移旧的前端应用。 Web Components 作为一个新的组件级容器,可以帮助我们兼容不同的应用和组件。只需要略微的修改,便可以在前端应用嵌入其他的应用中去。

桌面应用可以考虑使用 Electron 这样的桌面 GUI 框架。

移动端应用容器种类还比较多的,比如 Cordova + Ionic 的混合应用容器、 React Native 中的 WebView 容器,或者是 Fullter 中的 WebView 容器。拥有了这些容器,便拥有了新的可能性。可以将前端应用迁移到新的容器上,继续发挥原来的价值,而不需要花费大量的功夫,举个例子:

  1. 早先我们拥有一个移动 Web 应用,它可以运行在微信、移动应用中。随着应用越来越复杂,我们将其作为一个独立的应用而存在。为了在短期内看到效果,证明这种方式的可行性,我们思考的第一个方案是将现有应用开发一个混合应用。于是,我们进行一些少量修改,将其运行在诸如 Cordova 这种混合应用框架上。
  2. 后来,有了更多的开发人员想提升更好的用户体验,便尝试编写原生应用。由于开发人员仍是前端开发人员,边使用了React Native 中的 WebView 作为容器。在这种情况下,我们可以一边运行旧的应用,一边使用新的技术栈重写应用。在这个过程中,既保证业务的稳定运行,又提供了一种迁移方案。

重构

提倡代码层面重构。应用的重构是在软件开发过程的行为,而不是维护期进行的工作。

架构重构

在没有测试的情况下实施重构,无异于悬崖上行走。

在时间充沛的情况下,我们要做的第一件事情是引入测试,具体步骤如下:

  • 选择合适的测试框架,并引入项目中。
  • 找到需要重构的代码,编写相应的单元测试用例。
  • 提交相关的测试代码。
  • 准备进行代码重构。

在创建重构的时候不管之前有没有测试,我们重构的流程都相差不大,即:

  • 创建一个新的重构分支。
  • 从简单的重构开始练习。比如目录调整、重命名变量。
  • 小步提交。使用细致化的版本管理,方便出错后代码回滚。
  • 对复杂的代码进行样式拆分、逻辑拆分、组件拆分。对于函数来说,可以采取提取变量的方式进行。
  • 修改或者编写测试,保障业务功能正常。
  • 对于复杂的功能,寻找合适的人一起完成重构。

重写能解决问题吗?

在年轻的时候我们对有一点年限的应用总是有一个相似的看法,为什么我们不重写呢?而资深的程序员,又会同情地告诉我们“重写不会带来业务价值”。那么,怎样才能带来业务价值?下面是问题的答案:

  • 更少的功能完成时间。旧的系统需要3天才能完成的功能,新的系统现在只需要1天就能完成。
  • 更好的用户体验。
  • 提升应用的性能。旧的应用需要3秒才能响应结果,新的系统只需要0.5秒。

那么我们在重写之前,需要考虑哪些要素呢?

  • 重构、迁移、升级真的不能解决问题吗?
  • 重写的时间预期是多少?重写的时间花费往往比预期更长,比技术上花费的时间更短,但理清业务的时间更长。
  • 能接受重写的成本吗?重写不会对业务带来额外的好处,反而是在浪费时间。
  • 是否整理出完整的功能列表?只有清晰的功能列表,才能保证重写不被阻碍。
  • 产品是否领先于市场?在重写期间,我们的开发速度可能会落后于其他的产品。
  • 是否有能力同步维护两个产品团队?在重写期间,需要在新旧应用里实现同样的功能。
  • 在重写完成之前,是否可能变为遗留系统?要进行合理的技术选型,以避免重写失败。

因此只有当 重写应用的花费 + 维护应用的花费 < 维护现有系统的花费 时,我们才应该考虑重写。同时在重写的过程中,我们还要维护现有项目的正常运行。

重构架构

代码难以维护导致我们想重写系统,这其中的原因有:

  • 代码结构不合理
  • 依赖设计不合理

重构架构的核心目标是:将系统变成高内聚、低耦合的系统。