What are build tags/constraints, and when would you use them?
Quick Answer
A build tag (or build constraint) is a special comment, //go:build linux, at the top of a file that tells the Go toolchain to include or exclude that file from compilation based on conditions like OS, architecture, Go version, or custom tags passed via -tags. They're used for platform-specific code (a file with //go:build windows containing Windows-specific syscalls, alongside a //go:build !windows file with a different implementation of the same functions), for excluding integration tests from a normal test run (//go:build integration, only compiled when you explicitly run go test -tags=integration), or for conditionally compiling optional features. Filename suffixes like _windows.go, _linux.go, or _test.go provide an equivalent, implicit build constraint without needing an explicit //go:build comment.
Detailed Answer
Build tags let a single codebase compile differently depending on the target platform or an explicit flag, without runtime branching.
Explicit build tags
//go:build linux
package syscall_wrapper
func platformSpecificCall() { /* Linux-specific implementation */ }
//go:build !linux
package syscall_wrapper
func platformSpecificCall() { /* fallback for everything else */ }
Only one of these two files compiles into the final binary, depending on GOOS, with the compiler choosing automatically. There's no if runtime.GOOS == "linux" at runtime; the exclusion happens entirely at compile time.
Filename-based constraints (implicit, no comment needed)
worker_windows.go // only compiled when GOOS=windows
worker_linux.go // only compiled when GOOS=linux
worker_test.go // only compiled when running `go test`
Go recognizes _GOOS.go, _GOARCH.go, and _test.go suffixes automatically, as an alternative to writing an explicit //go:build line.
Custom tags for optional features or test categories
//go:build integration
package myapp_test
func TestDatabaseIntegration(t *testing.T) { /* needs a real database */ }
go test ./... # skips this file entirely
go test -tags=integration ./... # includes it
This is a common pattern for keeping slow, environment-dependent integration tests out of the default go test ./... run used during everyday development, while still keeping them in the same repository and easily runnable in CI with the right flag.
The practical guidance
Reach for build tags specifically when code genuinely needs to differ per platform or build mode, not as a general-purpose feature-flagging mechanism. For runtime-configurable behavior, a normal if branch or config value is almost always the simpler, better choice.