软件如何优雅地向前兼容?

 大家好,我是Z哥。

应该有不少程序员受过这样的困扰,特别是做后端的。项目一直在持续不断的迭代,但是呢老版本的代码还不能直接删掉,因为不一定所有的调用端都能在同一时间点完成升级。

你是怎么处理这种情况的呢?

方法上增加参数?方法实现里增加if else?

论难度的话,这个问题是不难,肯定每个人都有办法解决。但是解决的是否优雅?是否会给后续带来更大的成本?有这考虑的人可能就不多了。

我看到过一些项目里做兼容的方法是真的粗暴,直接怼if else。

比如,原本创建一笔交易的时候,我们需要的数据是用户的姓名、收货地址、邮编。现在迭代的一个新版本需要增加手机号信息,并且不需要邮编了。

粗暴的写法怎么写呢?

if(postData != null ){

    if(postData.mobile != null){

        //新版本的处理方式

    }

    else{

        //旧版本的处理方式

    }

}

如果以后再新加了一个参数呢?if else再多嵌套一层……

你见过类似上面那样的代码吗?

这可能是最容易想到的改法,但针对向前兼容不仅仅只有这一种方式。容我一个个来说。

/01  通过接口上增加版本号/

最常用的增加版本号的方式是直接加到接口上。比如,

http://api.xxx.com/1/user/login

如果后续的改动完全是新业务的追加,那么可以继续沿用这个1版本的接口。如果有做删减,那么可以新增一个版本2的接口,调整后的业务逻辑在这个版本的接口里实现。

http://api.xxx.com/2/user/login

这样一来,你的项目中就同时存在了两个版本的接口。新的客户端调用2版本的,老的客户端调用1版本的。

在这个期间,让客户端尽快都升级到2版本,然后后续就把1版本的接口删掉。

有的人有代码洁癖,觉得一个项目里同时存在两个相同业务逻辑的接口不太舒服,那么可以试试下面这个方法。

/02  一个前端版本对应一个后端版本/

与上面的区别就是,在项目的代码里,你不用新增加一个接口出来,直接把原先的接口修改成新的业务逻辑即可。

但是呢,新版本上线的时候,老版本不能被全部替换掉,需要保留一段时间给旧版本的客户端使用。这件事的逻辑与前面一样,给客户端一段时间来升级。

其实所谓的「服务发现」或者说「服务治理」就是这类思想的体现,只是玩的更加体系化。

在服务发现的服务端,针对不同的服务本身就有版本的概念,比如:

OrderService/v1 : 192.168.0.1:8000 , 192.168.0.1:8001

OrderService/v2 : 192.168.0.1:8100 , 192.168.0.1:8101

UserService/v1 : 192.168.0.1:9000 , 192.168.0.1:9001

UserService/v2 : 192.168.0.1:9100 , 192.168.0.1:9101

……

客户端调用的时候,根据其传入的版本号,自动路由到对应版本的服务目标地址。

当然这个方案有一个问题是,由于产生环境同时运行着多个版本的程序,所以数据库的字段只能增加,不能删除,并且增加的字段需要给上默认值。

删除字段只能随着后续旧版本的去除进行。

除了以上两种方案外,我们还可以做一些其他的工作以保障向前兼容更好地进行。

/01  数据库设计预留扩展字段/

我们在设计数据表的时候,如果对后续业务预判会扩展。那么可以在做数据表设计的时候预留几个扩展字段(ext1,ext2,ext3,……)。

这样的话配合上面提到的方案2能更好地进行,因为后续新增的字段可以暂时存在这些扩展字段里,不需要对表结构作出变更。

/02  APP提供强制更新功能/

如果是针对APP的向前兼容,那么务必在APP里留出一个强制更新的口子。以免有些用户长期不升级,导致你的旧版本迟迟无法去除。

针对你可以承受的多版本情况,可以给予提示性的更新提醒,由用户自行选择是否更新。如果针对少数隔了多个版本还未升级的用户,可以给出强制性的更新,让其无法继续在旧版本上操作。

强制更新也分局部更新和全量更新。现在针对局部更新的「热更新」受到了一些应用市场的限制,所以在用「热更新」之前谨慎了解对应平台的规则。

另外,向前兼容不可能是无止境,不丢掉一些包袱,不断的新增包袱只会让后续的维护成本不堪重负。

所以需要做好对各个版本使用情况的监控,一旦某个版本的使用量低于某个值,就应该给予强制更新的提醒。

