#An Overview of Essential Go Tools
One of the things that makes Go such a fantastic language to work with is the comprehensive set of tools that come built into the standard distribution. The Go team has embraced a philosophy of “batteries included” and strong conventions over configuration, which means you spend less time setting up tools and more time writing code.
In this article, we’re going to walk through the most essential tools in the Go toolchain. Whether you’re just starting out or you’ve been writing Go for years, you’ll find these tools invaluable for writing, testing, and maintaining Go applications.
go build
The go build command is your primary tool for compiling Go programs into executable binaries. It’s straightforward and handles dependency resolution automatically.
The most basic usage is simply:
go build
This will compile the package in the current directory and create an executable. If you want to give your binary a specific name:
go build -o myapp
Cross-Compilation
One of Go’s superpowers is how easy cross-compilation is. Want to build for Linux when you’re on macOS? No problem. You just set the GOOS and GOARCH environment variables:
GOOS=linux GOARCH=amd64 go build -o myapp-linux
GOOS=windows GOARCH=amd64 go build -o myapp-windows.exe
Embedding Version Information
A common pattern is to embed build information like version numbers into your binary. You can do this with the -ldflags flag:
go build -ldflags="-X main.Version=1.0.0 -X main.BuildTime=$(date -u '+%Y-%m-%d_%H:%M:%S')"
Then in your code, you’d have variables like:
var (
Version = "dev"
BuildTime = "unknown"
)
go run
The go run command compiles and executes your Go code in a single step, without creating a persistent binary:
go run main.go
Or if you’re working with multiple files in the same package:
go run .
This is perfect for development and testing during the writing process. However, it’s not suitable for production use since it recreates the binary every time you run it. For distribution and deployment, always use go build to create a proper binary.
go test
Testing is a first-class citizen in Go, and the go test command makes running tests incredibly simple. By convention, test files end with _test.go:
go test
For more verbose output showing each test that runs:
go test -v
You can run specific tests using the -run flag with a regular expression:
go test -run TestUserCreate
To see test coverage:
go test -cover
And to detect race conditions in concurrent code:
go test -race
Testing is crucial to writing reliable Go applications. If you’d like to dive deeper, we have a complete Introduction to Testing in Go guide that covers testing patterns in detail.
go fmt and gofmt
Go has an opinionated code formatter, and the Go community has embraced this philosophy wholeheartedly. This eliminates bike-shedding about code style—everyone’s code looks the same.
Format your current package:
go fmt
Or format all files in a directory recursively:
go fmt ./...
The gofmt command is the underlying tool:
gofmt -w main.go
The -w flag writes the changes back to the file. Most developers configure their editors to run go fmt automatically on save, ensuring consistent formatting without thinking about it.
go vet
The go vet command runs static analysis on your code to catch suspicious constructs that are likely bugs. It checks for things like:
- Printf format string mismatches
- Unreachable code
- Incorrect use of sync types
- Copying of lock values
- Struct field alignment issues
Run it with:
go vet
Here’s an example of code that go vet would catch:
func main() {
fmt.Printf("Hello %d\n", "world") // Wrong type for %d
}
Running go vet would immediately flag this as an error, saving you from discovering it later at runtime.
go mod
Go modules have become the standard way to manage dependencies. The key commands you’ll use are:
Initialize a new module:
go mod init github.com/yourusername/yourproject
Add or update dependencies by importing them in your code, then run:
go mod tidy
This fetches any missing dependencies and removes unused ones. It updates your go.mod and go.sum files.
If you want to vendor your dependencies (include them in your repository):
go mod vendor
This creates a vendor/ directory with all dependencies, useful for certain deployment scenarios.
go doc and godoc
The go doc command lets you read documentation from the command line without leaving your terminal:
go doc fmt.Println
go doc -h
When you write doc comments above your functions and types, following Go’s conventions, they become queryable:
// Greet returns a greeting message for the given name.
func Greet(name string) string {
return "Hello, " + name
}
Now other Gophers using your package can run go doc to read your documentation. There’s a convention: the first sentence should be a complete sentence starting with the name of the thing being documented.
go generate
The go generate command allows you to invoke code generation tools. You annotate your code with //go:generate directives:
//go:generate stringer -type=Color
type Color int
const (
Red Color = iota
Green
Blue
)
Then run:
go generate ./...
This is powerful for reducing boilerplate. Tools like stringer (which generates String() methods), mockgen (for mocking), and custom generators can all be integrated this way.
golangci-lint
While the Go toolchain covers the basics, many Gophers use golangci-lint as their linter of choice. It’s a meta-linter that runs many individual linters in parallel and presents the results in a unified way.
Install it with:
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
Then run:
golangci-lint run
You can configure it with a .golangci.yml file in your project root to customize which linters run and their settings. This is especially useful in team environments where you want consistent linting standards.
delve (dlv)
When you need to debug your Go code, the delve debugger is the standard tool. Install it with:
go install github.com/go-delve/delve/cmd/dlv@latest
Start debugging your program with:
dlv debug
This launches an interactive debugging session where you can set breakpoints, step through code, and inspect variables. While most debugging can be solved with logging and testing, dlv is invaluable when you need to dig deeper into runtime behavior.
Conclusion
The Go toolchain is genuinely one of the language’s greatest strengths. Everything you need to write, test, format, analyze, and document your code comes built-in or easily available. There’s no debate about which formatter to use, no endless configuration files to maintain, just straightforward tools that work reliably.
By mastering these tools—especially go build, go test, go fmt, and go vet—you’ll be well-equipped to write production-quality Go applications. As you grow as a Gopher, you’ll find tools like golangci-lint and delve extend your capabilities further.
Further Reading
- An Introduction to Testing in Go
- Makefiles for Go Developers
- Taskfiles for Go Developers
- GitHub Actions for Go Projects
Continue Learning
Build a Go Serverless App in 5 Minutes With Sst
In this video, we are going to look at what it takes to build a serverless application in Go in 5 minutes using SST.
Go Garbage Collection Overview
In this tutorial, we are going to be covering Garbage collection in Go and how it works
The Go init Function
In this tutorial we'll be looking at the Go init function, how to use it and some of the things to consider when using it within your Go programs.
Panic Recovery in Go - Tutorial
In this article, we are going to be taking a look at how we can actively recover from panics within our Go applications.