Beyond the Spec: Embed, Build Modes, and Assembly
The Go language spec covers types, control flow, and concurrency.
Search across all documentation pages
The Go language spec covers types, control flow, and concurrency.
Production binaries also need static assets, OS-specific code paths, foreign-language interop, and occasionally hand-tuned machine code.
This page is the conceptual anchor for Advanced Language Features.
Advanced Language Features Basics collects runnable snippets; sibling articles cover go:embed, build tags, assembly, linker flags, plugins, and compiler directives.
unsafe, release pipelines, container images, profiling with pprof.Picture a Go binary as three layers.
The language layer is what every developer writes daily: structs, interfaces, goroutines.
The toolchain layer decides which source files compile, what data is baked in, and what object files the linker emits.
The platform layer is where OS/arch differences, foreign ABIs, and hand-written instructions live.
go:embed sits in the toolchain layer.
At compile time the compiler reads files from disk and stores their bytes inside the compiled package.
At runtime you read them through embed.FS or typed variables - no separate asset directory on the deployment host.
Build constraints (build tags and file suffixes) gate which .go files belong in a build.
GOOS=windows might compile handler_windows.go while Linux builds skip it.
This keeps one module tree while shipping platform-correct behavior.
buildmode tells the linker what artifact to produce: a normal executable, a C archive, a C shared library, or a Go plugin .so.
Each mode changes symbol visibility, initialization, and how downstream consumers link or load the result.
Assembly is the escape hatch when Go code or the compiler cannot express the instruction sequence you need.
Go uses a Plan 9-derived assembler dialect, not Intel syntax, and it must cooperate with Go's calling convention and stack maps.
Source tree
|
+-- *.go (always, unless build-tagged out)
+-- *_linux.go / //go:build windows
+-- //go:embed assets/
+-- *.s (assembly, same package)
|
v
go build [-tags ...] [-buildmode ...] [-ldflags ...]
|
v
Binary / .a / .so / plugin
Embedding runs before the linker.
The embed package types (embed.FS, []byte, string) hold read-only data.
The compiler rejects patterns that escape the package directory or embed variable directories unsafely.
Build constraints combine with GOOS, GOARCH, and custom tags from -tags.
A file may require linux && amd64 while another provides a generic fallback.
The build list is the union of files whose constraints evaluate true.
buildmode interacts with cgo and plugins.
c-shared exports a C ABI surface; plugin produces a shared object the plugin package opens at runtime.
Both demand disciplined release processes: matching Go versions, compatible dependency graphs, and often identical build flags.
Assembly functions are declared in Go and implemented in .s files in the same package.
The Go declaration provides the type-safe entry; the assembler implements TEXT symbols the Go linker wires up.
Getting register usage or stack bounds wrong corrupts the garbage collector's assumptions.
| Approach | Strength | Weakness | Best Fit |
|---|---|---|---|
| go:embed | Single artifact, no runtime file IO for assets | Larger binaries, rebuild to change assets | CLIs, migrations, default configs |
| Build tags | Clean per-platform code without runtime branches | Matrix of files to maintain | OS-specific syscalls, arch-tuned code |
| buildmode c-shared | Ship Go logic as .so / .dylib for C hosts | ABI design, cgo overhead | Language bridges, legacy C apps |
| buildmode plugin | Runtime extension without restarting process | Version lock, Linux/macOS only | Controlled plugin hosts |
| Assembly | Maximum control in hot loops | Hard to read, test, and port | crypto kernels, SIMD after proof |
Operational teams should treat these features as release concerns, not style choices.
Embedded SQL migrations mean schema changes require a new binary tag.
Plugins require the host and plugin built with the same toolchain minor version and compatible dependency versions.
Assembly changes may need review on every new GOARCH you support.
Security review matters too: //go:linkname and plugins widen the attack surface; embed paths must not pull in secrets from the build machine accidentally.
//go:build line replaced the old // +build comment style, but conditional compilation is first-class.Containers can mount files at runtime.
Embed still helps when you want one self-contained binary, simpler tests, or assets that must never be missing on disk.
No.
Patterns are relative to the package containing the //go:embed directive and cannot use ...
Explicit //go:build lines express arbitrary boolean expressions.
Suffixes like _windows.go are shorthand the toolchain applies automatically for matching GOOS/GOARCH.
No.
The plugin package supports Linux, FreeBSD, and macOS on supported architectures.
When the work is pure computation inside Go's memory model and you can express it in Plan 9 assembly.
Reach for cgo when you must call an existing C library or OS API not exposed in syscall/x/sys.
Many directives are toolchain implementation details.
Treat //go:noinline, //go:linkname, and similar hints as rare, reviewed exceptions.
Embedded files live in the module source tree and are compiled into the package artifact.
They are not fetched separately at go mod download time.
Support varies.
go:embed and build tags are widely useful; plugins and some build modes are toolchain- and target-specific.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026