Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Go — User Guide

Generated May 2026 from golang/go at 1644917

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

Go is an open-source, statically typed, compiled programming language designed at Google for building reliable, efficient software at scale. It compiles directly to machine code, features built-in concurrency primitives (goroutines and channels), and produces single, self-contained binaries that run without external dependencies. Go powers much of the modern cloud infrastructure — Docker, Kubernetes, Terraform, and thousands of production services worldwide.

Key features:

  • Fast compilation — large projects compile in seconds, not minutes
  • Garbage collected — automatic memory management without manual allocation
  • Concurrency via goroutines and channels — lightweight threads (goroutines cost ~2KB each) with type-safe communication
  • Cross-compilation — build for any supported OS/architecture from any machine with GOOS and GOARCH
  • Single binary output — no runtime dependencies, no DLL hell, deploy by copying one file
  • Comprehensive standard library — HTTP servers, JSON, crypto, compression, databases, and more built in
  • Module system — built-in dependency management with go.mod and reproducible builds via go.sum
  • Built-in testing — go test includes unit tests, benchmarks, fuzz testing, coverage, and race detection

Prerequisites: A 64-bit operating system — Linux, macOS, Windows, or FreeBSD on supported architectures.

Install Go:

Download the latest release from go.dev/dl and follow the instructions for your platform, or use your system package manager.

Hello World in 60 seconds:

mkdir hello && cd hello
go mod init example/hello

Create main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Run it:

go run .
Hello, World!

Tip

go run . compiles and executes in one step — perfect for quick iteration. For production, use go build to create a reusable binary.

What's next:


2. Installation

System Requirements

Platform Architecture Minimum OS Version
Linux amd64, arm64, 386, armv6l, loong64, mips*, ppc64*, riscv64, s390x Kernel 2.6.32+ (glibc 2.17+)
macOS amd64, arm64 (Apple Silicon) macOS 11 Big Sur+
Windows amd64, arm64, 386 Windows 10 / Server 2016+
FreeBSD amd64, arm64, 386, armv6l, riscv64 FreeBSD 13+

Install Methods

Download the appropriate archive from go.dev/dl.

Linux (amd64):

wget https://go.dev/dl/go1.24.2.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.2.linux-amd64.tar.gz

Add to your shell profile (~/.bashrc, ~/.zshrc, etc.):

export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:$(go env GOPATH)/bin

macOS (arm64 / Apple Silicon):

wget https://go.dev/dl/go1.24.2.darwin-arm64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.2.darwin-arm64.tar.gz

Or download the .pkg installer from go.dev/dl and double-click to install.

Windows (amd64):

Download the .msi installer from go.dev/dl and run it. The installer automatically adds C:\Go\bin to your system PATH.

Alternatively, download the .zip archive and extract to C:\Go, then manually add C:\Go\bin to your PATH environment variable.

Important

On all platforms, ensure $GOPATH/bin (default: ~/go/bin on Unix, %USERPROFILE%\go\bin on Windows) is in your PATH. Binaries installed with go install are placed there.

Linux Package Manager

Ubuntu/Debian:

sudo apt update
sudo apt install golang-go

Fedora:

sudo dnf install golang

Arch Linux:

sudo pacman -S go

Warning

Distribution packages often lag behind the official release. For the latest version, use the binary download method. Check your version with go version after installing.

macOS Homebrew

brew install go

Homebrew installs Go to /opt/homebrew/Cellar/go/ (Apple Silicon) or /usr/local/Cellar/go/ (Intel) and symlinks the binary into your PATH.

From Source

Building Go from source requires a working Go installation (Go 1.22.6 or later) as a bootstrap compiler:

git clone https://go.googlesource.com/go goroot
cd goroot/src
./all.bash
ALL TESTS PASSED

---
Installed Go for linux/amd64 in /home/user/goroot
Installed commands in /home/user/goroot/bin

The all.bash script builds the toolchain, runs all tests, and installs binaries to ../bin/. Add that directory to your PATH.

For faster builds without running all tests, use ./make.bash instead.

Post-Install Verification

go version
go version go1.24.2 linux/amd64

Verify your environment:

go env GOROOT GOPATH GOBIN
/usr/local/go
/home/user/go

An empty GOBIN means binaries install to $GOPATH/bin.

GOROOT and GOPATH

  • GOROOT — where Go itself is installed (/usr/local/go on Linux/macOS, C:\Go on Windows). You rarely need to set this manually; the go command infers it from its own location.
  • GOPATH — your workspace for downloaded modules and installed binaries. Defaults to $HOME/go. The go command uses $GOPATH/pkg/mod for the module cache and $GOPATH/bin for installed binaries.

Managing Multiple Go Versions

Install additional Go versions side-by-side:

go install golang.org/dl/go1.23.0@latest
go1.23.0 download
go1.23.0 version
go version go1.23.0 linux/amd64

Each version installs to $HOME/sdk/go1.23.0/ and creates a wrapper command named for that version.


3. Configuration

The go env Command

View all environment variables:

go env

View specific variables:

go env GOOS GOARCH CGO_ENABLED
linux
amd64
1

Persistently set a variable (writes to $GOENV file, default ~/.config/go/env):

go env -w GOPROXY=https://proxy.golang.org,direct

Unset a persistent override:

go env -u GOPROXY

Show only variables that differ from defaults:

go env -changed

Output as JSON:

go env -json

Key Environment Variables

Variable Default Purpose
GOPATH $HOME/go Workspace root — module cache, installed binaries
GOBIN $GOPATH/bin Where go install places binaries
GOPROXY https://proxy.golang.org,direct Module proxy URL(s), comma-separated; direct = fetch from origin
GOPRIVATE (empty) Glob patterns for private modules (skips proxy and checksum DB)
GONOSUMDB (empty) Glob patterns to skip checksum verification
GONOSUMCHECK (empty) Glob patterns to skip checksum lookup in the sum DB
GONOPROXY (empty) Glob patterns to fetch directly (bypass proxy)
GOFLAGS (empty) Default flags applied to all go commands
CGO_ENABLED 1 (native), 0 (cross) Enable/disable cgo (C interop)
GOOS Host OS Target operating system for compilation
GOARCH Host arch Target architecture for compilation
GOTMPDIR System temp Directory for temporary build files
GOMODCACHE $GOPATH/pkg/mod Module download cache location
GOEXPERIMENT (empty) Comma-separated experimental features to enable
GOTOOLCHAIN auto Go toolchain selection policy (local, auto, path, or specific version)
GOTELEMETRY local Telemetry mode: off, local, or on

Tip

Set GOPRIVATE for your company's module path to prevent private code from being sent to the public proxy and checksum database: go env -w GOPRIVATE=github.com/mycompany/*

