Notes — Module 18: Build, Tooling, and Deployment¶
These are your personal study notes. Write freely and honestly. Incomplete notes are fine — they show where your understanding still needs work. Return to this file to add insights as they develop over time.
Module: [[go/18. Build, Tooling, and Deployment]] Topic: [[go]] Date started: YYYY-MM-DD Status: In progress
Concept Map¶
Sketch how the concepts in this module relate to each other. Fill in the Mermaid diagram.
mindmap
root((Build, Tooling & Deployment))
go command
build / install / run
vet / fmt / env
mod tidy / work
clean -cache
Build constraints
//go:build tags
file name conventions
custom tags -tags flag
Version stamping
-ldflags "-X pkg.Var=value"
git describe + commit SHA
-s -w strip symbols
Cross-compilation
GOOS / GOARCH matrix
CGO_ENABLED=0 static binary
go tool dist list
Build cache
GOCACHE location
reproducible builds
Linting
go vet built-in
staticcheck
golangci-lint .golangci.yml
gofmt / goimports
Makefile / Taskfile
orchestrates all steps
Docker multi-stage
builder stage toolchain
scratch or distroless final
layer caching go.mod first
GitHub Actions CI
test with -race
golangci-lint job
build matrix GOOS/GOARCH
GoReleaser
GitHub Release automation
checksums.txt
Supply-chain security
go.sum integrity
govulncheck CVE scanning
GOPROXY settings
GONOSUMDB
Alternative: draw this on paper, photo it, and link the image here.
Key Insights¶
The "aha moments" — the things that, once understood, made the rest clear. Be specific: "I finally understood X because Y" is more useful than "X makes sense".
- CGO_ENABLED=0 is not optional for scratch: I finally understood that a "Linux binary" built with CGO enabled is not necessarily self-contained — it may dynamically link against glibc, which doesn't exist in a scratch image. The distinction between a linked Go binary and a static Go binary clicked when I ran
lddon both. - -ldflags -X requires the full import path: I wasted 30 minutes wondering why my version wasn't stamped. The package path must be the full module path, not just the local package name. There is no compile error — the wrong path silently produces no effect.
- Add insights as you discover them
My Understanding¶
Explain the core concepts in your own words, as if teaching them to someone else. If you can't explain it simply, you don't understand it well enough yet.
The go Command Toolchain¶
Your explanation here
What I'm still unsure about: (e.g., the difference between go build and go install, and exactly where go install puts the binary)
Build Constraints¶
Your explanation here
What I'm still unsure about: (e.g., whether file name conventions and //go:build directives can conflict, and which takes precedence)
Cross-Compilation and CGO_ENABLED=0¶
Your explanation here
What I'm still unsure about: (e.g., which parts of the standard library actually use cgo on Linux by default — is it just net and os/user?)
Multi-Stage Docker Builds¶
Your explanation here
What I'm still unsure about: (e.g., when to choose scratch vs distroless — what does distroless actually provide that scratch doesn't?)
Supply-Chain Security¶
Your explanation here
What I'm still unsure about: (e.g., exactly what govulncheck checks vs a simple go list -m all version audit)
Connections to Other Topics¶
How does this module connect to things you already know?
| This module's concept | Connects to | How |
|---|---|---|
| CGO_ENABLED=0 and static binaries | [[go/17. Runtime Internals and the Memory Model]] | Disabling cgo removes the dynamic link to glibc; understanding what cgo does (bridge to C libraries) explains why disabling it makes the binary self-contained |
go.sum and GOPROXY |
[[go/7. Packages and Modules]] | The module system's cryptographic integrity layer; go.sum is produced by go mod tidy and verified on every download |
go test -race in CI |
[[go/11. Testing and Benchmarking]] | The race detector is a compile-time flag; understanding what it instruments (memory access logging) helps interpret its output in CI |
| Build cache and incremental compilation | [[go/16. Performance and Profiling]] | The build cache stores SSA-compiled package objects; cache invalidation mirrors recompilation boundaries |
Questions That Arose¶
Log questions as they appear. Don't stop to answer them now — just capture them. Then move the serious ones to QUESTIONS.md.
- What is the difference between
go build ./...andgo build .? Does./...build all binaries in the module? → added to QUESTIONS.md as Q001 - When GoReleaser runs, does it rebuild from source, or can it use cached artifacts? → might be answered in GoReleaser docs
- Does
govulncheckscan only direct dependencies or the full transitive graph? → added to QUESTIONS.md as Q002
Code Snippets Worth Remembering¶
Patterns, idioms, or examples that captured something important.
Version-stamping build command¶
go build \
-ldflags "-s -w \
-X 'github.com/acme/myapp/internal/version.Version=$(git describe --tags --always)' \
-X 'github.com/acme/myapp/internal/version.Commit=$(git rev-parse --short HEAD)' \
-X 'github.com/acme/myapp/internal/version.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" \
-o bin/myapp .
Why I'm saving this: This is the canonical build-time version stamping pattern. The -s -w flags reduce binary size; the three -X flags inject version info. Works in Makefiles and CI without modification.
Dockerfile layer ordering for module cache¶
# FAST: cache go mod download separately from source
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
Why I'm saving this: Layer ordering is the most impactful Dockerfile optimization for Go. go mod download is slow; source changes are frequent. Separating them means most rebuilds skip the module download entirely.
Static binary verification¶
CGO_ENABLED=0 GOOS=linux go build -o myapp .
ldd myapp
# Expected: "not a dynamic executable"
# If you see glibc entries, CGO is still active somehow
Why I'm saving this: Always verify with ldd before assuming a binary is static. CGO can be pulled in transitively through indirect dependencies.
What Tripped Me Up¶
Mistakes I made, misconceptions I had, things that confused me more than they should have. Being honest here helps you later.
- The -X flag path — I initially tried
-X version.Version=v1.0.0(short path), but it silently did nothing. It requires the full module-qualified import path. No warning, no error — just the default value. - CGO_ENABLED in multi-module workspaces — When using
go work,CGO_ENABLED=0must be set for each module being built, not just at the workspace level. Setting it as an environment variable covers all modules in the workspace.
Summary in My Own Words¶
Write a 3–5 sentence summary of this entire module without looking at any notes. If you can't do this, you need more study time.
Write your summary here after completing the module.
Last updated: YYYY-MM-DD