本文转载自微信公众号「跨界架构师」,接手作者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。过程是这样的: 随着时间的推移,数据慢慢会从旧库同步到新库。剩下还未同步的数据使用同步工具做一次全量同步。 原则上,以上的每一个方案在实际切流量之前都要对数据做核对,确保两边的数据一致。 最后迁移完后可以通过流量回放工具(如阿里的 jvm-sandbox-repeater )或者全链路压测工具来验证新版本的系统是否能运行地符合预期。 好了,总结一下。 这篇呢Z哥和你分享了应对老项目的思路,总的有5种: 我建议根据业务敏捷、运营效率、客户洞见、系统本身的韧性和弹性来考虑选择哪一种。 接下来专门对重写展开细说了一下,建议遵守三个原则: 最后分享了3个常用的新老数据迁移方案。一 重写
二 重构