Module Configuration: go.mod

Every Go project starts with a go.mod file created by go mod init:

go mod init github.com/yourname/myproject

This creates a go.mod file:

module github.com/yourname/myproject

go 1.24.2

require (
    github.com/gin-gonic/gin v1.9.1
    golang.org/x/sync v0.7.0
)

require (
    // indirect dependencies listed here
    golang.org/x/net v0.25.0 // indirect
)

Directives in go.mod:

Directive Purpose Example
module Declares the module path module github.com/user/repo
go Minimum Go version required go 1.24.2
toolchain Specific toolchain version toolchain go1.24.2
require Declares dependencies require golang.org/x/sync v0.7.0
replace Substitutes a module (local dev, forks) replace example.com/mod => ../local-mod
exclude Excludes a specific module version exclude example.com/mod v1.2.3
retract Marks versions as retracted by module author retract v1.0.0

go.sum — the companion file go.sum contains cryptographic checksums of every dependency. It is auto-generated and should be committed to version control. Do not edit it manually.

For the full configuration reference including all build flags, linker options, and advanced compiler directives, see the Admin Guide.


4. Core Usage

The go command is the single entry point for building, testing, managing dependencies, and working with Go code. Every subcommand follows the pattern:

go <command> [arguments]

4.1 Building and Running

go build — Compile Packages

Compile one or more packages into a binary:

go build .

This creates an executable named after the current directory. Specify an output name:

go build -o myapp .
# produces: ./myapp

Common flags:

Flag Purpose
-o <file> Set output file name/path
-v Print package names as they compile
-race Enable race detector (adds ~2x slowdown, ~5-10x memory)
-tags <list> Comma-separated build tags
-ldflags <flags> Pass flags to the linker (e.g., -s -w to strip debug info)
-gcflags <flags> Pass flags to the compiler (e.g., -N -l to disable optimizations)
-trimpath Remove file system paths from binary (reproducible builds)
-buildmode <mode> Build mode: default, c-archive, c-shared, plugin, pie
-pgo <file> Profile-guided optimization using CPU profile
-cover Build with coverage instrumentation
-buildvcs Include version control info (true, false, auto)

Cross-compile for a different platform:

GOOS=windows GOARCH=amd64 go build -o myapp.exe .

Note

Build results are cached. Subsequent builds of unchanged packages are near-instantaneous. The cache lives in $GOCACHE (default: ~/.cache/go-build on Linux).

go run — Compile and Execute

Compile and run in one step — ideal for development:

go run .

Run a specific file:

go run main.go

Pass arguments to your program after --:

go run . -- --port 8080

Or simply after the package pattern:

go run . --port 8080

Run a remote tool directly:

go run golang.org/x/tools/cmd/stringer@latest -type=MyEnum

go install — Compile and Install

Install a binary to $GOBIN:

go install .

Install a tool at a specific version (no go.mod required):

go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1
# binary installed to $GOPATH/bin/golangci-lint

Install the latest version:

go install example.com/tool@latest

4.2 Testing

go test — Run Tests

Run all tests in the current package:

go test
PASS
ok      example.com/myapp    0.042s

Run all tests recursively:

go test ./...

Common flags:

Flag Purpose
-v Verbose — print each test name and status
-run <regex> Run only tests matching the regex
-bench <regex> Run benchmarks matching the regex (use . for all)
-cover Show coverage percentage
-coverprofile <file> Write coverage data to file
-race Enable race detector during tests
-count <n> Run each test n times (use -count=1 to bypass cache)
-timeout <d> Fail tests after duration (default: 10m)
-short Signal tests to skip long-running operations
-fuzz <regex> Run fuzz tests matching regex
-json Output results in JSON (for CI parsing)
-c Compile test binary without running it
-shuffle <mode> Randomize test execution order: on, off, or seed
-failfast Stop after first test failure
-parallel <n> Max parallel tests (default: GOMAXPROCS)

Run tests with coverage and generate an HTML report:

go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
ok      example.com/myapp/pkg    0.031s    coverage: 87.3% of statements

Run benchmarks:

go test -bench=. -benchmem ./...
BenchmarkSort-8     1000000    1042 ns/op    256 B/op    4 allocs/op

Tip

Use -count=1 to bypass the test cache when you need fresh results: go test -count=1 ./.... Go caches passing test results and reuses them when source hasn't changed.

Test File Conventions

Test files must end in _test.go. They can contain four types of test functions:

func TestXxx(t *testing.T) { ... }       // Unit test
func BenchmarkXxx(b *testing.B) { ... }  // Benchmark
func FuzzXxx(f *testing.F) { ... }       // Fuzz test
func ExampleXxx() { ... }                // Testable example (checked output)

Table-driven tests — the idiomatic Go testing pattern:

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 1, 2, 3},
        {"negative", -1, -2, -3},
        {"zero", 0, 0, 0},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Add(tt.a, tt.b); got != tt.expected {
                t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.expected)
            }
        })
    }
}

4.3 Dependency Management

go mod init — Initialize a Module

go mod init github.com/yourname/myproject

Creates go.mod with the module path declaration.

go mod tidy — Synchronize Dependencies

Adds missing dependencies and removes unused ones:

go mod tidy

This is the single most important module command. Run it after adding/removing imports or upgrading Go versions.

go get — Add or Update Dependencies

Add a dependency:

go get github.com/gin-gonic/gin@v1.9.1

Update to latest:

go get -u github.com/gin-gonic/gin

Update all direct and indirect dependencies:

go get -u ./...

Update only patch versions:

go get -u=patch ./...

Remove a dependency:

go get github.com/old/dependency@none

Add a tool dependency (Go 1.24+):

go get -tool golang.org/x/tools/cmd/stringer

Common flags:

Flag Purpose
-u Update to latest version
-u=patch Update only patch versions
-t Include test dependencies
-tool Add as a tool dependency (Go 1.24+)
@version Specify exact version: @v1.2.3, @latest, @none

go mod download — Pre-fill Module Cache

go mod download

Downloads all modules in go.mod to the local cache ($GOMODCACHE) without building. Useful for Docker layer caching:

COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o /app .

go mod vendor — Vendor Dependencies

go mod vendor

Copies all dependencies into a vendor/ directory. Build with -mod=vendor or set GOFLAGS=-mod=vendor.

go mod graph — Show Dependency Graph

go mod graph
example.com/myapp golang.org/x/sync@v0.7.0
example.com/myapp github.com/gin-gonic/gin@v1.9.1
golang.org/x/sync@v0.7.0 golang.org/x/net@v0.25.0

go mod verify — Verify Checksums

go mod verify
all modules verified

Checks that downloaded modules match the expected checksums in go.sum.

