仓库源文

.. Kenneth Lee 版权所有 2018-2020

:Authors: Kenneth Lee :Version: 1.0

交付中的Version和Revision


本文为交付建立一个概念空间,以便更容易讨论问题。

当我们交付一个部件,我们会给它一个版本号,比如1.0。这个我称为Version。这个 Version会交给一个(组)用户使用,这些用户在使用的过程中,会发现问题,会要求我修 改,这时我需要修改它,修改完了,它是否还叫1.0呢?我确实仍可以叫它1.0,因为从用 户的角度来说,这个版本和原来的版本没有区别,可以直接替换。但在我的家里,我肯定 不能把它叫1.0了,因为它的内容不一样了,如果我要修改什么东西,必须在这个修改的版 本的基础上进行修改。

为了方便表述,我把这个新的内部的版本叫1.0.1。这样的版本,我称为Revision。我会不 断发现Bug并进行修复,我们Revision会发生积累,但对用户来说,我的版本仍是1.0,当 我跟他说,我卖了一个1.0的产品给他的时候,我指的主要是最后一个Revision,或者说是 这个Version的Head。

但是,我交付了Version 1.0给客户A,并把Revision升级到了1.0.3,这时客户B进来了, 我也用这个Head给他交付,但当我升级1.0.3到1.0.4的时候,A客户愿意升,B客户不愿意 。这时我们产生了两种策略:

  1. A使用Version 1.0的Revision1.0.4,B使用Version 1.0的Revision 1.0.3,B落后于 Version 1.0的Head,未来他要升级,只能继续追Version 1.0的HEAD。

  2. A使用Version 1.0的Revision 1.0.4,B使用Version 1.1的Revision 1.1.0=1.0.3。A 后续在1.0.x的revision上升级,而B后续在1.1.x的revision上升级

对于后面这种情况,我认为我们交付了“两个版本”。对应的前者我们认为只是交付了一个 版本。其区别在于我需要维护多少个HEAD:

    .. figure:: _static/两个版本.jpg

当我们进行配套,测试,升级,维护(修Bug)等行为的时候,多一个不同的HEAD,都多一 套工作。

    | 版本是个对外的概念,在内部开发上,版本其实就是“分支”。
    | 因为一旦我们对外承诺了一个Version,我们开发的时候就必
    | 须记录在这个Version上我们的修改过程。

这种版本和A、B是否同一个客户无关,同一个客户,在不同的设备上也可以因为维护策略 的不同,产生不同的分支。比如我们先做了一个设备H1,支持软件V1.0,然后我们出了设 备H2,和H1混用,那么我的软件是升级到V1.0.x去同时支持呢?还是已经部署的设备继续 用V1.0.x,新的设备用两者兼容的V2.0.x去支持呢?这也会事实上造成版本分裂的结果。 因为如果你不用后一个方案,前一个方案要出补丁的时候,你是要出两个不一样的补丁的 。如果你的软件要和另一个软件配套,你也需要测试两次。下面是一个示意:

    .. figure:: _static/软件配套.jpg

这种版本也和交付是源代码还是二进制无关,我们只关心你交出去的那个东西,是否有人 回头来找你,要求在那个确切交出去的东西上面作进一步的修改。关键在于你的交付界面 ,不是这个交付的形式是二进制还是源代码。你用一套源代码编译出两个二进制的Version ,你说这是同一个Version,这个没有意义,因为如果你真可以认为这是一个Version,你 根本就应该该出去一个二进制。你给的是两个版本,就会预期它可以和其他部件有不同的 配套关系。

如果你交付的是源代码,就算用户编译出很多个实例在他的环境中使用,他回来找你要版 本,你也是升级这个源代码版本给他,那么你就仅交付一个版本,测试也仅仅测试一个版 本,他自己编译了多少个平台,都和你无关。(但要注意,如果你的源代码说要支持多个 OS或者硬件的版本,那么你的测试工作量不会减少,但这不影响你交付的时候就是交付一 个Version。)

如果你交付的界面是二进制,甚至你仅仅是修改了一下编译选项,同一个源代码编译出两 个二进制版本给用户。我们都认为它构成了两个Version。因为它们需要不同的配套,比如 你一个是big-endian编译的,一个是little-endian编译的,那么你要给他们加一个插件, 那个插件也需要两个二进制版本进行交付版本配套和测试啊。

    | 对于版本这个概念我们需要反复给读者们强调:“版本”是因为你
    | 的行为和承诺产生的,不是你是否把它叫做版本。你说“我只有一个版本,
    | 名字叫1.0,但我给的时候,给A客户和B客户的东西不是同一个……”
    | 这就是两个版本,不是因为你把他们都叫做1.0就不是两个版本了。
    | 因为发生了Bug,你还是要找那个“不同”的分支(编译选项不同也是不同,
    | 你一样要记录那个区别才能给那个客户承诺),在那个分支上给他解决问题。

