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


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