rss twitter gitlab github linkedin linkedin instagram
Learning Go: Versioning Tools
Oct 15, 2021

Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I’ll earn a commission. Please notice your final price is not affected at all by using those links.

Versioning dependencies is key because it allows us to know exactly what the final artifact will look like, it let’s us create deterministic builds that work the same as long as the dependencies don’t change. Regarding tool dependencies, specifically those tools written in Go, the versioning process is similar to what we typically do when using Go Modules.

The code used for this post is available on Github.

How to version tools in Go?

Although not necessary I recommend you to install direnv, this tool allows you to sandbox environment variables by directory, and because it allows us to do that we can define our own PATH and GOBIN for a concrete directory, what this means in practice is that by doing so we can we install binaries relative to the project we are currently working on without making them globally available in our system.

The Tools as Dependencies Paradigm is what we need, basically define a file that blank imports all the tools we need and make it only buildable for a concrete tag.

That paradigm works for sure but I like to take this a bit further and instead of making the tools.go file part of your principal Go module I prefer defining another separated Go module inside an internal package, for example internal/tools; this is with the idea of keeping those dependencies clearly isolated from the principal Go module.

For example, assuming we depend on sqlc, we will create a new module in internal/tools, and then we will create a file called: internal/tools/tools.go with the following content:

package tools

import (
	_ ""

Next, we will go get it:

go get

This is so the go.sum is populated correctly, by doing all of this if users of our Go module decide to import the principal package they will not require the packages needed for the tools we use but rather only those required by the principal package; and because we have our tools package in internal there’s no way external users are able to import that package either.

Please refer to the source code for more details about the final internal/tools package.


Versioning is fundamental because we know for sure the exact dependencies we need for building our program, the same applies for external programs we need for our program to build, example of those are linters or code generators; having everything tagged as part of our repository allows building artifacts the same way no matter what because we know for sure the versions we depend on.

If you’re looking to sink your teeth into more Go-related topics I recommend the following:

Back to posts