Best Practices & Deployment
Learn idiomatic Go style with gofmt and effective naming, how to build production binaries with go build, cross-compilation with GOOS/GOARCH, and a brief look at Docker deployment.
Introduction
You have now covered the language itself, concurrency, I/O, JSON, web servers, testing, and generics. This final lesson closes the loop: how to write Go that looks like Go, how to compile it into a real deployable binary, how to target other operating systems and architectures from a single machine, and how that binary typically ends up running in production.
- How gofmt enforces a single, consistent Go style automatically.
- Idiomatic naming conventions used throughout the Go ecosystem.
- How to build an optimized, production-ready binary with go build.
- How to cross-compile for a different OS/architecture using GOOS and GOARCH.
- How a Go binary typically fits into a minimal Docker deployment.
Idiomatic Style with gofmt
Go ships with an opinionated formatter, gofmt, that rewrites your source code into the one true Go style — consistent indentation, spacing, and alignment. There is deliberately no configuration; every Go codebase in the world is formatted the same way, which eliminates an entire category of style debates.
gofmt -l . # list files that are not formatted correctlygofmt -w . # rewrite files in placego vet ./... # catch suspicious constructs gofmt cannotMost Go editors and IDEs run gofmt automatically on save. Combined with go vet (which flags suspicious code like format-string mismatches), this should be part of every developer's default workflow.
Effective Naming
Go favors short, clear names over long, descriptive ones — especially for local variables with a small scope. Package names are short, lowercase, and singular; exported identifiers use MixedCaps rather than underscores.
| Convention | Example | Notes |
|---|---|---|
| Package names | http, json, mathutil | Short, lowercase, no underscores. |
| Exported identifiers | ReadFile, HTTPClient | MixedCaps, capitalized first letter. |
| Unexported identifiers | readBuffer, isValid | MixedCaps, lowercase first letter. |
| Short-lived local variables | i, err, w, r | Short names are idiomatic in small scopes. |
| Acronyms | ID, URL, HTTP (not Id, Url, Http) | Keep acronyms fully capitalized. |
Building Production Binaries
go build compiles your program into a single, statically linked executable with no external runtime dependency — no interpreter, no JVM, nothing else to install on the target machine. For production, it is common to strip debug information to shrink the binary and pass build-time variables like a version string.
go build -o myapp .
# a leaner production build: strip symbols, inject a version stringgo build -ldflags="-s -w -X main.version=1.4.0" -o myapp .Click Run to see what this code prints.
That single myapp file is everything you need to run the program — copy it to a server, or into a container, and execute it directly.
Cross-Compilation with GOOS/GOARCH
One of Go's standout deployment features is built-in cross-compilation: you can build a binary for Linux, Windows, or macOS, on any architecture, directly from your development machine, just by setting two environment variables before go build.
GOOS=linux GOARCH=amd64 go build -o myapp-linux-amd64 .GOOS=windows GOARCH=amd64 go build -o myapp-windows-amd64.exe .GOOS=darwin GOARCH=arm64 go build -o myapp-macos-arm64 .| GOOS | GOARCH | Target |
|---|---|---|
| linux | amd64 | Standard 64-bit Linux servers |
| linux | arm64 | ARM-based Linux servers/containers (e.g. AWS Graviton) |
| windows | amd64 | 64-bit Windows |
| darwin | arm64 | Apple Silicon Macs |
This means a CI pipeline running on a single Linux build agent can still produce ready-to-run binaries for every platform your users need, with no separate build machines required.
A Brief Note on Docker Deployment
Because a Go binary is statically linked and self-contained, it pairs naturally with a minimal, multi-stage Docker build: compile in a full Go image, then copy just the resulting binary into a tiny final image with no Go toolchain at all.
FROM golang:1.22 AS builderWORKDIR /appCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .
FROM scratchCOPY --from=builder /app/myapp /myappENTRYPOINT ["/myapp"]The final image built from scratch contains nothing but your compiled binary — often just a few megabytes — which keeps deployment images small, fast to pull, and with a minimal attack surface.
Common Mistakes
- Committing unformatted code instead of running gofmt (or letting your editor do it) before every commit.
- Using verbose, Java-style getter/setter naming instead of idiomatic short Go names.
- Forgetting CGO_ENABLED=0 when building for a minimal base image like scratch or alpine, causing a dynamically linked binary that fails to run.
- Shipping debug symbols and unstripped binaries to production, bloating the deployed artifact.
- Ignoring go vet warnings, missing real bugs it was specifically designed to catch.
Best Practices
- Run gofmt and go vet as part of your standard workflow, ideally enforced in CI.
- Follow Go naming conventions: short local names, MixedCaps for exported identifiers, capitalized acronyms.
- Build with -ldflags="-s -w" for production to strip debug information and reduce binary size.
- Use multi-stage Docker builds so your final image contains only the compiled binary, not the whole toolchain.
- Set CGO_ENABLED=0 for fully static binaries when targeting minimal container base images.
Frequently Asked Questions
No. go build produces a statically linked, self-contained binary that runs directly on the target OS/architecture with no separate Go installation required.
CGO_ENABLED controls whether cgo (linking against C libraries, such as for certain DNS or SQLite drivers) is used. Setting it to 0 forces a fully static binary, which is required for minimal images like scratch that have no C runtime at all.
Yes. Set GOOS=windows GOARCH=amd64 before go build, and the Go toolchain cross-compiles the executable without needing a Windows machine at all.
Key Takeaways
- gofmt enforces one consistent style across every Go codebase, with no configuration needed.
- Go naming favors short, clear identifiers and MixedCaps over underscores.
- go build produces a single, statically linked, dependency-free production binary.
- GOOS and GOARCH enable cross-compilation for any target platform from any development machine.
- Multi-stage Docker builds combine well with Go binaries to produce small, minimal deployment images.
Summary
Idiomatic style, a simple build toolchain, effortless cross-compilation, and a natural fit for minimal container images are a big part of why Go has become a default choice for backend services and command-line tools alike. With this lesson, you have gone from Go fundamentals all the way through concurrency, real-world I/O, and production deployment.