您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件开发专栏 > 开发技术 > 正文

接手历史悠久的老项目,干or跑?

发表于:2021-07-09 作者:Zachary 来源:跨界架构师

大家好,我是Z哥。

这是我的第199篇原创,离200篇还有一步之遥。为了庆祝这重要时刻,我也在琢磨着要不要在第200篇的时候搞个什么小活动。还没想好,等下周再看吧。

不过不管怎样,你也应该为Z哥的坚持点个赞吧,哈哈。所以,看完文章后不要忘了划到文末点赞哦~

好了,回到正题。

很多人吐槽老项目是怎么怎么垃圾。这的确是一个很大的问题。根据 IEEE Spectrum 之前发布的一份报告数据:

自 2010 年以来,全世界IT 产品和服务支出约为 35 万亿美元,其中约四分之三用于运营和维护现有的 IT 系统。另外,至少有 2.5 万亿美元用于尝试替换旧的 IT 系统,而且其中约有 7200 亿美元被浪费在失败的替换工作上。

https://spectrum.ieee.org/computing/it/inside-hidden-world-legacy-it-systems

因此,面对不堪的、充满历史味道的老项目对程序员群体来说会是一个常态。

不过我们可以做些什么呢?难道是要么硬着头皮去填别人挖的坑,要么跑路么?

如果你不跑路的话,最常见的应对方式无非是「重写」或者「重构」。

一 重写

一般来说,越是年轻的程序员越是最喜欢重写。除了年轻气盛的原因外,主要是因为完全摆脱了老代码的技术债和束缚,可以尽情地施展自己的才华。

但是对于老系统的重写,如果没有高层的支持、业务部门的配合,靠谱的开发团队,这事大概率很难能按预期完成。

而且选择「重写」方案不仅仅是重新写一套代码这么简单,系统切换也是一个头疼的事情,在复杂一些的项目里,它的难度并不亚于重写一套代码。

而且一旦选择「重写」,意味着在一段时间内要么老系统并行开发,要么会攒一堆需求,业务方能不能持续支持「重写」也会面临很大的挑战。

就算这些问题都能搞定。「重写」还可能造成的一个结果是:在过了几个月后,重写后的系统又成了别人眼中的“破烂不堪的老系统”。这是最讽刺的……

如果在重写过程中,由于任何原因牺牲了对质量的要求,这个讽刺很容易成为现实。

因此,很多公司之所以不支持重写,理由是:

又不是不能用。

这么看来他们并不是迂腐,也是一种理性的选择,也是没错的。

二 重构

相对地,年纪越大的程序员可能更倾向于重构。毕竟他们有更多的机会见过、经历过那些惨痛的重写事件。当然更主要的原因是,他们掌握了更多应对“垃圾代码”的方式方法,认为通过小范围的代码重构也能化腐朽为神奇,提升项目质量。

的确没错,但这是有一个前提的。就是垃圾代码的产生速度要低于重构的速度。可是对大部分中小公司来说,开发团队可能不具备这样的条件。因为技术好的程序员要么走向了管理岗位,要么身上的重担太多,没太多时间来做重构。再加上技术差的程序员继续复制粘贴,挖更多的坑。通过重构来改善老项目,可能永远在“重构的路上”。

而且,如果项目的技术复杂度大于业务复杂度,那么配合重构的测试成本就很高了。

除了这两种常见的方式外,还有其它的方式也能应对这些老系统。它们的的风险和收益在下图里已经很好地呈现出来了。

其实Z哥认为,这些方式我们都可以用,但是要沿着正确的思路来。(绞杀是什么?往下看)

首先从业务角度来考虑到底是选择哪种方案。

  • 从业务敏捷视角来看,能不能让响应力变得更快?
  • 从运营效率视角来看,如何通过系统改造,提升业务运营的效率?
  • 从客户洞见视角来看,如何让系统更好地发现客户洞见,进而更好地理解客户需求和演进产品?

其次是从系统本身来考量,比如,我们想通过改造获得多大的弹性?

最后,根据整体上对业务帮助的价值大小来选择哪一种方式。如果对业务的帮助巨大,完全值得投入大量资源去重写,那么就不要畏畏缩缩的小范围重构。如果对业务帮助很小,那么也就别想着重写了,老老实实在小范围内修修补补就好了。

