Go的主要版本差劲-来自粉丝

2020-09-16 22:55:41

我通常是围棋工具链中僵硬的粉丝。事实上,我们在Qvault的前端和后端都使用Go。拥有跨整个语言的标准化格式化、审查和测试是非常棒的。我收到的第一个真正的批评是Go模块处理主要版本的方式。它过于固执己见,在相当多的场景中会减慢开发速度。

GO模块以及关联的命令go mod和go get可以被视为与npm和yarn等效的Go。Go工具链提供了一种管理依赖项和锁定代码集合所依赖的版本的方法。

最常见的操作之一是更新现有模块中的依赖项。例如:

#更新所有依赖项go get-u./...#添加丢失的依赖项并删除未使用的依赖项go mod tidy#保存项目';s";供应商";foldergo mod供应商中的所有依赖项代码。

GO模块使用GIT标签和语义版本控制来跟踪与相关模块兼容的依赖项版本。语义版本控制是格式化版本号的一种方式,它看起来如下所示:v{main}.{Minor}.{patch}。例如,v1.2.3。

进行不兼容的API更改时的主要版本,以向后兼容的方式添加功能时的次要版本,以及修复向后兼容的错误时的PATCH版本。

GO已决定要求v0和v1以外的所有版本都使用模块路径中的主版本。有两种方法可以实现这一点。

第一个也是推荐的方法在Go博客上的一个示例中列出:

要在Ggithub.com/googleapis/gax-go的v2目录上开始开发,我们将创建一个新的v2/gax-go目录,并将我们的软件包复制到其中。

换句话说,对于每个主要版本,我们都鼓励维护整个代码库的新副本。如果您希望预模块用户能够使用您的软件包,这也是唯一的方法。

第二种方法是只在go.mod中更改模块的名称。例如,模块github.com/lan-c-wagner/go-tinydate将变成模块github.com/lan-c-wagner/go-tinydate/v2。除了这不适用于旧版本的Go之外,我还发现它有问题,因为它破坏了(在我看来)模块名称最有用的东西之一-它们反映了文件路径。

允许包维护人员简单地通过更新git标签来指定主要版本,不需要更改模块名称。没有必要有两个真理来源。

我们可以通过向Go Get CLI添加警告或提示来强制安全更新。我们不需要增加不必要的耗时政策。

当发布新版本的依赖项时,我们有一个简单的命令来获取最新的内容:go get-u。问题是该命令无法自动更新到新的主要版本。它将只下载新的次要更改和补丁。甚至没有控制台消息通知您存在新的主要版本!

如果旧程序包和新程序包具有相同的导入路径,则新程序包必须向后兼容旧程序包。

导入兼容性规则

换句话说,我们应该只在进行破坏性更改时增加主要版本,并且如果进行破坏性更改,它们不能具有相同的导入路径。虽然这是有道理的,但我认为一个简单的控制台警告会是一个更好的解决方案,而不是将繁琐的更新策略强加给社区。

客户端的另一个问题是,我们不仅需要更新go.mod,实际上还需要通过代码库grep,并更改每个import语句以指向新的主要版本:

想要使用v2的用户必须将他们的软件包导入和模块要求更改为:github.com/googleapis/gax-go/v2。

我们不是使用几个简单的CLI命令来获取最新的依赖项,而是对代码本身进行更改。

对于不同的主要版本使用不同的路径在我们可能需要同一包的两个不同版本的情况下更有意义,您知道,钻石导入等等。这是例外,而不是规则,绕过大多数代码库中不存在的问题似乎很奇怪。

Go get-u应该有一个额外的命令行标志来更新主要版本,并且应该默认显示一个警告,提示您还没有更新的主要版本。

默认导入路径不应在主要版本之间更改。如果模块需要不同的版本,则可以通过不同的路径来标记这些额外的版本。

通常情况下,我希望构建一个包,该包具有特定于领域的逻辑,并且只在我工作的小公司的服务中使用。例如,我们有一个repo,它保存整个系统中使用的常见实体的struct{}定义。

有时,我们需要对这些结构定义进行向后不兼容的更改。如果它是一个开源库,我们就不会经常更改,但是因为它是内部的,而且我们知道所有依赖项,所以我们定期更改导出字段的名称。我们更名并不是因为我们一开始就选择了不好的名字,我们通常是因为创业公司的业务需求变化很快而改名。

这意味着重大版本更改是相当有规律的。有人说我们应该继续使用V0,这是一个合理的解决方案。问题是这些都是生产包,许多服务都在使用它们。我们想去探险。

Go使得更新主要版本非常麻烦,以至于在大多数情况下,当我们应该增加主要版本时,我们选择只增加次要版本。我们希望遵循正确的版本控制方案,只是不想在我们的开发过程中添加不必要的步骤。

我理解为什么会做出这些决定--我甚至认为,在很多情况下,这些决定都是伟大的决定。对于任何开源或面向公众的模块来说,这都很有意义。Go工具链执行严格的规则,鼓励良好的API设计。

在努力使公共API变得伟大的过程中,他们使良好的“本地化”包设计变得不必要地困难。

Github上有一个悬而未决的问题,它将使新的主要版本更容易从CLI中发现。如果你感兴趣的话请看一下。