Golang1.21兼容性问题-向后兼容
目录
向后兼容⌗
兼容性指的是源码的兼容,当你更新Go版本之后,需要重新编译,我们可以添加新的API,但是不应该破坏现有代码的使用。Go 1 and the Future of Go Programs对Go1的兼容性进行了描述,以及不保证兼容性的多个例外场景。
简单来说,就是老版本的Go代码可以使用新版本的Go工具链正确地编译和运行。
保持兼容性的主要手段⌗
Golang保持兼容性的主要手段有两个方式:API检查和测试。
API检查⌗
Go使用一个工具维护每个package export出来的API
api/ (master) $ ls
except.txt go1.13.txt go1.17.txt go1.20.txt go1.4.txt go1.8.txt README
go1.10.txt go1.14.txt go1.18.txt go1.21.txt go1.5.txt go1.9.txt
go1.11.txt go1.15.txt go1.19.txt go1.2.txt go1.6.txt go1.txt
go1.12.txt go1.16.txt go1.1.txt go1.3.txt go1.7.txt next
$ cat go/api/go1.21.txt
pkg bytes, func ContainsFunc([]uint8, func(int32) bool) bool #54386
pkg bytes, method (*Buffer) AvailableBuffer() []uint8 #53685
pkg bytes, method (*Buffer) Available() int #53685
pkg cmp, func Compare[$0 Ordered]($0, $0) int #59488
pkg cmp, func Less[$0 Ordered]($0, $0) bool #59488
pkg cmp, type Ordered interface {} #59488
pkg context, func AfterFunc(Context, func()) func() bool #57928
pkg context, func WithDeadlineCause(Context, time.Time, error) (Context, CancelFunc) #56661
pkg context, func WithoutCancel(Context) Context #40221
pkg context, func WithTimeoutCause(Context, time.Duration, error) (Context, CancelFunc) #56661
golang会有一个测试专门来检测实际的package APIs是否匹配这些文件。如果新增了新的API到一个package中,除非将相关信息添加到这个api文件,否则这个测试就会break。
Dave Cheney的how-go-uses-go-to-build-itself,介绍了
go tool api
的使用。
实际上在
go1.20
和go1.21
中都会显示go: no such tool "api"
,不是很清楚是要被替换掉,还是说未来会修复,起码我没在release note和issue中看到相关信息。
但这种方式只能检测某种类型的问题,也就是API变更和移除。还是有很多其他方式会改变go的兼容性。
测试⌗
和所有软件一样,版本升级都需要靠测试来保证兼容性和质量。Golang是怎么做的呢?
- 对下个版本的代码运行现有的测试用例
- 定期对Google内部的Go代码进行新版本的go测试。
- 当测试通过时候,提交安装为google的生产工具链
- 如果一个变更破坏了google的内部测试,可以认为它也会破坏外部测试,需要寻找减少影响的方法。
- 回滚变更
- 找到一种方式重写,使其不破坏任何程序
- 如果这个变更很重要,即便破坏了程序也需要发布,那么会尽力减少影响,并在发布声明中记录可能的问题。
PS:其实这种策略在基础组件开发中都是可以借鉴的:组件本身测试、小范围运行测试版本、最后release正式版本。只是大多数公司,没有google这种内测规模也没有稳定的版本发布周期。
Go1.21的改进⌗
为了提高向后兼容性,Go1.21正式规定了Go使用GODEBUG
环境变量来控制那些根据Go1的兼容性承若是非breaking,但可能导致已有程序行为受影响的默认行为变更(比如,bug的修复就不会考虑对现有程序行为的影响)。
为了兼容性添加的GODEBUG
最少会被维护2年(4个Go版本),某些可能会延长,甚至无限期。
在可能的情况下,每个GODEBUG
的设置,都有一个关联的runtime/metrics 名为/godebug/non-default-behavior/<name>:events
的counter,来统计非默认行为的使用情况。
Go1.21.1可以在main
包的源文件的package
语句之前加入//go:debug
指令来覆盖DODEBUG
默认值。
GODEBUG详情见Go, Backwards Compatibility, and GODEBUG
在Go1.21之后,当Go产生行为变更的时候,它会根据在workspace
的go.work
或main
模块的go.mod
文件的go line
来判断新旧的行为的选择。