The Go build system is known for its efficiency and continuous improvement. Go 1.24 introduces a significant enhancement with the “tool” directive, addressing how Go-based dev tools are managed.
Previously, managing tools like linters (golangci-lint), code generators (mockery, oapi-codegen), or other build utilities often involved manual installation steps (using “go install” or some package manager) outside the project's dependency management, leading to potential version inconsistencies across development environments and CI systems.
I’ve seen it many times when two developers generate different code by using the same command, only because the versions of these tools are different. There are workarounds to “fix“ the versions in fake “tools.go” file, but it’s not enough and felt dirty.
But not anymore! Go 1.24 addresses these challenges directly with the “tool” directive.
Our sponsor
With Multiplayer you can build better distributed systems: automatically document your system architecture, effortlessly debug with deep session replays, and collaboratively design your system. Why are you still wasting time drawing manual diagrams?
Adding a new tool to go.mod
Let’s take oapi-codegen as an example, to add it to our Go project we can simply use “go get -tool“:
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
Once installed, the tool is available for use via “go tool“. Note, that the first run could be a bit slow because Go will compile the tool if it’s not yet compiled, repeated executions are much faster. Which should not be a big problem for dev tools, but worth mentioning.
go tool oapi-codegen
Note, that oapi-codegen could be installed globally with a different version. Calling “go tool“ without a tool name will list all the tools. Note there are some built-in to Go, so you will always have a few.
The tool will be added to a project and listed in our go.mod file, so all users of our software will understand what tools and what versions are required.
tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
require (
github.com/oapi-codegen/runtime v1.1.1
)
By the way, while not a huge deal, I find “go tool toolname“ a bit verbose as you always have to type it. But it doesn’t matter if you use “go:generate“ for example.
Pairing with go:generate
What's also great is that we can leverage that in go:generate annotations, which completely eliminates a manual tool installations. Traditionally, we would rely on the tool being installed globally on the host:
// go:generate oapi-codegen openapi.yaml
But now we could use the “tool“:
// go:generate go tool oapi-codegen openapi.yaml
Which will use the correct version pinned in your go.mod file.
# A single command to run all generators!
go generate
Separate go.mod file
It is also possible to use a separate go.mod file for dev tools in case you don’t want to mix it with your regular dependencies or you have some issues with the versions clashing.
go tool -modfile=tools.mod oapi-codegen
Non-Go tools
Only Go tools are supported. However, some projects will likely depend on some non-Go tools, which have to be managed separately.
Conclusion
With the introduction of the “tool” directive in Go 1.24, Go modules gain a much-improved way to handle tool dependencies, similar to “npm run“ in Node.js world.