go mod why — Explain Why a Module Is Needed

go mod why golang.org/x/net
# golang.org/x/net
example.com/myapp
github.com/gin-gonic/gin
golang.org/x/net/html

go mod edit — Programmatic go.mod Editing

go mod edit -require=github.com/pkg/errors@v0.9.1
go mod edit -replace=example.com/mod=../local-mod
go mod edit -json

4.4 Code Quality

go vet — Static Analysis

Run the default set of analyzers:

go vet ./...

Vet catches common mistakes like: Printf format-argument mismatches, unreachable code, incorrect struct tags, suspicious mutex copies, and more.

Flags:

Flag Purpose
-json Output results in JSON
-vettool <binary> Use an external analyzer binary

Warning

go vet is not a linter — it focuses on correctness bugs, not style. For comprehensive linting, consider tools like golangci-lint which runs go vet plus dozens of additional analyzers.

go fmt — Format Code

Format all Go files in the current package:

go fmt ./...

This runs gofmt -l -w on each file, applying the canonical Go formatting style. There is no configuration — every Go project uses the same format.

go fix — Apply Code Fixes

go fix ./...

Automatically updates code to use newer Go APIs when they replace deprecated ones. Typically run after upgrading Go versions.

4.5 Information and Documentation

go doc — View Documentation

View package documentation:

go doc fmt

View a specific function:

go doc fmt.Println

Show all symbols (including unexported with -u):

go doc -all fmt

View source code:

go doc -src fmt.Println

Start a local documentation server:

go doc -http=:6060

go list — List Packages and Modules

List all packages in the current module:

go list ./...

List with custom format:

go list -f '{{.ImportPath}} {{.GoFiles}}' ./...

List module information:

go list -m -json all

Check for available updates:

go list -m -u all
golang.org/x/sync v0.7.0 [v0.8.0]

List all versions of a module:

go list -m -versions golang.org/x/sync
golang.org/x/sync v0.0.0-20171101214715-fd80eb99c8f6 v0.1.0 v0.2.0 ... v0.8.0

go version — Print Version Information

go version
go version go1.24.2 linux/amd64

Inspect a compiled binary:

go version -m ./myapp
./myapp: go1.24.2
    path    example.com/myapp
    mod     example.com/myapp    (devel)
    dep     golang.org/x/sync    v0.7.0    h1:YsImfS...=
    build   -compiler=gc
    build   GOOS=linux
    build   GOARCH=amd64
    build   -ldflags=-s -w

go env — Environment Information

go env
go env -json GOOS GOARCH
go env -changed

4.6 Code Generation

go generate — Run Directives

Process //go:generate directives in Go source files:

go generate ./...

A directive looks like:

//go:generate stringer -type=Pill

Available variables in generate directives:

Variable Value
$GOARCH Target architecture
$GOOS Target operating system
$GOFILE Name of the file containing the directive
$GOLINE Line number of the directive
$GOPACKAGE Package name of the file
$GOROOT Go root directory
$DOLLAR A literal $ sign

Note

go generate is never run automatically by go build. It is a separate development-time step. Generated files should be committed to version control.

4.7 Workspaces

Workspaces let you work with multiple modules simultaneously without replace directives.

go work init — Create a Workspace

go work init ./module-a ./module-b

Creates a go.work file:

go 1.24.2

use (
    ./module-a
    ./module-b
)

go work use — Add Modules

go work use ./module-c

go work sync — Synchronize Build List

go work sync

Syncs the workspace's build list back into each module's go.mod.

go work edit — Edit go.work Programmatically

go work edit -use=./new-module
go work edit -json

go work vendor — Vendor Workspace Dependencies

go work vendor

4.8 Other Commands

go clean — Remove Cached Files

go clean                   # Remove build artifacts in current dir
go clean -cache            # Clear entire build cache
go clean -testcache        # Clear test result cache
go clean -modcache         # Clear module download cache
go clean -fuzzcache        # Clear fuzz test cache

Caution

go clean -modcache deletes ALL downloaded modules across all projects. This requires re-downloading dependencies for every project on next build. Use only when you need to reclaim disk space or fix a corrupted cache.

go telemetry — Manage Telemetry

go telemetry              # Show current mode
go telemetry off          # Disable telemetry
go telemetry local        # Collect locally only (default)
go telemetry on           # Upload anonymized usage data

go tool — Run Low-Level Tools

Access bundled tools:

go tool                   # List available tools
go tool compile           # Go compiler
go tool link              # Go linker
go tool asm               # Go assembler
go tool cgo               # C/Go bridge
go tool cover             # Coverage analysis
go tool trace             # Execution tracer
go tool pprof             # Profile viewer
go tool nm                # Symbol table
go tool objdump           # Disassembler
go tool addr2line         # Address-to-line mapping

Run the CPU profiler on a profile file:

go tool pprof -http=:8080 cpu.prof

View an execution trace:

go tool trace trace.out

go bug — File a Bug Report

go bug

Opens a pre-filled issue template in your browser with your system information.


5. Common Tasks

5.1 Create a New Project

Goal: Start a new Go project from scratch.

Steps:

mkdir myproject && cd myproject
go mod init github.com/yourname/myproject

Create main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello from myproject!")
}

Verify:

go run .
Hello from myproject!

If it fails: Ensure go is in your PATH (go version). Check that the module path is valid (no spaces, follows Go module naming conventions).

5.2 Add a Dependency

Goal: Import and use an external package.

Steps:

go get github.com/google/uuid@latest

Use it in code:

import "github.com/google/uuid"

id := uuid.New()

Then tidy:

go mod tidy

Verify: go build . succeeds without errors.

If it fails: Check network connectivity. If behind a firewall, set GOPROXY=direct or configure a private proxy.

5.3 Update All Dependencies

Goal: Bring all dependencies to their latest versions.

Steps:

go get -u ./...
go mod tidy

For conservative updates (patch versions only):

go get -u=patch ./...
go mod tidy

Verify:

go build ./...
go test ./...

If it fails: A dependency may have introduced a breaking change. Check go list -m -u all for the specific version jump and review the changelog.

5.4 Run Tests with Coverage

Goal: Measure test coverage across your project.

Steps:

go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
example.com/myapp/pkg/handler.go:15:    HandleRequest    100.0%
example.com/myapp/pkg/handler.go:42:    ParseInput       75.0%
total:                                  (statements)     87.3%

Generate an HTML report:

go tool cover -html=coverage.out -o coverage.html

Verify: Open coverage.html in a browser. Green = covered, red = uncovered.

5.5 Cross-Compile for Different OS/Architecture

Goal: Build binaries for platforms other than your own.

Steps:

# Linux AMD64
GOOS=linux GOARCH=amd64 go build -o myapp-linux-amd64 .

