Go 1.15 发布于 2020 年 8 月 11 日。它不是那种让人立刻重写服务的版本,更像一次安静的维护:链接、运行时、二进制体积和标准库细节都变得更顺手一点。1注 1Go 1.15 的主要版本说明。

工具链改进.

Go 1.15 最值得关注的变化之一是链接器重写。官方说明提到,更快的链接速度、更低的内存占用,以及用 Go 实现的新基础,为后续工具链工作留下了更清晰的空间。2注 2Go 团队对发布重点的解释。

我会把这类升级记录成很短的仓库笔记:

go-upgrade-check.sh
go version
go test ./...
go test -race ./...
go build -trimpath ./cmd/service

好的升级记录不需要复杂,关键是能复查:从哪个版本到哪个版本,构建时间有没有变化,二进制体积有没有变化,是否出现了代码层面的兼容调整。

运行时成本.

defer 在 Go 1.15 里有了实用的性能改进。常见场景下,运行时可以使用 open-coded defers,让清理型代码保留清晰写法的同时减少成本。

func processFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer f.Close()
 
    return process(f)
}

这件事的意义不是鼓励到处写 defer,而是让普通代码不用为了微小成本过早变形。真正的规则仍然是:

  1. 01defer 出现在能表达资源归属的位置。
  2. 02先用 profile 找到热点路径。
  3. 03不要在没有证据时牺牲可读性。

标准库细节.

time/tzdata 让程序可以把时区数据库嵌入二进制文件。对小容器、受限系统或宿主环境不可靠的部署来说,这个选择能减少外部依赖。3注 3适合部署环境不稳定或缺少 zoneinfo 的场景。

package main
 
import (
    "fmt"
    "time"
    _ "time/tzdata"
)
 
func main() {
    loc, err := time.LoadLocation("Asia/Shanghai")
    if err != nil {
        panic(err)
    }
    fmt.Println(time.Now().In(loc).Format(time.RFC3339))
}

测试里也有一个小但有用的 API:testing.TB.TempDir。它给测试提供自动清理的临时目录,减少共享路径和手写 cleanup 带来的噪音。

func TestSomething(t *testing.T) {
    dir := t.TempDir()
    path := filepath.Join(dir, "sample.txt")
 
    if err := os.WriteFile(path, []byte("hello"), 0o644); err != nil {
        t.Fatal(err)
    }
}

升级判断.

Go 1.15 是一次保守发布,但保守不等于无感。评估升级时,我会看三类信号:

范围观察内容常见信号
工具链build、link、vet、raceCI 时间和峰值内存
运行时defer、分配密集路径profile 变化
环境时区文件、容器、测试目录只在部署环境出现的问题

这类版本最好的结果是无事发生:服务照常运行,CI 更快一点,制品更小一点,部署假设更少一点。