#An Overview of Essential Go Tools

Elliot Forbes Elliot Forbes · May 11, 2022 · 6 min read

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