# Windows AMD64
GOOS=windows GOARCH=amd64 go build -o myapp-windows-amd64.exe .

# macOS ARM64 (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o myapp-darwin-arm64 .

# Linux ARM (Raspberry Pi)
GOOS=linux GOARCH=arm GOARM=7 go build -o myapp-linux-armv7 .

Verify:

file myapp-linux-amd64
myapp-linux-amd64: ELF 64-bit LSB executable, x86-64, ...

If it fails: If your code uses cgo, set CGO_ENABLED=0 for pure Go builds or install the target's C cross-compiler.

Tip

See all supported platform combinations with go tool dist list. As of Go 1.24, there are 80+ supported GOOS/GOARCH pairs.

5.6 Build a Static Binary

Goal: Create a fully static binary with no dynamic library dependencies.

Steps:

CGO_ENABLED=0 go build -ldflags='-s -w' -o myapp .
  • CGO_ENABLED=0 — disable cgo, removing glibc dependency
  • -s — strip symbol table
  • -w — strip DWARF debug info

Verify:

ldd myapp
not a dynamic executable

5.7 Use Build Tags

Goal: Conditionally compile code based on tags.

Create a file feature_premium.go:

//go:build premium

package main

func premiumFeature() string {
    return "Premium enabled"
}

Create the default fallback feature_default.go:

//go:build !premium

package main

func premiumFeature() string {
    return "Premium not available"
}

Build with the tag:

go build -tags=premium -o myapp .

5.8 Profile a Program (CPU, Memory)

Goal: Find performance bottlenecks.

Steps — CPU profile via test:

go test -cpuprofile=cpu.prof -bench=. ./...
go tool pprof -http=:8080 cpu.prof

Steps — Memory profile:

go test -memprofile=mem.prof -bench=. ./...
go tool pprof -http=:8080 mem.prof

Steps — Runtime profiling with net/http/pprof:

import _ "net/http/pprof"
// Then: go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

5.9 Write and Run Benchmarks

Goal: Measure function performance.

Create sort_test.go:

func BenchmarkSort(b *testing.B) {
    data := make([]int, 1000)
    for i := range data {
        data[i] = 1000 - i
    }
    b.ResetTimer()
    for b.Loop() {
        slices.Sort(data)
    }
}

Run:

go test -bench=BenchmarkSort -benchmem -count=5 ./...
BenchmarkSort-8   154032   7812 ns/op   0 B/op   0 allocs/op

Compare results across changes using benchstat:

go install golang.org/x/perf/cmd/benchstat@latest
go test -bench=. -count=10 ./... > old.txt
# ... make changes ...
go test -bench=. -count=10 ./... > new.txt
benchstat old.txt new.txt

5.10 Use go generate

Goal: Auto-generate code from directives.

Add a directive to your Go source:

//go:generate stringer -type=Color

type Color int

const (
    Red Color = iota
    Green
    Blue
)

Install the tool and run:

go install golang.org/x/tools/cmd/stringer@latest
go generate ./...

This creates color_string.go with a String() method for your enum.

5.11 Embed Files with go:embed

Goal: Bundle static files (HTML, SQL, configs) into your binary.

package main

import (
    "embed"
    "fmt"
)

//go:embed templates/*.html
var templates embed.FS

//go:embed version.txt
var version string

func main() {
    fmt.Println("Version:", version)
    data, _ := templates.ReadFile("templates/index.html")
    fmt.Println(string(data))
}

Build normally — embedded files are included automatically:

go build -o myapp .

5.12 Use Workspaces for Multi-Module Development

Goal: Develop multiple interdependent modules locally without publishing.

mkdir workspace && cd workspace
git clone https://github.com/yourorg/lib.git
git clone https://github.com/yourorg/app.git
go work init ./lib ./app

Now app automatically uses your local lib changes without replace directives. Build and test across modules:

go build ./...
go test ./...

5.13 Vendor Dependencies

Goal: Include all dependencies in your repository for hermetic builds.

go mod vendor
go build -mod=vendor .

To always use vendor:

go env -w GOFLAGS=-mod=vendor

Verify: The vendor/ directory contains all dependency source code and a vendor/modules.txt manifest.

5.14 Use the Race Detector

Goal: Find data races in concurrent code.

go test -race ./...
go run -race .
go build -race -o myapp .
WARNING: DATA RACE
Read at 0x00c0000a4020 by goroutine 7:
  main.readCounter()
      /home/user/main.go:15 +0x38
Previous write at 0x00c0000a4020 by goroutine 6:
  main.incrementCounter()
      /home/user/main.go:11 +0x48

Warning

The race detector adds significant overhead (~2x CPU, ~5-10x memory). Use it during testing and development, not in production binaries.

5.15 Debug with Delve

Goal: Step-debug a Go program.

Install Delve:

go install github.com/go-delve/delve/cmd/dlv@latest

Start debugging:

dlv debug .
Type 'help' for list of commands.
(dlv) break main.main
(dlv) continue
(dlv) next
(dlv) print myVar
(dlv) goroutines

Debug a test:

dlv test -- -run TestMyFunction

5.16 Use Profile-Guided Optimization (PGO)

Goal: Improve performance using real-world profiling data.

Steps:

  1. Collect a CPU profile from production or representative load:
curl -o default.pgo http://localhost:6060/debug/pprof/profile?seconds=30
  1. Place the profile in the main package directory as default.pgo:
mv default.pgo ./cmd/myapp/default.pgo
  1. Build — PGO is applied automatically when default.pgo exists:
go build -o myapp ./cmd/myapp

Or specify explicitly:

go build -pgo=cpu.pgo -o myapp .

PGO typically improves performance by 2-7% with no code changes.

5.17 Build C-Compatible Libraries

Goal: Create libraries callable from C, Python, Ruby, etc.

C static archive:

package main

import "C"

//export Add
func Add(a, b C.int) C.int {
    return a + b
}

func main() {}
go build -buildmode=c-archive -o libmath.a .

C shared library:

go build -buildmode=c-shared -o libmath.so .

Both produce a header file (libmath.h) for C consumers.

5.18 Pin a Specific Go Toolchain Version

Goal: Ensure everyone on the team uses the same Go version.

Add the toolchain directive to go.mod:

module example.com/myapp

go 1.24.2
toolchain go1.24.2

Or set the policy:

go env -w GOTOOLCHAIN=go1.24.2

With GOTOOLCHAIN=auto (the default), Go automatically downloads the required toolchain version if your local one is older.

5.19 Use GOPRIVATE for Private Repositories

Goal: Fetch modules from private Git repositories.