其实类似的向前兼容问题,除了api之外,在对外提供的三方库SDK、公司内给其他项目调用的二方库SDK中,也有一样的情况。

他们的解决思路也是类似的。

最后我建议你多看一些知名的开源项目,从中你可以学到很多优雅的编程技巧和代码设计理念。

好了,总结一下。

这篇呢Z哥和你分享了软件向前兼容的一些方法。除了粗暴的if else之外,你可以尝试一下以下两种方法:

  1. 通过接口上增加版本号

  2. 一个前端版本对应一个后端版本

除此之外,以下这两件事也对做好向前兼容有很大帮助。

  1. 数据库设计预留扩展字段

  2. APP提供强制更新功能

希望对你有所帮助。

不要光顾收藏,记得用起来哦。

推荐阅读:

  • 为什么我们总是「习惯性辩解」?

  • 致后浪:6句肺腑之言,望你少走弯路

原创不易,如果你觉得这篇文章还不错,就「在看」或者「分享」一下吧。鼓励我的创作 :)

如果你有关于软件架构、分布式系统、产品、运营的困惑

可以试试点击「阅读原文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307337.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode293周赛5234. 移除字母异位词后的结果数组

一:题目 二:上码 class Solution { public:/**1.所谓字母异位词就是相同的字母组成的字符串(这个字符串可以相同)2.首先判断两个字符串中的字母是否相同用map进行计数,然后在另一个字符串中查找某个字符进行--;3.如果最终的map中所有的value…

七国要求科技巨头预留后门 应对马甲芯片高度警惕

日前,美国、英国、日本、澳大利亚、加拿大、新西兰、印度呼吁科技公司设计产品时确保政府可以访问经过加密的消息和内容。其实,西方科技公司配合西方政府在产品中预留后门早已不是新鲜事,斯诺登就曾经爆料多个美国科技巨头配合美国情报部门收…

leetcode78. 子集

一:题目 二:上码 class Solution {/**横向递归的是:我们子集的长度纵向递归的是:我们子集的中元素的更改*/List<List<Integer>> ans new ArrayList<>();List<Integer> path new ArrayList<>();public void getAns(int[] nums,int st) {ans.ad…

跟我一起学Redis之看完这篇比常人多会三种类型实战(又搞了几个小时)

前言对于Redis而言&#xff0c;很多小伙伴只关注其关键的五大基础类型&#xff1a;string、hash、list、set、sorted set(有序集合)&#xff0c;其实还有三种特殊类型在很多应用场景也比较适合使用&#xff0c;分别是&#xff1a;bitmap、geospatial、hyperloglog&#xff1b;上…

leetcode90. 子集 II(树层去重)

一:题目 二:上码 class Solution {/**- 先排序- 树层去重:那么直接在从横向循环中去重即可*/List<List<Integer>> ans new ArrayList<>();List<Integer> path new ArrayList<>();public void getAns(int[] nums,int st) {ans.add(new ArrayLi…

Rancher 2.5 正式发布,多项重大更新来袭!

喜欢就关注我们吧&#xff01;近日&#xff0c;Kubernetes 管理平台 Rancher 2.5 正式发布&#xff0c;官方公布了在 Rancher 2.5 中的多项重大更新&#xff0c;包括全新的安装体验、针对边缘集群的大规模 GitOps 以及 EKS 集群的全生命周期管理。具体更新信息如下&#xff1a;…

leetcode491. 递增子序列(树层去重魔改版)

一:题目 二:上码 class Solution {/**思路:1.这里已经指明了不允许有重复的&#xff0c;那么树层去重 树枝去重(以前用过的元素不再使用)2.坑:这里不能用 传统的树层去重了 因为我们无法排序了3.这里给出的解决办法是 是利用 hashmap,对每个数出现的频率进行统计如果其频率…

数据结构与算法专题——第十二题 Trie树

今天来聊一聊Trie树&#xff0c;Trie树的名字有很多&#xff0c;比如字典树&#xff0c;前缀树等等。一&#xff1a;概念 下面有and,as,at,cn,com这几个关键词&#xff0c;构建成 trie 树如下。从上面图中&#xff0c;应该可以或多或少的发现一些好玩的特性。根节点不包含字符&…

leetcode46. 全排列

一:题目 二:上码 class Solution {/**1.全排列问题,我们的横向都是从0开始不再是st了 因为我们用到前面已经使用过的元素单是在纵向递归当中我们不能使用重复的元素&#xff0c;这里我们用used来去重。*/private List<List<Integer>> ans new ArrayList<>(…

leetcode47. 全排列 II

一:题目 二:上码 class Solution {private List<List<Integer>> ans new ArrayList<>();private List<Integer> path new ArrayList<>();private boolean[] used;public void getAns(int[] nums,boolean[] used) {if (path.size() nums.leng…

golang项目目录推荐

序言 逛GitHub的时候发现有个4.5k对goalng项目结构的推荐的项目&#xff0c;这里就简单的推荐下 文件目录 /cmd 项目主要的应用程序。 对于每个应用程序来说这个目录的名字应该和项目可执行文件的名字相匹&#xff08;例如&#xff0c;/cmd/myapp&#xff09;。不要在这个…

C# 很少人知道的科技

本文来告诉大家在C#很少有人会发现的科技。即使是工作了好多年的老司机也不一定会知道这些科技&#xff0c;如果觉得我是在骗你&#xff0c;那么请看看本文的内容。原本最初 C# 的设计是简单和高效开发的&#xff0c;在经过了这么多年众多公司和开发者的努力下&#xff0c;整个…

Typescript前端接口联调自动化的探究与实践

源宝导读&#xff1a;Web应用程序一般都是前后端分离的基本架构&#xff0c;而前后端很可能分别是两拨人分别开发&#xff0c;前后端的接口连调成为高频沟通的对象&#xff0c;开发内耗最大的也在这个环节。本文将分享如何基于OpenAPI将前后端接口协议标准化和自动化&#xff0…

leetcode51. N 皇后(java详解)

一:题目 二:上码 class Solution {/**思路:1.先说我们选用的数据结构;我们是选取的是List<List<string>> ans 来存每次的结果;我们在创建这个二维矩阵的时候用的是char的二维数组,那么的话等到我们得到一种可行解的时候 将char的二维数组每一行转换成 String 并存…

程序员如何跨越35岁危机?这篇给点干货建议!

职场&认知洞察 丨 作者 / findyi这是findyi公众号的第83篇原创文章这两天在我的读者群里做了一个职业小调研&#xff0c;发现关注我公众号的70%以上都是程序员。毕竟程序员吸引程序员&#xff0c;这也算猿粪吧&#xff0c;哈哈。这个小调研也引发大家对程序员行业的激烈探讨…

leetcode455. 分发饼干

一:题目 二:上码 class Solution {public int findContentChildren(int[] g, int[] s) {int ans 0;int gIndex 0;int sIndex 0;Arrays.sort(g);Arrays.sort(s);while (gIndex < g.length && sIndex < s.length) {if (s[sIndex] > g[gIndex]) gIndex; //只…

写了多年代码,你会 StackOverflow 吗

写了多年代码&#xff0c;你会 StackOverflow 吗Intro准备写一个傻逼代码的系列文章&#xff0c;怎么写 StackOverflow 的代码&#xff0c;怎么写死锁代码&#xff0c;怎么写一个把 CPU 跑满&#xff0c;怎么写一个 OutOfMemory 的代码。今天主要来看 StackOverflow&#xff0c…

C#实现迭代器

迭代器模式&#xff08;Iterator&#xff09;&#xff0c;提供一种方法顺序访问一个聚合对象中的各种元素&#xff0c;而又不暴露该对象的内部表示。C#中使用IEnumerator接口实现&#xff0c;Java中使用Iterator接口实现&#xff0c;其中原理都差不多&#xff0c;下面我就用C#代…

从CLR GC到CoreCLR GC看.NET Core为云而生

内存分配概要前段时间在园子里看到有人提到了GC学习的重要性&#xff0c;很赞同他的观点。充分了解GC可以帮助我们更好的认识.NET的设计以及为何在云原生开发中.NET Core会占有更大的优势&#xff0c;这也是一个程序员成长到更高层次所需要经历的过程。在认识GC的过程中&#x…

springboot邮件发送(牛客论坛项目之QQ邮箱发送)

一:邮箱发送原理 1:狂神图解 张三通过smtp协议连接到Smtp服务器&#xff0c;然后发送一封邮件给网易的邮件服务器网易分析发现需要去QQ的邮件服务器&#xff0c;通过Smtp协议将邮件转投给QQ的Smtp服务器QQ将接收到的邮件存储在456789qq.com这个邮件账号的空间中李四通过Pop3协…