New in Go 1.21: Toolchains
Tags: go, toolchain
Published: 2023-10-21 13:10:04

---
title: New in Go 1.21: Toolchains
published: true
description: 
tags: go, toolchain, golang
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-21 13:07 +0000
---

The version 1.21 of the language implemented an essential new feature. According to the documentation:

> Starting in Go 1.21, the Go distribution consists of a go command and a bundled Go toolchain, which is the standard library as well as the compiler, assembler, and other tools. The go command can use its bundled Go toolchain as well as other versions that it finds in the local PATH or downloads as needed.

I did a proof of concept to validate how this new functionality works, which I will describe in this post.

On my machine, I had the following version of `go`:

```bash
go version
go version go1.21.0 darwin/arm64
```

I created three libs, each with a different version of go as a requirement. In the commands below, you can see the content of each `go.mod` of the libs:

```bash
❯ cd l1 ; cat go.mod; cd ..
module github.com/eminetto/l1

go 1.19.0

❯ cd l2; cat go.mod; cd ..
module github.com/eminetto/l2

go 1.20.0

❯ cd l3; cat go.mod; cd ..
module github.com/eminetto/l3

go 1.21.1
```

Starting with version 1.21, the `go 1.20.0` line of the `go.mod` file, in this example of the `l2` lib, indicates the minimum language version required we need to use to compile it.

Next, I created a project to import the three libraries. The following diagram shows the dependencies:

[![toolchain](https://eltonminetto.dev/images/posts/toolchain.png)](https://eltonminetto.dev/images/posts/toolchain.png)

The `main.go` of the `proj` contains:


```go
package main

import (
	"fmt"

	"github.com/eminetto/l1"
	"github.com/eminetto/l2"
	"github.com/eminetto/l3"
)

func main() {
	fmt.Println(l1.Sum(1, 2))
	fmt.Println(l2.Sum(1, 2))
	fmt.Println(l3.Sum(1, 2))
}
```

The project's `go.mod` contains:

```go
module github.com/eminetto/proj

go 1.21.0

require (
	github.com/eminetto/l1 v0.0.0-20231012141607-826d3914801f
	github.com/eminetto/l2 v0.0.0-20231012141624-28120aab8596
	github.com/eminetto/l3 v0.0.0-20231012141624-2810bakab896
)
```

I ran the project, and it worked without errors:

```bash
❯ go run main.go
3
3
3
```

Next, I simulated the process of updating one of the libs.
To do this, I updated the `l3` lib to a newer version of Go:


```bash
❯ cd l3; cat go.mod
module github.com/eminetto/l3

go 1.21.3
```

I pushed it to the repository and created a new version (`v0.0.1`).  

According to the [documentation](https://tip.golang.org/doc/toolchain):

> The Go toolchain refuses to load a module or workspace that declares a minimum required Go version greater than the toolchain’s own version.

To validate this, in the project, I updated the version of `l3`:

```bash
❯ go get github.com/eminetto/l3@v0.0.1
go: downloading github.com/eminetto/l3 v0.0.1
go: github.com/eminetto/l3@v0.0.1 requires go >= 1.21.3; switching to go1.21.3
go: upgraded go 1.21.0 => 1.21.3
go: upgraded github.com/eminetto/l3 v0.0.0-20231012141629-a747d5b44b93 => v0.0.1
```

What's new is the following excerpt:

```bash
go: github.com/eminetto/l3@v0.0.1 requires go >= 1.21.3; switching to go1.21.3
go: upgraded go 1.21.0 => 1.21.3

```

What happened was that the `go` command updated the version of Go installed on my machine to `1.21.3`:

```go
❯ go version
go version go1.21.3 darwin/arm64
```

And the project's `go.mod` has also been updated to version `1.21.3.`

```bash
❯ cat go.mod 
module github.com/eminetto/proj

go 1.21.3

require (
        github.com/eminetto/l1 v0.0.0-20231012141607-826d3914801f
        github.com/eminetto/l2 v0.0.0-20231012141624-28120aab8596
        github.com/eminetto/l3 v0.0.1
)
```

And the project continues to run without errors. My machine now has the minimum version necessary to import ALL libs and run without errors. In this case, it was updated to `1.21.3` because it was necessary to run `l3` since the others are below the current version I had installed on my machine (`1.21.0`). 

Another fact I noticed is that there is now one version of Go for the project and another for the rest of the operating system:

```bash
❯ cd proj

Developer/post-toolchain/proj
❯ go version
go version go1.21.3 darwin/arm64

Developer/post-toolchain/proj
❯ cd ..

~/Developer/post-toolchain
❯ go version
go version go1.21.0 darwin/arm64
```

The `go` command identifies that the `proj` directory requires a specific language version. And all this management is done automatically.

Let's now think about a slightly more complex example. Let's suppose that one of the libs, for example, `l2`, was updated to:


```go
module github.com/eminetto/l2

go 1.24rc1
```


The `go` command gets the list of available `toolchains` and finds that the most recent releases are `Go 1.28.3`, `Go 1.27.9`, and `Go 1.29rc2`. The `go` command will choose `Go 1.27.9` in this situation.

If `l2` required `Go 1.28` or later, the `go` command would choose `Go 1.28.3` because `Go 1.27.9` is too old. If `l2` required `Go 1.29` or later, `Go 1.29rc2` would be chosen because the other options are too old.

When executing the command `go get github.com/eminetto/l2@v0.0.1` (the newest version of the package), it will update the project's `go.mod` file, and a new line will be added with the chosen `toolchain `:


```go
❯ cat go.mod 
module github.com/eminetto/proj

go 1.27.9

toolchain go1.27.9

require (
        github.com/eminetto/l1 v0.0.0-20231012141607-826d3914801f
        github.com/eminetto/l2 v0.0.1
        github.com/eminetto/l3 v0.0.1
)

```


You can find more details about the motivations behind this feature, the version naming, and advanced behavior settings in the language's official [documentation](https://tip.golang.org/doc/toolchain).

This feature is another example of the language team's care to maintain compatibility between past and future versions. With this functionality, it will be possible to guarantee the longevity of applications written in Go, which is very important for the future of the language in increasingly complex environments.

Thanks to friends [Matheus Mina](https://www.mfbmina.dev/), [Tiago Temporin](https://aprendagolang.com.br/), and [Eduardo Hitek](http://eduardohitek.dev) for reviewing the text and suggesting improvements.

Originally published at [https://eltonminetto.dev](https://eltonminetto.dev/en/post/2023-10-18-go121-toolchain/) on October 18, 2023