一个中大型的系统由于业务快速迭代,某些模块不断因为项目的排期,引入临时方案,而临时方案最后又变成了最终方案。
这种技术债务不断积累,导致模块逐步变得僵化,对业务的支撑只能依靠研发团队不断加班,在原有的系统上打补丁来支持,维护成本很高,扩展性不强。
这个过程不断持续,整个系统到处都是坑,到最后,只有用一个新的系统来替代,把老系统下线。
这种粗粒度的重构,通常都涉及比较广的业务范畴,如果考虑不全面,实施不好,会对整个系统造成灾难性的后果。
这两年在部门进行了几次核心子系统的重构,有一些个人理解的重构原则,简单总结一下。
首先,是对遗留系统的功能做减法,从业务层面选择最基础和最核心的功能优先完成。
一个软件系统,20%的功能就可以满足业务方80%的功能,选择这20%的功能首先进行重构,可以在较短的时间构建一个可以使用的版本上线;等新系统上线之后,按照重要程度从剩下的80%的功能点选择一部分迭代开发,对于原来一些不常用的辅助性功能可以直接丢弃,避免不必要的复杂度。
商业系统通常业务流程比较重,并且和整个组织架构的角色权限结合在一起,在重构的过程中,也可以和业务方一起对流程进行精简,进一步降低系统的复杂度。
此外,如果核心领域模型存在多个版本,在重构的时候要在系统内部完成统一,从根上解决基础数据结构多样性带来的业务复杂性。
比如对于电商系统,商品是最基础的领域模型,如果系统内部存在多个版本,业务层需要根据不同的版本进行不同的处理,而这些处理的业务逻辑,可能散落在系统的各个角落,业务代码copy&paste,缺少统一的处理模式,if-else满天飞。
对于这种情况,先要从逻辑层面设计统一的商品模型,另外从物理层面设计数据迁移方案,保证新系统不再出现这种情况。
其次,新系统对遗留系统的替代,是一个类似灰度发布的过程,要设计可行的并行方案,从而降低因为新系统不稳定对业务带来的影响。
新系统是为了解决遗留系统扩展性和可维护性的问题,从整体架构设计角度进行的优化升级,即使是经过充分测试也无法保证上线之后不会出现问题,尤其是在开发节奏快、项目排期紧的情况下。
如果新系统上线后马上下线老系统,因为bug、性能、交互设计等等导致用户满意度下降,这将给新系统的开发团队带来非常大的压力,一方面要快速处理新系统的问题,另外一方面还要开发遗留系统的功能点,这对系统的稳定性带来极大的风险。
规避这种风险的方法就是在项目开始的时候就思考如何引入小流量的方案,逐步引导用户迁移到新系统。
第三,要充分考虑对周边依赖系统的影响,在服务层进行向后兼容设计。
服务层是由一系列的API构成,在重构的时候,通常的思路是设计新版本的API集合,根据新系统的领域模型对输入&输出进行重新设计,同时保留原来的API,这种方案适用于调用方比较少的情况。
如果调用方很多,每个调用方都需要配合进行重构,上面的方法,不管是从项目推动还是风险控制都存在问题。
这种情况,可以考虑引入一个服务分发层,保证API接口不变的情况,根据API调用的上下文,把请求分发给遗留系统和新系统分别处理;分发层聚合调用结果,从而达到对调用方透明的目的。
当遗留系统完全下线之后,在新系统里去掉分发层,完成整个服务层的迁移。
新系统重构完成之后,为了避免经过一段时间的演进,蜕化为另外一个遗留系统,需要在功能迭代过程,处理好质量要求和项目进度的矛盾,这是另外一个值得深入讨论的话题。
Simple is the Best.