Advanced Language Features Best Practices
Reach for power features only with clear operational need.
These rules distill the advanced-language-features section: default to portable Go, document release commands, and treat plugins, assembly, and linkname as exceptional.
How to Use This List
- Apply when designing release pipelines or reviewing a PR that touches
go:embed, build tags, or-buildmode - Use Tier A before merging any power-feature change
- Promote to Tier B when shipping plugins, c-shared, or assembly to production
- Pair with Beyond the Spec: Embed, Build Modes, and Assembly for conceptual context
- Revisit after Go minor upgrades; plugins and directives break most often across versions
A - Default to portable Go
- Start with ordinary Go and disk/config injection. Reach for embed and build modes only when operations or portability demand it.
- Profile before assembly or noinline. Prove CPU cost with
pprofor benchmarks, not intuition. - Prefer runtime flags over build tags for product toggles. Tags are for platform and toolchain matrices, not daily feature switches.
- Keep platform branches in tagged files, not scattered
runtime.GOOSchecks with foreign imports. One file per platform scales cleaner in review. - Document why a power feature is required in the PR and runbook. Future maintainers should see the operational trigger.
B - Embed and assets
- Embed only assets that must ship inside the binary. Large or volatile files belong on CDN, object storage, or ConfigMaps.
- Never embed secrets from the build machine. Bake defaults only; inject credentials at runtime.
- Use
embed.FSwithfs.Subfor HTTP static routes. Strip prefixes so URL paths stay intuitive. - Version embedded migrations with the binary tag. Schema and code deploy together; record that coupling in release notes.
- Measure binary size delta in CI when embed folders grow. Fail review when artifacts exceed budget thresholds.
C - Build constraints and release commands
- Use
//go:buildon new files; migrate legacy// +buildon touch. Keep constraints readable with parentheses. - CI cross-compiles at least one non-linux
GOOSwhen you ship cross-platform. Windows or darwin catches missing fallbacks early. - Pin
-ldflags,-tags, and-buildmodein Makefile or CI logs. Reproducible artifacts require copy-paste exact commands. - Stamp version with
-X main.version=...using the correct import path. Verify withgo list -f '{{.ImportPath}}' ./cmd/.... - Keep stripped (
-s -w) and debug build targets separate. Operators need symbols during incidents.
D - Plugins, c-shared, and foreign ABIs
- Treat plugins as trusted code in the host address space. No unload API; bad plugins require process restart.
- Rebuild host and plugins on the same Go patch version. Record pairs in a release manifest.
- Share
go.mod/go.sumversions between host and plugin modules. Dependency skew causes subtle crashes atLookup. - Prefer RPC or WASM for untrusted extensions. Reserve
pluginfor controlled on-prem catalogs. - Document platform support: no Windows plugins. Plan alternate extension mechanisms for Windows customers.
E - Assembly and compiler directives
- Provide pure Go fallback for every
.simplementation.//go:build !amd64(or relevant arch) files are mandatory. - Ban
//go:linknamein application packages. Restrict to forks of runtime/stdlib patterns with upgrade owners. - Restrict
//go:noinlineto tests and benchmarks unless a runtime contract requires it. Do not pessimimize hot production paths. - Run targeted arches in CI when
.sfiles exist. amd64-only tests miss broken arm64 syntax. - Run
go fixafter toolchain upgrades and review diffs. Modernizers catch inline and API migrations early.
FAQs
What is the minimum bar before adding go:embed?
Demonstrate that runtime file paths or external volumes are unacceptable for deployment or compliance, and that binary size impact is acceptable.
When are build tags better than configuration files?
When the code must not compile on other platforms at all (different syscalls, cgo availability), not when users simply toggle features.
Should startups use Go plugins?
Rarely.
Prefer static binaries and service boundaries until a paying customer mandates in-process extension with shared ops control.
How do I review assembly PRs?
Require proof of benchmark improvement, arch fallbacks, and a named owner who will fix breakages on Go upgrades.
Can I skip stack version footers on internal drafts?
No for published docs pages.
Readers need to know which Go release assumptions apply.
What belongs in Advanced Features vs CGO section?
Use Advanced Features for embed, tags, buildmode, plugins, assembly, and directives.
Use CGO when the primary problem is C header interop and C memory rules.
Related
- Beyond the Spec: Embed, Build Modes, and Assembly - section anchor
- //go:embed for Files and Templates - asset embedding patterns
- Plugin Packages & Dynamic Loading - plugin operational caveats
- Compiler Directives: //go:noinline, linkname & go:fix inline - directive hazards
- Packages & Modules Best Practices - module hygiene for plugin hosts
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).