前言

如何确保软件工程质量?
如何管理版本?
如何确保迭代的稳定性?

源代码版本管理

对可执行程序进行版本管理,仅仅是召回上个版本
如果想要深究问题的根源 ,那么我们就要对其源码进行管理。
我们很容易想到将源码和可执行程序的版本对应起来并且保持一致

但是事情没那么简单

请看下图:

项目于团队.png

不同模块可能由不同团队开发,甚至有些模块是外部第三方团队开发。这意味着,从细粒度的视角来看,一个软件工程的生命周期中,包含着很多个彼此完全独立的子软件工程。这些子软件工程它们有自己独立的迭代周期,我们软件只是它们的 “客户”。

发布单元的概念

拥有独立的迭代周期的软件实体,我们称之为 “发布单元”。你可能直觉认为它就是模块,但是实际上两者有很大的不同。

对于一个发布单元,我们直观的一个感受是它有自己独立的源代码仓库(repo)

发布单元可能是:

  • 可执行程序
  • 动态库
  • 虚拟机自定义动态库
  • 静态库
  • 源码

发布单元的输入,包含如下两个部分:

  • 若干自己独立演进的模块
  • 自己依赖的发布单元列表

以 github 为例:

它提供了以下源代码质量的管理手段:

  1. 团队成员开发活动的独立性
  2. 完善的代码质量检查机制
  3. 完善的回滚机制(revert
    代码质量检查过程,需求显然比较易变。所以在这里 github 做了开放设计。我们再一次感受到了开闭原则的威力。

发布单元的外部依赖管理,通常不同语言有自己的惯例

所有外部依赖管理无非要达到这样一个目标:指定我这个发布单元依赖的各个模块(嗯,这是通俗说法,其实是指依赖的发布单元)的建议版本是什么

前提假设

  1. 我们不能修改发布单元自身包含的各个模块的的代码
  2. 我们不能修改发布单元依赖的外部模块(同样地,其实指依赖的发布单元)的版本。

还有两个东西没有做到只读:

  1. 操作系统内核
  2. 编译器

软件发布的版本管理

我们大家可能都接触过各种软件发布的管理工具,比如 apt、rpm、brew 等等。

但不是每一次软件安装过程都是OK的

用户之间系统环境的差异太大了。让每个软件的发布者都能够想到多样化的环境并加以适配,这是非常高的要求。

解决方式:

容器化

容器的镜像(image),不只是包含了软件发布的可执行程序本身,也完整包含了运行它的所有环境,包括依赖的动态库和运行时,甚至包括了它依赖的 “操作系统”

只读设计的确定性

软件项目的管理期望达到确定性。但软件工程本身是快速变化的,是不确定的。这就是软件工程本身的矛盾。我们的目标是在大量的不确定性中找到确定性,这其实就是软件工程最核心的点。

在业务只读,接口稳定的预期下,模块与模块之间就可以自由组合,构建越来越复杂的系统。

我们开发的时候,有时候会倾向于变量只读,以提高内心对确定性的预期。我并没有去用严谨的方式实证过变量只读的收益究竟有多大,但它的确成为了很重要的一种编程流派,即函数式编程。

函数式编程从编程范式来说比较小众,但是其只读思想被广泛借鉴。

这里面最典型的就是大数据领域的 Spark。

对一个只读的 RDD 施加一个变换(transform),即得到另一个 RDD

版本的兼容问题

让一个模块依赖另一个模块(严谨来说是发布单元)的特定版本,这解决了版本的确定性问题。

为什么依赖模块的重构会给我们的系统带来未知风险?

但有时候我们无法放弃兼容。这发生在我们在做一个互联网服务时。一旦我们发布了一个 api,它就很难收回,因为使用这个 api 的客户端可能有很多。如果我们放弃这个 api 就意味着我们放弃了很多用户,这是不可接受的。

比较常见的做法是为所有 api 引入版本号,如 “/v2/foo/bar”。当我们对 api 发生不兼容的修改时,就升级版本号,比如 “/v3/foo/bar”。

好处:

如果我们对某个复杂模块进行了全局重构,并且兼容老版本的行为细节非常困难时,我们可以直接升级所有 api 的版本号

标签: 代码, 模块, 系统, 核心, 业务, api, 机制, 原则, 依赖, 版本, 变量, 函数, 设计, 编译, image, 单元, 软件

知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

发送一条友善的评论

  • 目录