当你拿捏不准方案的时候,选择按模块替换会是一个不错的办法,相当于通过每一次换一个零部件,把整个机器上的零部件全部翻新一遍,以此完成重写的效果。Martin Fowler 称之为「绞杀法」。

「绞杀法」的最佳实践是DDD+微服务,因为它们可以提供更好的「隔离性」「自治性」,更有利于「替换」的进行。

当确定好改造的方案后,还需要制定度量指标,在实施改造的过程中持续关注这些指标的变化。最好能够通过可视化工具将这些信息共享出来,这样的好处有两点,

  • 一是能够让团队了解改造进展和成果,确保改造朝着正确的方向走;
  • 二是能够让相关的干系人(领导、业务部门)也能了解到工作的进展情况,提高对预期的确定性,以持续获得他们的支持。

关于具体重构方法我就不说了,可以翻看我之前的一篇文章《好的重构方法才能摆脱“屎山”》。

关于重写可以来聊几句,之前我们没聊过。我的建议是遵守以下几个原则。

系统具备演进的能力。

凭借度量指标来把握演进的方向。

小步快速迭代。

01 系统具备演进的能力

既然是重写系统,那么一定要为可见的未来做一些预留,方便后续的演进。毕竟能够决定重写的系统自然是比较核心的系统,不管在技术上还是在业务上都会持续发生变化,注重系统的演进能力就是在降低未来项目复杂度增长的幅度。

具体的方法可以概括为3个词:抽象、分类(分层)、解耦。

02 凭借度量指标来把握演进的方向

前面也提到了度量指标的价值。这里继续强调一下它的重要性。

如果说在重写时期,度量指标是衡量重写工作完成好坏的尺子,那么在重写后它就是指引未来系统演进的明灯。

不管系统后续要做什么升级改造,对自身了解的越清楚,做出的决定和选择自然越合理。而度量指标起到的就是这个效果。

一个缺少度量指标的系统,随着业务逻辑的不断堆砌,很容易自由生长过了头。

03 小步快速迭代

近些年随着CI/CD的普及,小步快跑式的敏捷开发被越来越多人提到,并且开始运用。

它的好处是显而易见的,对项目的可控性更高,容错性更强。

如果你不知道“小步”该多“小”?你就按发布出了问题能不能快速回滚作为标准去考虑就好了。能快速回滚的迭代节奏就是“小步”。

其实重写系统完之后还有一个头疼的事情要解决,就是数据迁移。业界常用的方案有四种,我先大致列一下主要思路,后续再发文展开聊聊。

01 双写

  • 新库配置为旧库的从库,从旧库同步数据。
  • 数据写入的时候,不仅要写入旧库,也要写入新库。
  • 数据校验。
  • 两边数据完全同步后,灰度切流量。

由于有双写的存在,所以在切换的过程中出现任何的问题,都可以将读写流量随时切换到旧库去,安全感极强。

02 异步双写+对账

这个方案其实是双写的变种,将同步写入变成了异步写入。也因此需要一个对账机制,确保最终一致性的达成。

一般这个异步的机制要么通过 MQ ,要么通过数据库的 binlog 进行。

03 直接用新库,数据惰性迁移

这个方案中间需要架设一个缓存层,用来存放数据的key。过程是这样的:

  • 先访问新库,如果有数据则直接进入步骤 3 ,如无数据进入步骤 2 。
  • 访问旧库,找到相应的数据 Insert 到新库,再进入步骤 3 。
  • 进行业务逻辑的操作……

随着时间的推移,数据慢慢会从旧库同步到新库。剩下还未同步的数据使用同步工具做一次全量同步。

原则上,以上的每一个方案在实际切流量之前都要对数据做核对,确保两边的数据一致。

最后迁移完后可以通过流量回放工具(如阿里的 jvm-sandbox-repeater )或者全链路压测工具来验证新版本的系统是否能运行地符合预期。

好了,总结一下。

这篇呢Z哥和你分享了应对老项目的思路,总的有5种:

  • 重构
  • 重新部署
  • 更换平台
  • 重写
  • 绞杀

我建议根据业务敏捷、运营效率、客户洞见、系统本身的韧性和弹性来考虑选择哪一种。

接下来专门对重写展开细说了一下,建议遵守三个原则:

  • 系统具备演进的能力。
  • 凭借度量指标来把握演进的方向。
  • 小步快速迭代。

最后分享了3个常用的新老数据迁移方案。

  • 双写
  • 异步双写+对账
  • 直接用新库,数据惰性迁移