这问题为什么重要?关键就是这个配套关系。比如你这个软件S和硬件H有配套关系,为了 保证这种配套是可靠的,我们每次出版本,就必然需要在所有的组合中进行测试。假设H有 H1.0和H2.0两个版本,你的软件有V1.0和V1.1两个版本,我们每次出厂,都会测试V1.0的 HEAD和H1.0/H2.0的配套,以及V1.1的HEAD和H1.0/H2.0的配套。但我们不会测试其他的 Revision的版本配套关系,否则我们就会有无数的组合了。

    .. figure:: _static/软件配套2.jpg

考虑到每个客户的解决方案中都会有无数的部件和之间有匹配关系,减少Version的数量, 我们才有可能保证我们的测试是可控的。但Version太少,就很难满足客户的需要,因为你 任何需要都要求用户升级到HEAD上,那么每个用户都会被很多的其他用户的要求所绑架。

你很Nice,用户喜欢升级就升级,喜欢不升级就不升级,关键在于你把他的要求看作是一 种Revision还是一种Version,如果看作Revision,问题很简单,问题他们自己负责,如果 他们负责不了,就追到HEAD上。但如果我们把他的需求看作Version,那么就是我们需要为 他的问题负责了。

    | 如果读者需要更切身的体会,我们再看一个例子:
    | 比如Ubuntu 18.04是一个Ubuntu的Version,
    | 18.04.1是它的Revision,内核是4.15,
    | 后来它的Revision变成18.04.3,内核升级到5.3。
    | 现在你做一个支持Ubuntu 18.04的版本,
    | 那么你出的版本就应该升级到18.04.3,支持5.3的内核。
    | 但你的客户不一定同意,要求你支持18.04.1,你很Nice,
    | 你答应了。这样马上就会把18.04.1变成一个Version
    | (而不是一个Revision),但Version是需要支持的,
    | 现在你自己决定支持18.04.1,那么你天然“制造”了一个Version,
    | 那你就要准备好给客户解决CustomizedUbuntu 18.04.1-X这个版本
    | 的后续Revision如何升级的问题了。比如他发现他用的openssl有个
    | 安全漏洞,就应该是你给他修这个Bug(Ubuntu只会在18.04.3上
    | 修这个Bug),就算不是Openssl,你只是换掉了内核,
    | 那这个内核的Bug,也都需要你来修,因为Ubuntu的维护中已经看
    | 不见这个Revision了,他只能看见他的Head。我们控制Version是控制
    | 维护的可信度和工作量,不是人为“认为”有多少个版本,这个问题不能
    | 强行通过定义解决的。

Version和Revision可以叠加的,这会让问题变得更加的复杂。以前面这个Ubuntu的例子为 例。你在使用20.04这个Version,然后现在OpenSSL中发现了一个Bug,OpenSSL的revision 从1.1.1e,升级到1.1.1f。但为了叠加管理,18.04的HEAD虽然移动了,但维护者并没有升 级这个Revision,所以你听起来觉得这个Revision还是20.04.1,但其实你交付的东西已经 不同了。

而且,如果用户用的情形足够复杂,多个用户可能使用你的组合中的不同组合。比如你同 时提供了OpenSSL的v1和v2两个库到你的20.04.1中,部分用户调用其中的v1,部分使用了 v2,这算几个Version?

在我们这个定义中,我们只承诺我们交付出去的那个安装前的状态,所以,这是一个 Version。只是这个Version的组合测试工作量很大而已。

    | 说到Linux发行版的版本,我再补充一个例子:
    | 很多人很容易陷入一个误区。比如要求Ubuntu支持某个硬件。
    | 那理论上你唯一的方法是和Ubuntu合作,在它的版本上支持你的硬件,
    | 这说的才是Ubuntu的Version支持这个硬件。
    | 但如果Ubuntu专门出另一个版本,专门支持你这个硬件,
    | 那这就是Ubuntu-Customized XX Version支持你这个硬件,
    | 这产生了另一个版本,不是原来那个意思了。如果你不是和Ubuntu合作,
    | 而是(因为这是开源软件)自己“定制”了一个版本
    | (只要你不要保留Ubuntu中部分商业软件。这完全合法,
    | 实际上Ubuntu本来就是基于完全开放的Debian定制的),
    | 不要使用它的商标,你完全可以做一个ILoveYouLinux V1.0来实现这个支持,
    | 但这个就不是Ubuntu的分支了。它很多地方很像Ubuntu,
    | 你甚至可以每次在Ubuntu升级的时候跟着升级,但你的节奏,
    | 相应客户问题的能力,定制能力就不同了。这不能认为这是同一个版本,
    | 因为它的发展不能按同一个版本那样发展的,你加了你的代码后,
    | 原来分支可以做的修改,新分支可不一定可以修改。
    | 这是我们关心Version和Revision的核心原因。

确定Version,是开发视图最难最脏的一个工作,不给出明确的Version的每个版本,开发 组和需求方都会觉得怎么都行,没有什么不可以的,一旦给定Version,而且他们明白 Version是啥意思,什么幺蛾子都出来了。所以这才是架构定义要面对的问题,这种问题上 ,不让开发部和需求方现场打架,都不是好的架构师。