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
GOOSandGOARCH - 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.modand reproducible builds viago.sum - Built-in testing —
go testincludes 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, usego buildto create a reusable binary.
What's next:
- Section 2: Installation — detailed setup for every platform
- Section 3: Configuration — environment variables and module setup
- Section 4: Core Usage — every
gosubcommand explained - Section 5: Common Tasks — 25+ recipes for real-world workflows
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
Binary Download (Recommended)
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/binon Unix,%USERPROFILE%\go\binon Windows) is in yourPATH. Binaries installed withgo installare 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 versionafter 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/goon Linux/macOS,C:\Goon Windows). You rarely need to set this manually; thegocommand infers it from its own location. - GOPATH — your workspace for downloaded modules and installed binaries. Defaults to
$HOME/go. Thegocommand uses$GOPATH/pkg/modfor the module cache and$GOPATH/binfor 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
GOPRIVATEfor 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-buildon 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=1to 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 vetis not a linter — it focuses on correctness bugs, not style. For comprehensive linting, consider tools likegolangci-lintwhich runsgo vetplus 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 generateis never run automatically bygo 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 -modcachedeletes 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+ supportedGOOS/GOARCHpairs.
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:
- Collect a CPU profile from production or representative load:
curl -o default.pgo http://localhost:6060/debug/pprof/profile?seconds=30
- Place the profile in the main package directory as
default.pgo:
mv default.pgo ./cmd/myapp/default.pgo
- Build — PGO is applied automatically when
default.pgoexists:
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:
- If you trust the source, delete the stale entry from
go.sumand re-fetch:
go mod tidy
- 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.Mutexorsync.RWMutexfor shared state - Use channels for communication between goroutines
- Use
sync/atomicfor 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 gccorsudo 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
-shortflag
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:
- Moving shared types to a separate package
- Using interfaces to invert the dependency
- 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
GOROOTshould point to the Go installation (e.g.,/usr/local/go)GOPATHshould 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
-raceto detect data races - Use
-count=5or higher to reproduce intermittent failures - Use
-shuffle=onto 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:
- Check the Go release notes for breaking changes
- Run
go fix ./...to apply automated fixes - 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:
- Test benchmarks:
go test -cpuprofile=cpu.prof -memprofile=mem.prof -bench=. - Runtime pprof:
import _ "net/http/pprof"then fetch profiles via HTTP - Execution trace:
go test -trace=trace.outthengo 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.