Pick Code by Operating System With Go File Suffixes
A file named name_linux.go or name_arm64.go is built only on that OS or CPU with no build comment. See the valid pairs, the unix suffix trap and how to check.

The shortest way to write platform-specific Go is to name the file for the platform. No comment, no tag, no build system: the toolchain reads the end of the file name and decides. It is the technique behind most of os, syscall and net in the standard library. It is also one of those conventions where a single wrong word silently builds the wrong code. This tip covers the pattern, the check that proves it works, and the trap that catches everyone once. It is part of the Go basics path, the file-name companion to build tags, and a common cause of undefined name errors.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
The pattern
Put one definition per platform in files named for it. The shared code calls the function, and the toolchain picks:
package main
import "fmt"
func main() { fmt.Println(platform()) }package main
func platform() string { return "linux" }package main
func platform() string { return "darwin" }package main
func platform() string { return "windows" }go run .
GOOS=linux go build -o /dev/null . && echo "linux build ok"
GOOS=windows go build -o /dev/null . && echo "windows build ok"darwin
linux build ok
windows build okOn this macOS machine go run . printed darwin, since only plat_darwin.go matched. Setting GOOS cross compiled for the other two, and both succeeded. The names can also combine an OS and an architecture, net_linux_arm64.go, in that order. A file can be limited to an architecture alone, hash_amd64.go, which is how assembly implementations sit beside a pure Go fallback in hash_generic.go with a //go:build !amd64 line.
Prove it covers every target
The compile succeeded for Linux and Windows because both have a definition. A platform nobody wrote one for is the usual failure, and the toolchain says it in the shortest possible way:
GOOS=freebsd go build -o /dev/null . 2>&1; true# example.test/lab
./main.go:5:27: undefined: platformThe result is undefined: platform, the same message as any missing name, with nothing in it to suggest a platform file is missing. That is why I run a cross compile for each target in CI, listing the values you support. go tool dist list prints every GOOS/GOARCH pair the toolchain knows, and those are exactly the words that count as suffixes.
The unix trap
//go:build unix is a valid constraint, covering every Unix-like system. The name unix is not a valid file suffix, though, because the suffix list is only operating systems and architectures. A file called plat_unix.go has no restriction at all and is compiled everywhere:
package main
import "fmt"
func main() { fmt.Println(platform()) }package main
func platform() string { return "unix" }GOOS=windows go list -f '{{.GoFiles}}' .[main.go plat_unix.go]The listing for Windows includes plat_unix.go. If a plat_windows.go existed beside it, the two definitions would collide with a platform redeclared error, on Windows only, which is when you discover it. The fix is to say what you mean inside the file, //go:build unix, and name the file anything. This is the most common mistake with the feature, and it is easy to make because unix looks like a platform name. The same applies to any word that is not in go tool dist list: _bsd.go, _posix.go, _mac.go and _win.go are ordinary file names.
Choosing between a suffix and a comment
Use the suffix when a file is wholly for one OS or architecture. It is visible in a directory listing, nobody can misplace it and the toolchain, editors and linters all understand it. Use a //go:build line when the condition is not a single name: linux || darwin, !cgo, amd64 && !purego, or a custom tag. You can combine them. A file named x_linux.go can still carry //go:build !android, and both must hold, which is the answer when you want Linux but not Android, since Go counts android as a linux for suffix purposes.
Where this does not reach
The suffix chooses files, not lines. Differences inside a function, a path separator or a signal name, belong in a small helper per platform, or in runtime.GOOS at run time when the difference is cheap and the code compiles everywhere. Compile-time selection is the right tool when one platform's code does not even build on another, as with system calls. The rules are spelled out in the go command's build constraints section, and the standard library's os package is the best place to see the pattern at scale.


