Go Commands Cheat Sheet

build, run, test, get, mod, vet, fmt, and More

Essential Go commands for building, running, testing, and maintaining code.

The go command is your workshop

You write main.go. You type go run main.go. It works. You add an import for a third-party package. You run it again. The tool downloads the dependency, compiles it, and executes your code. You feel magic. Then you try to share the code with a friend. They clone the repo and run it. They get a dependency error because the module files are missing. The magic breaks.

The go command isn't just a wrapper around a compiler. It is the single entry point for the entire Go toolchain. It manages dependencies, formats code, checks for bugs, runs tests, and builds binaries. In other ecosystems, you might juggle a package manager, a linter, a formatter, and a build system. In Go, one command does it all. Understanding the distinction between go run, go build, go test, and go mod prevents workflow friction and keeps your projects reproducible.

One tool, many jobs

Think of the go tool as a unified workshop. You don't need separate stations for fetching materials, inspecting blueprints, or assembling parts. When you ask the workshop to build something, it checks the inventory, fetches missing components, verifies the plans for safety hazards, formats the workspace, and assembles the final product.

The commands are verbs that describe what you want the workshop to do. go run executes code immediately. go build produces a distributable binary. go test verifies correctness. go mod manages the dependency graph. go vet inspects source for common mistakes. go fmt enforces standard formatting. The toolchain is opinionated by design. This reduces configuration overhead and ensures that code written by different developers looks and behaves consistently.

Minimal example: run versus build

The most common confusion is between running code and building a binary. go run is for development iteration. go build is for distribution.

// main.go
package main

import "fmt"

// main is the entry point for executable programs
func main() {
    // Print output to stdout to confirm execution
    fmt.Println("Hello from Go")
}

Run the code directly:

go run main.go

Build a standalone binary:

go build -o myapp main.go
./myapp

go run compiles the code to a temporary binary and executes it. It discards the binary when the process exits. This is fast for quick checks. go build produces a binary file named myapp that you can copy to a server, share with a user, or run later. The -o flag sets the output name. Without -o, the binary takes the name of the directory or the first file argument.

Use go run when you are iterating. Use go build when you need an artifact.

What happens under the hood

When you invoke any go command, the toolchain follows a deterministic sequence. It resolves dependencies, checks the build cache, compiles packages, and links the result.

Dependency resolution relies on the go.mod file. If your code imports a package, Go checks the module file. If the dependency is missing or the version is unsatisfied, the tool fetches the module from the network. By default, Go uses the module proxy at proxy.golang.org, which caches modules globally. This speeds up downloads and protects against supply chain attacks by verifying checksums against the go.sum file.

The build cache stores compiled artifacts. If you run go build twice without changing the source, the second run is nearly instant. The tool compares the hash of the source files and the build configuration. If nothing changed, it reuses the cached object. This caching applies to go run, go test, and go build. It makes the development loop snappy even for large projects.

Convention aside: The build cache is global across projects. Running go clean -cache clears it if you suspect stale artifacts, but this is rare. Trust the cache.

Realistic workflow: a module with tests

Real projects have structure. You initialize a module, write code, format it, vet it, test it, and build it. Here is a realistic workflow for a small HTTP server.

// server.go
package main

import (
    "fmt"
    "net/http"
)

// handleRoot responds to HTTP requests with a simple message
func handleRoot(w http.ResponseWriter, r *http.Request) {
    // Write response to the HTTP writer
    fmt.Fprintf(w, "Server is running")
}

func main() {
    // Register the handler for the root path
    http.HandleFunc("/", handleRoot)
    // Start the server on port 8080
    http.ListenAndServe(":8080", nil)
}
// server_test.go
package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

// TestHandleRoot verifies the handler returns the expected status
func TestHandleRoot(t *testing.T) {
    // Create a test request targeting the root path
    req := httptest.NewRequest("GET", "/", nil)
    // Create a response recorder to capture the output
    rr := httptest.NewRecorder()
    // Call the handler with the recorder and request
    handleRoot(rr, req)
    // Assert the status code is 200
    if rr.Code != http.StatusOK {
        t.Errorf("expected status 200, got %d", rr.Code)
    }
}

Initialize the module:

go mod init example.com/myapp

Write the code. Format it:

go fmt ./...

Check for bugs:

go vet ./...

Run tests:

go test ./...

Sync dependencies:

go mod tidy

Build the binary:

go build -o server .

Convention aside: Run go mod tidy before every commit. It adds missing dependencies and removes unused ones. This keeps go.mod and go.sum accurate. If you skip this, your repository might build locally but fail for others who have a clean cache.

Convention aside: go fmt is mandatory. The community uses it universally. Most editors run it on save. Don't argue about indentation or brace placement in code reviews. Let the tool decide.

Pitfalls and compiler errors

The go tool catches mistakes early, but some errors require understanding the workflow.

Running go run on a package without a main function fails. The compiler rejects this with go run: cannot run non-main package. Only packages named main can be executed. Library packages must be built or tested, not run.

Forgetting to initialize a module in a directory without a go.mod file causes resolution errors. You get go: cannot find main module or go: cannot determine module path for source directory. Always run go mod init at the project root.

go vet finds bugs that the compiler misses. If you write fmt.Printf("Count: %d", "hello"), the compiler accepts the code because Printf takes variadic arguments. go vet catches the type mismatch and reports fmt.Printf format %d has arg x of wrong type string. Run go vet regularly to catch these issues.

Building a directory that contains only test files produces no output. The tool reports no non-test Go files in /path. go build skips test files by default. Use go test to run tests, or ensure the directory has non-test source files.

Importing a package and not using it triggers a compile error. The compiler complains with imported and not used. Go requires all imports to be used. This rule prevents dead code and keeps dependencies minimal. If you need an import for its side effects, use the blank identifier: import _ "package".

Convention aside: The blank identifier _ discards a value intentionally. result, _ := ... says "I considered the second return value and chose to drop it". Use it sparingly with errors. Dropping errors silently is a common source of bugs. Prefer if err != nil { return err } to make the unhappy path visible.

Decision: which command to use

The go tool offers many commands. Pick the right one for the job.

Use go run when you are iterating on code and want immediate feedback without creating a binary.

Use go build when you need a standalone binary to deploy, share, or archive.

Use go test when you want to execute test functions and verify correctness.

Use go mod tidy when you have added or removed imports and need to sync the go.mod and go.sum files.

Use go vet when you want to catch common mistakes like format string errors or unreachable code before running the program.

Use go fmt when you want to enforce standard formatting across the codebase.

Use go install when you want to build a tool and place the binary in your GOBIN directory for global access.

Use go doc when you need to read documentation for a package or function from the command line.

Use go env when you need to inspect environment variables like GOPATH or GOROOT.

Use go list when you want to query module metadata or list packages in a dependency graph.

Where to go next

Mastering the toolchain is the foundation. Once you are comfortable with building and testing, explore how to structure tests for complex services and how to manage external dependencies in integration scenarios.

Trust the toolchain. Run go mod tidy before every commit. Let go fmt handle style. Use go vet to find bugs early. The go command is your workshop. Keep it clean.