go env -w GOPRIVATE=github.com/mycompany/*,gitlab.internal.com/*

Configure Git for authentication:

git config --global url."https://oauth2:${GITLAB_TOKEN}@gitlab.internal.com/".insteadOf "https://gitlab.internal.com/"

Or use SSH:

git config --global url."git@github.com:".insteadOf "https://github.com/"

5.20 Create and Run Fuzz Tests

Goal: Discover edge-case bugs with automated fuzzing.

func FuzzParseJSON(f *testing.F) {
    // Seed corpus
    f.Add([]byte(`{"key": "value"}`))
    f.Add([]byte(`{}`))
    f.Add([]byte(`[]`))

    f.Fuzz(func(t *testing.T, data []byte) {
        var result map[string]any
        if err := json.Unmarshal(data, &result); err != nil {
            return // Invalid input is fine
        }
        // Re-marshal and check round-trip
        out, err := json.Marshal(result)
        if err != nil {
            t.Fatalf("Marshal failed: %v", err)
        }
        var result2 map[string]any
        if err := json.Unmarshal(out, &result2); err != nil {
            t.Fatalf("Round-trip failed: %v", err)
        }
    })
}

Run fuzzing for a duration:

go test -fuzz=FuzzParseJSON -fuzztime=30s ./...
fuzz: elapsed: 30s, execs: 142857 (4762/sec), new interesting: 12
PASS

Failing inputs are saved to testdata/fuzz/FuzzParseJSON/ and replayed automatically on subsequent test runs.

5.21 Use go test -json for CI

Goal: Machine-parseable test output for CI pipelines.

go test -json ./... 2>&1 | tee test-results.json

Each line is a JSON object:

{"Time":"2026-05-07T10:00:00Z","Action":"pass","Package":"example.com/myapp","Test":"TestAdd","Elapsed":0.001}

Use gotestsum for better CI integration:

go install gotest.tools/gotestsum@latest
gotestsum --junitfile results.xml ./...

5.22 Build Reproducible Binaries with -trimpath

Goal: Produce identical binaries regardless of the build machine's file layout.

go build -trimpath -ldflags='-s -w' -o myapp .

The -trimpath flag removes local file paths from the binary. Combined with version-pinned dependencies (go.sum) and a pinned toolchain, builds are reproducible across machines.

5.23 Inject Version Info with -ldflags

Goal: Stamp build metadata into your binary at compile time.

package main

var (
    version = "dev"
    commit  = "none"
    date    = "unknown"
)

func main() {
    fmt.Printf("myapp %s (commit: %s, built: %s)\n", version, commit, date)
}

Build with values:

go build -ldflags="-X main.version=1.2.3 -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o myapp .
myapp 1.2.3 (commit: abc123def, built: 2026-05-07T10:00:00Z)

5.24 Use WebAssembly

Goal: Run Go in the browser.

GOOS=js GOARCH=wasm go build -o main.wasm .

Copy the JavaScript support file:

cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .

Create index.html:

<html>
<head><script src="wasm_exec.js"></script></head>
<body>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
    .then(result => go.run(result.instance));
</script>
</body>
</html>

Alternatively, target WASI:

GOOS=wasip1 GOARCH=wasm go build -o main.wasm .

5.25 Use Build Caching Effectively

Goal: Understand and manage Go's build cache.

View cache location:

go env GOCACHE
/home/user/.cache/go-build

Clear the build cache:

go clean -cache

Clear test cache only:

go clean -testcache

View cache size:

du -sh $(go env GOCACHE)

The build cache is safe to delete at any time — it's purely a performance optimization.


6. Troubleshooting

Error: cannot find module providing package example.com/foo

Cause: The dependency is not in your go.mod or the module proxy cannot find it.

Solution:

go get example.com/foo@latest
go mod tidy

If the module is private, ensure GOPRIVATE is set:

go env -w GOPRIVATE=example.com/*

Prevention: Always run go mod tidy after adding new imports.


Error: verifying module: checksum mismatch

Cause: The downloaded module content doesn't match what go.sum records. This can happen when a module author retracts and re-publishes the same version (a violation of module semantics) or when using a misconfigured proxy.

Solution:

  1. If you trust the source, delete the stale entry from go.sum and re-fetch:
go mod tidy
  1. If that fails, clear the module cache:
go clean -modcache
go mod download

Prevention: Never manually edit go.sum. Let Go tooling manage it.


Error: SECURITY ERROR - checksum mismatch from sum database

Cause: The checksum database (sum.golang.org) reports a different checksum than what you downloaded. This could indicate a compromised module or network interference.

Solution: Verify the module's authenticity. If it's a private module:

go env -w GONOSUMDB=example.com/*
go env -w GOPRIVATE=example.com/*

If you trust the module:

go clean -modcache
go mod download

Prevention: Use GOPRIVATE for all internal modules from the start.


Error: module declares its path as "X" but was required as "Y"

Cause: The module directive in the dependency's go.mod doesn't match the import path you used. This typically happens with forks or vanity import paths.

Solution: Use the path declared in the module's go.mod, or add a replace directive:

go mod edit -replace=Y=X@version

Prevention: Check the module's go.mod before adding it as a dependency.


Error: WARNING: DATA RACE during test execution

Cause: Two goroutines access the same variable concurrently without synchronization, and at least one access is a write.

Solution: Add proper synchronization:

  • Use sync.Mutex or sync.RWMutex for shared state
  • Use channels for communication between goroutines
  • Use sync/atomic for simple counters and flags

Prevention: Always run go test -race ./... in CI.


Error: gcc: exec: "gcc": executable file not found in $PATH

Cause: Your code (or a dependency) uses cgo, which requires a C compiler. CGO_ENABLED=1 (the default) but no C compiler is installed.

Solution:

Install a C compiler:

  • Linux: sudo apt install gcc or sudo dnf install gcc
  • macOS: xcode-select --install
  • Windows: Install MinGW-w64 or TDM-GCC

Or disable cgo if you don't need C interop:

CGO_ENABLED=0 go build .

Prevention: Set CGO_ENABLED=0 in CI/Docker builds when you don't need cgo.


Error: permission denied accessing module cache

Cause: The module cache directory ($GOMODCACHE, default $GOPATH/pkg/mod) has incorrect permissions, often caused by running go as root and then as a regular user.

Solution:

sudo chown -R $(whoami):$(whoami) $(go env GOMODCACHE)

Or clear and rebuild:

sudo go clean -modcache
go mod download

Prevention: Never run go commands as root. Use -mod=vendor in Docker containers.


Error: test timed out after 10m0s

Cause: A test or the entire test suite took longer than the default 10-minute timeout.

Solution: Increase the timeout:

go test -timeout 30m ./...

Or fix the slow test — look for:

  • Tests waiting on network calls that hang
  • Infinite loops or deadlocks
  • Tests that don't respect the -short flag

Prevention: Use testing.Short() to skip long tests:

if testing.Short() {
    t.Skip("skipping long test in short mode")
}

Error: import cycle not allowed

Cause: Package A imports package B which imports package A (directly or transitively).

Solution: Break the cycle by:

  1. Moving shared types to a separate package
  2. Using interfaces to invert the dependency
  3. Merging the tightly-coupled packages

Find the cycle:

go vet ./...

Prevention: Keep package dependencies acyclic. Design packages around behaviors, not objects.


Error: build constraints exclude all Go files in /path/to/pkg

Cause: All .go files in the package have build constraints (//go:build) that exclude your current GOOS/GOARCH combination, or the package requires a build tag you haven't specified.

Solution:

Check file headers for build constraints. If the package requires a tag:

go build -tags=required_tag ./...

If cross-compiling, verify you're targeting the right platform:

go env GOOS GOARCH

Prevention: Document required build tags in your README.


Error: GOPATH vs GOROOT confusion

Cause: GOROOT is set to GOPATH or vice versa, or GOROOT points to the wrong directory.

Solution:

go env GOROOT GOPATH
  • GOROOT should point to the Go installation (e.g., /usr/local/go)
  • GOPATH should point to your workspace (e.g., $HOME/go)

Reset to defaults:

go env -u GOROOT
go env -u GOPATH

Prevention: Don't set GOROOT manually. The go command infers it from its own location.


Error: go: updates to go.mod needed

Cause: You're in a module-aware directory but go.mod is out of sync with your imports. Running go commands in read-only module mode (e.g., GOFLAGS=-mod=readonly) prevents automatic updates.

Solution:

go mod tidy

Prevention: Run go mod tidy after any import changes. In CI, use -mod=readonly to catch untidy modules.


Error: proxy/network issues with GOPROXY

Cause: The module proxy is unreachable, blocked by a firewall, or returning errors.

Solution:

Try fetching directly:

GOPROXY=direct go get example.com/module@latest

Or switch to a different proxy:

go env -w GOPROXY=https://goproxy.io,direct

Check if the module exists on the proxy:

curl https://proxy.golang.org/example.com/module/@latest

Prevention: For air-gapped environments, use go mod vendor and build with -mod=vendor.


Error: invalid version: unknown revision

Cause: The version specified in go.mod or go get doesn't exist in the module's repository. This can happen when a tag is deleted or when using a pseudo-version for a non-existent commit.

Solution:

Check available versions:

go list -m -versions example.com/module

Update to a valid version:

go get example.com/module@latest

Prevention: Use tagged versions rather than pseudo-versions when possible.


Issue: Flaky test failures

Cause: Tests that pass inconsistently due to race conditions, timing dependencies, shared state, or environmental assumptions. Common across large Go projects (golang/go issues #76540, #65293, #67599, #73820).

Solution:

  • Run with -race to detect data races
  • Use -count=5 or higher to reproduce intermittent failures
  • Use -shuffle=on to detect order-dependent tests
  • Isolate shared state with t.TempDir() and unique ports
  • Use t.Parallel() carefully — ensure tests don't share mutable state
go test -race -count=10 -shuffle=on -v ./...

Prevention: Design tests to be hermetic — each test should set up and tear down its own state.


Issue: Binary size too large

Cause: Go binaries include the runtime, garbage collector, and all imported packages. Default builds include debug symbols and DWARF info.

Solution:

Strip debug info:

go build -ldflags='-s -w' -o myapp .

Use upx for further compression (at the cost of startup time):

upx --best myapp

Analyze what's consuming space:

go build -o myapp .
go tool nm -size myapp | sort -n -r | head -20

Prevention: Audit imports — each imported package adds to binary size. Avoid large dependencies for small features.


Error: undefined: SomeFunction after upgrading Go version

Cause: A function or type was moved, renamed, or removed in the new Go version, or a build constraint now excludes the file containing it.

Solution:

  1. Check the Go release notes for breaking changes
  2. Run go fix ./... to apply automated fixes
  3. Check build constraints in the affected files

Prevention: Read release notes before upgrading. Test thoroughly with go test ./... after any version change.


Error: module not found in vendor

Cause: The vendor/ directory is out of sync with go.mod. A dependency was added or updated without re-vendoring.

Solution:

go mod vendor
go build -mod=vendor .

Prevention: Always run go mod vendor after go get or go mod tidy when using vendor mode.


Error: WebAssembly wasm_exec.js errors

Cause: The wasm_exec.js support file doesn't match the Go version used to compile the .wasm binary. Each Go version ships its own wasm_exec.js.

Solution:

Always copy wasm_exec.js from the same Go version used for compilation:

cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .
GOOS=js GOARCH=wasm go build -o main.wasm .

For WASI targets (GOOS=wasip1), use wasm_exec_node.js from the same location.

Prevention: Automate the copy step in your build script so it stays in sync with Go upgrades.


Error: corrupt zip file for timezone data

Cause: The embedded timezone database is corrupted, or the system timezone data is missing. Common in minimal Docker images.

Solution:

Install timezone data:

# Alpine
apk add tzdata

# Debian/Ubuntu
apt install tzdata

Or embed timezone data in the binary:

import _ "time/tzdata"

Prevention: Use import _ "time/tzdata" when deploying to minimal containers.


Error: connection is shut down (net/rpc)

Cause: The RPC client is using a connection that was closed by the server or lost due to a network issue. The net/rpc package does not reconnect automatically.

Solution:

Implement reconnection logic in your client. Check client.Call() errors and create a new client when the connection is lost.

Note: net/rpc is frozen — no new features will be added. Consider using gRPC or a similar framework for new projects.

Prevention: Use connection health checks and implement retry logic with backoff.


Issue: Test caching surprises

Cause: go test caches passing results and skips re-execution when source hasn't changed. This can mask failures caused by external state (database, time, network).

Solution:

Bypass the cache:

go test -count=1 ./...

Or clear the test cache:

go clean -testcache

Prevention: Tests that depend on external state should call t.Setenv() or use test helpers that change the test's inputs, which automatically invalidates the cache.


Issue: go vet false positives

Cause: go vet reports issues that are intentional in your code (e.g., intentionally unused results, certain struct tag patterns).

Solution:

Suppress specific analyzers:

go vet -composites=false ./...

Or use a //nolint directive if using golangci-lint, which wraps go vet.

Note that go vet itself doesn't support inline suppression — if an analyzer is too noisy, disable it. However, most go vet warnings are genuine bugs.

Prevention: Investigate each warning before suppressing. go vet has a very low false-positive rate.


Issue: go generate not running

Cause: The //go:generate directive has a syntax error (e.g., space between // and go:generate), the required tool isn't installed, or you're running go generate in the wrong directory.

Solution:

Check the directive syntax — it must be exactly //go:generate with no space:

//go:generate stringer -type=MyEnum   // CORRECT
// go:generate stringer -type=MyEnum  // WRONG — space before 'go'

Ensure the tool is installed:

which stringer || go install golang.org/x/tools/cmd/stringer@latest

Run in the right directory:

go generate ./...  # recursive from current module root

Prevention: Use go generate -v ./... to see which directives are being processed.


Issue: go.work workspace problems

Cause: Workspace mode (go.work) conflicts with go.mod replace directives or CI builds that don't expect a workspace.

Solution:

Disable workspace mode for CI:

GOWORK=off go build ./...

Or remove go.work from version control and document it as a local development tool.

Check which workspace is active:

go env GOWORK

Prevention: Add go.work and go.work.sum to .gitignore if workspaces are developer-local only.


7. FAQ

General

What is Go?

Go is an open-source programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. First released in 2009 (Go 1.0 in 2012), it is a compiled, statically typed language built for systems programming, cloud services, and concurrent applications. The golang/go repository contains the entire language implementation: compiler, runtime, standard library, and toolchain.

Why choose Go over other languages?

Go excels when you need: fast compilation (large projects build in seconds), reliable concurrent programming (goroutines are simpler than threads), easy deployment (single static binary), or strong standard library coverage (HTTP, JSON, crypto, databases built in). It deliberately avoids complexity — no generics inheritance hierarchy, no exceptions, no implicit behavior.

What is Go NOT good for?

Go is not ideal for: GUI desktop applications (limited native GUI support), embedded systems with extreme memory constraints (garbage collector overhead), heavy numerical computing (Python/NumPy or Julia are better), or scripting one-liners (too verbose for trivial scripts).

Is Go "Golang"?

"Go" is the official name. "Golang" is the commonly used search-friendly name (since "Go" is hard to search for). The domain is go.dev, the module path prefix is golang.org, and the repository is github.com/golang/go.


Installation

How do I install multiple Go versions?

Use the version-specific installer pattern:

go install golang.org/dl/go1.22.0@latest
go1.22.0 download
go1.22.0 version

Each version installs independently under $HOME/sdk/. You can switch your default by adjusting GOTOOLCHAIN or your PATH.

What's the difference between GOROOT and GOPATH?

GOROOT is where Go itself is installed — the compiler, standard library source, and tools. You should almost never set it manually. GOPATH is your workspace — where go install puts binaries ($GOPATH/bin) and the module cache lives ($GOPATH/pkg/mod). With modules (Go 1.11+), you no longer need to work inside $GOPATH.

Can I use Go without setting any environment variables?

Yes. The defaults work out of the box for most users. The Go installer sets GOROOT implicitly, GOPATH defaults to $HOME/go, and the default GOPROXY (proxy.golang.org) serves most public modules. You only need to configure environment variables for private modules, cross-compilation, or custom setups.


Modules

What is go.sum and should I commit it?

go.sum contains cryptographic checksums for every version of every module your project depends on (direct and indirect). It ensures that future downloads produce the exact same code. Yes, commit it to version control — it's essential for reproducible builds and security.

How do I use replace in go.mod?

replace redirects a module to a different location — typically a local path during development or a fork:

// Use a local copy during development
replace github.com/original/pkg => ../my-local-pkg

// Use a fork
replace github.com/original/pkg => github.com/myfork/pkg v1.2.3

Run go mod edit -replace to add replacements from the command line. Remember to remove replace directives before publishing your module.

How do I depend on private modules?

Set GOPRIVATE to a comma-separated list of module path prefixes:

go env -w GOPRIVATE=github.com/mycompany/*,go.internal.corp/*

This tells Go to skip the public proxy and checksum database for those modules. You'll also need to configure Git authentication (SSH keys or tokens) for the hosting service.

What does go mod tidy actually do?

It adds require directives for any imported packages not yet in go.mod, removes require directives for packages no longer imported, and updates go.sum with checksums for all dependencies. It's the single command that synchronizes your module metadata with your actual code.

How do I downgrade or pin a dependency version?

Specify the exact version:

go get example.com/module@v1.2.3

To remove a dependency entirely:

go get example.com/module@none
go mod tidy

Building

How does Go handle static linking?

By default, Go produces statically linked binaries when cgo is disabled. With CGO_ENABLED=0, the resulting binary has zero external dependencies:

CGO_ENABLED=0 go build -o myapp .

When cgo is enabled (default on native builds), the binary dynamically links to the system C library. To force static linking with cgo:

CGO_ENABLED=1 go build -ldflags '-linkmode external -extldflags "-static"' -o myapp .

How does cross-compilation work?

Set GOOS and GOARCH before building:

GOOS=linux GOARCH=arm64 go build -o myapp .

Go includes a cross-compiler for every supported platform. No additional toolchains needed for pure Go code. If your code uses cgo, you'll need a C cross-compiler for the target.

What are build tags and how do I use them?

Build tags (build constraints) control which files are included in a build. The modern syntax uses //go:build at the top of a file:

//go:build linux && amd64

Common uses: platform-specific code, optional features, integration tests. Specify tags at build time with -tags:

go build -tags=integration,premium ./...

What's the difference between go build and go install?

go build compiles and writes the binary to the current directory (or -o path). go install compiles and writes the binary to $GOBIN (default: $GOPATH/bin). Use go build for project binaries, go install for tools you want globally available.


Testing

How do I measure test coverage?

go test -cover ./...                            # Summary
go test -coverprofile=c.out ./...               # Write profile
go tool cover -func=c.out                        # Per-function breakdown
go tool cover -html=c.out                        # HTML report

For coverage across the entire binary (not just unit tests), use the -cover flag with go build:

go build -cover -o myapp .
GOCOVERDIR=/tmp/coverage ./myapp
go tool covdata textfmt -i=/tmp/coverage -o coverage.out

How do benchmarks work?

Benchmark functions start with Benchmark and take a *testing.B:

func BenchmarkFoo(b *testing.B) {
    for b.Loop() {
        Foo()
    }
}

Run with go test -bench=.. The test framework automatically determines how many iterations to run for stable timing. Use -benchmem for allocation statistics.

What is fuzz testing?

Fuzz testing (Go 1.18+) automatically generates random inputs to find bugs. A fuzz function provides seed inputs, and Go's fuzzer mutates them to find edge cases. Failures are saved to testdata/fuzz/ and replayed as regression tests.

go test -fuzz=FuzzMyFunc -fuzztime=1m ./...

How do table-driven tests work?

Table-driven tests are the idiomatic Go pattern for testing multiple inputs. Define a slice of test cases, then loop through them with t.Run() for named subtests:

tests := []struct{ name string; in, want int }{
    {"positive", 5, 10},
    {"zero", 0, 0},
}
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        if got := Double(tt.in); got != tt.want {
            t.Errorf("got %d, want %d", got, tt.want)
        }
    })
}

How do I skip tests conditionally?

func TestRequiresNetwork(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping network test in short mode")
    }
    // ...
}

Run with -short to skip them: go test -short ./...


Performance

When should I use goroutines?

Use goroutines when you have tasks that can run concurrently — I/O operations, handling multiple network connections, parallel processing of independent data. Goroutines are cheap (~2KB initial stack) so you can create thousands. Don't use them for CPU-bound tasks that don't benefit from parallelism on a single core — goroutines aren't magic parallelism.

How does Go manage memory?

Go uses a concurrent, tri-color, mark-and-sweep garbage collector. You don't allocate or free memory manually. The GC runs concurrently with your program, pausing all goroutines only briefly (typically <1ms). Tune GC behavior with GOGC (default: 100, meaning GC runs when heap grows 100%) and GOMEMLIMIT (soft memory limit for the process).

GOGC=50 ./myapp     # More frequent GC, less memory
GOGC=200 ./myapp    # Less frequent GC, more memory, less CPU
GOMEMLIMIT=1GiB ./myapp  # Soft memory ceiling

How do I profile a Go program?

Three main approaches:

  1. Test benchmarks: go test -cpuprofile=cpu.prof -memprofile=mem.prof -bench=.
  2. Runtime pprof: import _ "net/http/pprof" then fetch profiles via HTTP
  3. Execution trace: go test -trace=trace.out then go tool trace trace.out

Analyze profiles with: go tool pprof -http=:8080 cpu.prof

What is PGO (profile-guided optimization)?

PGO (Go 1.21+) uses CPU profiles from production runs to optimize the next build. Place a default.pgo file in your main package directory, and go build automatically applies optimizations — inlining hot functions, improving branch prediction, etc. Typical improvement: 2-7%.


Tooling

What's the difference between gofmt and go fmt?

gofmt is the standalone formatting tool with additional flags (-d for diff, -s for simplification). go fmt is a convenience wrapper that runs gofmt -l -w on each .go file in a package. For most users, go fmt ./... is sufficient.

What does go vet check for?

go vet runs a suite of static analyzers that catch common mistakes:

  • Printf format/argument mismatches
  • Unreachable code
  • Incorrect struct tags
  • Suspicious mutex/lock copies
  • Boolean expression simplification
  • Unused results of certain functions
  • Invalid build constraints
  • Assembly/Go declaration mismatches

It has a deliberately low false-positive rate. Every finding should be investigated.

How do I manage tool dependencies?

Go 1.24 introduced tool dependencies. Add tools to your module:

go get -tool golang.org/x/tools/cmd/stringer

This records the tool in go.mod and ensures everyone on the team uses the same version. Run tools with:

go tool stringer -type=MyEnum

How do I see the Go source code for a standard library function?

go doc -src fmt.Println

Or browse the source at cs.opensource.google/go/go or github.com/golang/go.

What is go telemetry?

Go 1.23+ includes opt-in telemetry for the Go team to understand toolchain usage. It defaults to local (collected but not uploaded). Use go telemetry off to disable collection entirely, or go telemetry on to opt in to uploading anonymized data.

How do I write a Go plugin?

Go supports plugins on Linux and macOS (not Windows). Build a plugin:

go build -buildmode=plugin -o myplugin.so ./plugin/

Load it at runtime:

p, _ := plugin.Open("myplugin.so")
sym, _ := p.Lookup("MyFunc")
myFunc := sym.(func() string)
fmt.Println(myFunc())

Plugins must be compiled with the exact same Go version and dependency versions as the host binary.

How do I use go:linkname?

go:linkname is a compiler directive that lets you access unexported functions from other packages. It requires import "unsafe":

import _ "unsafe"

//go:linkname someInternalFunc runtime.someInternalFunc
func someInternalFunc()

This is an unsupported escape hatch — internal APIs can change without notice. Avoid it in production code.

How do I use GOEXPERIMENT?

GOEXPERIMENT enables experimental features that aren't yet the default:

GOEXPERIMENT=rangefunc go build .

Available experiments change between Go versions. Check the release notes for what's available.


Concurrency

What's the difference between goroutines and OS threads?

Goroutines are multiplexed onto a small number of OS threads by Go's runtime scheduler. A goroutine starts with ~2KB of stack (vs ~1MB for an OS thread) and grows dynamically. You can run millions of goroutines on a single machine. The runtime handles scheduling, preemption, and work-stealing across available CPU cores.

How do channels work?

Channels are typed conduits for communication between goroutines:

ch := make(chan string)       // unbuffered
ch := make(chan string, 10)   // buffered (capacity 10)

go func() { ch <- "hello" }()  // send
msg := <-ch                     // receive

Unbuffered channels synchronize sender and receiver. Buffered channels allow sends to proceed until full. Use select for multiplexing multiple channels.

When should I use sync.Mutex vs channels?

Use sync.Mutex when protecting shared data from concurrent access (simple locking). Use channels when communicating between goroutines or when building pipelines. The Go proverb: "Don't communicate by sharing memory; share memory by communicating." In practice, mutexes are simpler for guarding a single data structure; channels are better for coordinating workflows.

How do I wait for multiple goroutines to finish?

Use sync.WaitGroup:

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        process(id)
    }(i)
}
wg.Wait()

Or use golang.org/x/sync/errgroup for goroutine groups with error propagation.

How do I limit concurrency?

Use a semaphore pattern with a buffered channel:

sem := make(chan struct{}, 10) // max 10 concurrent
for _, item := range items {
    sem <- struct{}{} // acquire
    go func(it Item) {
        defer func() { <-sem }() // release
        process(it)
    }(item)
}

Or use golang.org/x/sync/semaphore for a weighted semaphore.


Deployment

What's the recommended Dockerfile for Go?

Use a multi-stage build for minimal images:

FROM golang:1.24 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /app/server .

FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

The scratch base image produces the smallest possible container. Use alpine if you need a shell for debugging or CA certificates (COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/).

How do I handle signals for graceful shutdown?

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

server := &http.Server{Addr: ":8080"}
go func() {
    <-ctx.Done()
    server.Shutdown(context.Background())
}()
server.ListenAndServe()

How do I compile for embedded Linux / IoT devices?

Cross-compile with the appropriate GOOS, GOARCH, and GOARM:

GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags='-s -w' -o myapp .

Go supports ARM (v5, v6, v7), ARM64, MIPS, RISC-V, and other architectures commonly found in embedded devices.