这些年来,由于golang缺少依赖管理,社区涌现了五花八门的依赖管理方案,比如glide、govendor、godep等。而golang 自1.11起,内置了modules方案,总算是来了个一锤定音。

go modules方案的设计是非常好的,网上介绍go modules机制的文章很多,我这里也不再赘述。工作中写的一些golang项目,我都已经用上了该方案来做依赖管理。之所以这么快的拥抱新方案,是认为有google的背景,即使是新鲜出炉的功能,稳定性应该是有保证的,即使有坑,趟趟也就过去了。

但是在这个过程中,发现的一些问题让我对google软件工程质量的信心大打折扣。

举个例子,如果你的开发环境是centos6.5, git版本是比较旧的v1.7.1,这个时候使用go modules是会有问题的(不要问我为什么要在centos6.5上面编译golang程序,因为我个人习惯将开发环境保持与线上环境一致)。在go get或者go build会报一些五花八门的错误,比如:

1
build xxxx: cannot find module for path yyy

或者:

1
unknown revision xxxx

具体是哪一种错误,取决于不同的执行时机,比如你是对一个之前没有用过go modules的项目从头执行了

1
go mod init xxxx

做初始化之后再执行go get 或者go build,还是对一个已经使用go modules的项目直接执行 go get 或者go build。

如果遇到这类问题,解决方法非常简单,升级一下git 版本到比较新的版本即可。这里说起来很轻描淡写,但是实际排查过程却是很让人恼火,因为整个go cmd的代码实在是太乱了,代码质量不敢恭维,阅读体验很差。那么问题在什么地方呢,在于在确定版本依赖关系的过程中,go cmd代码里调用了一些git指令,而这些指令在较老版本的git中还没有支持。这是一个多么正常的使用场景,可是golang从1.11到现在已经发布的1.11.2,均没有解决这个报错信息与真实原因风马牛不相及的问题。

一个系统的构建对其他某些组件的版本有依赖,这在软件工程中是一个再常见不过的事情。最简单的处理方式是,要么在文档中写明依赖的版本信息,要么在构建过程中,如果发现某些组件版本太低,就抛出相关信息,然后退出。无论怎么做,都是为了达到一个目的:告诉使用者发生了什么事情,该怎么做。而go 1.11到go 1.11.2里是怎么做的呢,发现git 的一些指令执行失败了,却并不做好异常处理,一直到另一块代码中不得不失败退出,才丢出一个没有任何参考意义的错误信息。

也许你会觉得我有点小题大做,毕竟是软件就会有bug。但真正让我恼火的是,一些golang的开发者居然保有这样的想法:

反正开发者们一般是在ubuntu上面开发,上面的git 版本一般足够新了,编译完之后把二进制再部署到centos等老的系统上去就行了。

这话当然不是我杜撰的,具体见: https://github.com/golang/go/issues/26746

连我这种软件行业的凡夫俗子,都知道遇到异常的合理处理方式是什么(例如我在2013年写的一篇文章:再不判断异常分支就剁手),google 里绝对意义上的高手竟然对此毫不在意,实在让人大跌眼镜。