One binary ships with the language and covers dependency management, building, testing, formatting and static checks. ./... means “this package and everything below it”.
The go command
| Task | Command |
|---|---|
| Create a project | go mod init example.com/app |
| Add a dependency | go get github.com/x/y@v1.4.0 |
| Sync deps with imports | go mod tidy |
| Why is a dep here? | go mod why -m github.com/x/y |
| Run without building | go run ./cmd/server |
| Build a binary | go build -o bin/app ./cmd/server |
| Install a CLI tool | go install github.com/air-verse/air@latest |
| Pin a dev tool in the project | go get -tool ... → go tool ... |
| Test | go test ./... |
| Format | gofmt -w . / go fmt ./... |
| Static checks | go vet ./... |
| Docs in the terminal | go doc net/http.ServeMux |
| Multi-module workspace | go work init ./svc-a ./svc-b |
| Generate code | go generate ./... |
Modules
go.moddeclares the module path, Go version and direct dependencies;go.sumpins checksums. Commit both.- The
goline is the minimum language version; atoolchainline can require a newer toolchain, which is downloaded automatically. - Major versions ≥ 2 are part of the import path:
github.com/jackc/pgx/v5.
Build
# Cross-compile: just set two env vars
GOOS=linux GOARCH=arm64 go build -o bin/app-linux-arm64 ./cmd/server
# Static, reproducible, stripped, with an injected version
CGO_ENABLED=0 go build -trimpath \
-ldflags="-s -w -X main.version=1.4.2" -o bin/app ./cmd/server
| Flag | Effect |
|---|---|
CGO_ENABLED=0 |
No libc dependency → fully static, runs on scratch / distroless |
-trimpath |
Removes local file paths from the binary |
-ldflags="-s -w" |
Strips symbol table and DWARF (smaller binary) |
-X pkg.var=value |
Sets a string variable at link time |
go version -m bin/app shows the module versions and build settings baked into any Go binary.
Check
go vet ./... # suspicious code the compiler accepts
go test -race ./... # data race detector
go test -bench=. -benchmem ./... # benchmarks with allocations
govulncheck ./... # known CVEs in code paths you actually call
Beyond the built-ins, staticcheck or golangci-lint bundle many more analysers.
Ship: multi-stage container build
FROM golang:1 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download # cached layer while deps don't change
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /app ./cmd/server
FROM gcr.io/distroless/static-debian12
COPY --from=build /app /app
ENTRYPOINT ["/app"]
The final image contains one binary and a few CA/timezone files: tiny, and nearly nothing to patch.
Dev loop
- Live reload: air rebuilds and restarts on file changes.
- Makefile as the entry point (
make run,make test,make build) keeps commands discoverable; Go itself has no task runner. go generateruns//go:generatedirectives for codegen (mocks, OpenAPI, sqlc, stringer).