演进类型:
- 更新:让旧的应用依赖和环境不断更新,以免成为一个不可维护的遗留系统。
- 迁移:在改变小量代码的情况下,让旧的应用可以运行在新的架构上。
- 重构:对架构的重构,往往从模块、服务级别上对应用进行代码改善。
- 重写:对系统中的部分功能、应用或者全部功能,使用新的技术栈、语言进行重写。
- 重写架构:对系统的层面上,对应用进行重新设计、拆分。
迁移与重构、重写最大的不同之处在于,迁移只需要修改少量的代码,就可以使旧的项目系统恢复生机。
应用迁移
应用迁移可以分为如下几个步骤:
- 创建全新的运行环境。
- 准备接近线上环境的测试数据,如Staging
- 执行更细粒度的版本管理控制,以便于回滚。如每一个小的变更,每一个新特性的升级,都需要版本管理工具来记录。
- 优先升级次要组件版本,以方便向上兼容。
- 逐一升级核心框架,以查找对应的更新日志。
- 必要时自己编写依赖。在升级依赖的过程中,我们极有可能遇到的一个问题是,某个依赖不再更新,此时需要自己内联这个依赖。
- 清理掉不需要的代码和文件。
- 进行完整的用户验收测试。
- 上线前使用线上的环境进行预部署。
当然,这样的升级过程过于复杂,还有一种粗暴的迁移方式:
- 使用最新版本的依赖,创建一个新的 “hello world”。
- 将旧应用的部分代码直接复制到新项目中。
- 修改不合适的代码。
这种粗暴的方式并非总是不可取的方案。当然了我们还是比较推崇第一种方案。
架构迁移
架构迁移前
在使用新技术架构迁移旧的应用之前,我们需要考虑以下几个问题:
- 旧的系统出现了什么问题?
- 拆解部分应用是够能够解决问题?
- 有没有平滑迁移的策略?
- 是否可能带来新的业务价值?
- 如何降低未来的演进风险?
架构迁移中
决定迁移之后,我们可以尝试迁移应用,步骤如下
- 构建和提取基础设施,如组件库、代码库;
- 确认用于练手的边缘应用。如果失败了、不合适了,那么可以尝试使用其他的模式。
- 寻找合适的解耦方式,包含数据、时间等。
- 尝试对系统的其他部分进行拆分。
- 编写对应的文档及相应的培训。
值得一提的是,无论从零开发创建一个应用,还是迁移到微前端架构,我们都不是从一开就做最复杂的事情。而是不断地在边缘试探,直到觉得合适的时候才开始真正的动手去做。其过程无非是创建新项目,并编写一些 Demo
,再评估该项目是否适合我们。
架构迁移后
在架构迁移后我们应该有以下产出:
- 接入使用指南
- 团队培训文档
- 新架构特性以及不足分析
迁移方式之微前端
迁移到微前端架构的几个步骤:
- 寻找合适的微前端技术。
- 确认可行的微前端方案。
- 尝试使用其中的某些方案。
- 对比、讨论几种不同方案的利弊。
- 决定适合当前项目实施的方案。
- 尝试在一个项目中使用新架构开发。
- 编写架构决策记录及文档,记录实施过程中常见的问题。
- 对项目成员进行相关培训。
迁移方式之寻找容器
对于浏览器应用来说, iframe
是当前用的比较广泛的前端容器,它可以极大地方便我们迁移旧的前端应用。 Web Components
作为一个新的组件级容器,可以帮助我们兼容不同的应用和组件。只需要略微的修改,便可以在前端应用嵌入其他的应用中去。
桌面应用可以考虑使用 Electron
这样的桌面 GUI
框架。
移动端应用容器种类还比较多的,比如 Cordova
+ Ionic
的混合应用容器、 React Native
中的 WebView
容器,或者是 Fullter
中的 WebView
容器。拥有了这些容器,便拥有了新的可能性。可以将前端应用迁移到新的容器上,继续发挥原来的价值,而不需要花费大量的功夫,举个例子:
- 早先我们拥有一个移动
Web
应用,它可以运行在微信、移动应用中。随着应用越来越复杂,我们将其作为一个独立的应用而存在。为了在短期内看到效果,证明这种方式的可行性,我们思考的第一个方案是将现有应用开发一个混合应用。于是,我们进行一些少量修改,将其运行在诸如Cordova
这种混合应用框架上。 - 后来,有了更多的开发人员想提升更好的用户体验,便尝试编写原生应用。由于开发人员仍是前端开发人员,边使用了
React Native
中的WebView
作为容器。在这种情况下,我们可以一边运行旧的应用,一边使用新的技术栈重写应用。在这个过程中,既保证业务的稳定运行,又提供了一种迁移方案。
重构
提倡代码层面重构。应用的重构是在软件开发过程的行为,而不是维护期进行的工作。
架构重构
在没有测试的情况下实施重构,无异于悬崖上行走。
在时间充沛的情况下,我们要做的第一件事情是引入测试,具体步骤如下:
- 选择合适的测试框架,并引入项目中。
- 找到需要重构的代码,编写相应的单元测试用例。
- 提交相关的测试代码。
- 准备进行代码重构。
在创建重构的时候不管之前有没有测试,我们重构的流程都相差不大,即:
- 创建一个新的重构分支。
- 从简单的重构开始练习。比如目录调整、重命名变量。
- 小步提交。使用细致化的版本管理,方便出错后代码回滚。
- 对复杂的代码进行样式拆分、逻辑拆分、组件拆分。对于函数来说,可以采取提取变量的方式进行。
- 修改或者编写测试,保障业务功能正常。
- 对于复杂的功能,寻找合适的人一起完成重构。
重写能解决问题吗?
在年轻的时候我们对有一点年限的应用总是有一个相似的看法,为什么我们不重写呢?而资深的程序员,又会同情地告诉我们“重写不会带来业务价值”。那么,怎样才能带来业务价值?下面是问题的答案:
- 更少的功能完成时间。旧的系统需要3天才能完成的功能,新的系统现在只需要1天就能完成。
- 更好的用户体验。
- 提升应用的性能。旧的应用需要3秒才能响应结果,新的系统只需要0.5秒。
那么我们在重写之前,需要考虑哪些要素呢?
- 重构、迁移、升级真的不能解决问题吗?
- 重写的时间预期是多少?重写的时间花费往往比预期更长,比技术上花费的时间更短,但理清业务的时间更长。
- 能接受重写的成本吗?重写不会对业务带来额外的好处,反而是在浪费时间。
- 是否整理出完整的功能列表?只有清晰的功能列表,才能保证重写不被阻碍。
- 产品是否领先于市场?在重写期间,我们的开发速度可能会落后于其他的产品。
- 是否有能力同步维护两个产品团队?在重写期间,需要在新旧应用里实现同样的功能。
- 在重写完成之前,是否可能变为遗留系统?要进行合理的技术选型,以避免重写失败。
因此只有当 重写应用的花费 + 维护应用的花费 < 维护现有系统的花费 时,我们才应该考虑重写。同时在重写的过程中,我们还要维护现有项目的正常运行。
重构架构
代码难以维护导致我们想重写系统,这其中的原因有:
- 代码结构不合理
- 依赖设计不合理
重构架构的核心目标是:将系统变成高内聚、低耦合的系统。