Absorbed content from rodin/golang-conventions and rodin/prometheus-conventions into a sources/ directory. Reference material — descriptive, not prescriptive. Part of taxonomy cleanup (elixir-patterns issue #4).
8.2 KiB
Go Language Source: Convention Reference
Quick-reference for conventions extracted from the golang/go source code. Each entry: pattern name, location, example, when to use, when NOT to use, origin.
Owner-Attributed TODOs
Location: Throughout src/
// TODO(gri): consider using a different approach here
// TODO(rsc): this should be cleaned up in the next release
When to use: Known limitations that a specific person should address. The owner tag creates accountability without creating an issue (issues are for user-visible problems; TODOs are for internal engineering debt).
When NOT to use: User-visible bugs (file an issue instead). Aspirational improvements with no clear owner. Anything that should block a release.
Origin: Convention since Go's earliest commits. The Go team averages 3,428 TODOs across the codebase — this is a conscious engineering culture, not neglect.
internal/ Packages
Location: src/internal/ (61 packages)
// internal/singleflight — dedup concurrent calls
// internal/godebug — runtime feature flags
// internal/poll — OS I/O polling
// internal/bisect — binary search debugging
When to use: Code shared between stdlib packages that should NOT become public API. Utility code that isn't stable enough for the compatibility promise. Implementation details that users shouldn't depend on.
When NOT to use: Code that external packages need. Code that's stable enough for public API (promote it). One-off helpers that only one package uses (keep them package-private).
Origin: src/internal/ existed since Go moved sources from
src/pkg to src (2014). The compiler enforces the import restriction
— no code outside the tree can import internal packages.
GODEBUG: Runtime Feature Flags
Location: internal/godebug/godebug.go (316 lines)
var http2server = godebug.New("http2server")
func ServeConn(c net.Conn) {
if http2server.Value() == "0" {
// user opted out of HTTP/2
}
// IncNonDefault must be called each time non-default behavior fires
http2server.IncNonDefault()
}
When to use: Behavior changes that might break existing programs.
The old behavior becomes accessible via GODEBUG=setting=value. Tied
to go.mod's go directive for automatic version-based defaults.
When NOT to use: Bug fixes that no reasonable program depends on. New features (use GOEXPERIMENT instead). Performance optimizations that don't change observable behavior.
Origin: Brad Fitzpatrick added the package in Aug 2021. Russ Cox formalized it as the backward compatibility mechanism in proposal #56986 (Nov 2022, 70 comments). 79 settings now exist.
GOEXPERIMENT: Compile-Time Feature Gates
Location: internal/goexperiment/flags.go (136 lines)
// Set via: GOEXPERIMENT=jsonv2 go build
// Check via build tag: //go:build goexperiment.jsonv2
When to use: Major new APIs or behavior changes that need real-world testing before committing to the compatibility promise. The code lives in-tree but isn't visible without the flag.
When NOT to use: Small features that can ship directly. Bug fixes. Internal refactoring. Anything that doesn't need user feedback before committing.
Origin: The GOEXPERIMENT mechanism predates its formalization — used for fieldtrack, regabi, unified. json/v2 (Apr 2025) is the highest- profile use: 6,387 lines shipped behind a flag.
Compiler Directives
Location: Throughout runtime/ and cmd/
//go:linkname localName remote/pkg.ExportedName
//go:nosplit
//go:nowritebarrier
//go:noescape
//go:systemstack
When to use: ONLY in the runtime and compiler. linkname accesses
unexported symbols across packages. nosplit prevents stack growth
checks. nowritebarrier asserts no GC barriers. systemstack forces
execution on the system stack.
When NOT to use: In application code. In stdlib packages outside
runtime. In third-party packages (explicitly unsupported — the Go team
actively removes go:linkname targets that external packages depend
on).
Origin: 1,711 go:linkname and 2,428 other runtime directives.
The Go team is actively trying to reduce linkname usage (92 touches
in 2024, many removals).
Generated Code (Checked In)
Location: cmd/compile/internal/ssa/
// Code generated by ssa/gen/*.go; DO NOT EDIT.
// opGen.go — 97,135 lines
// rewriteAMD64.go — 79,703 lines
// rewritegeneric.go — 38,337 lines
When to use: When the generated output needs git blame history.
When generators should be auditable alongside their output. When build
reproducibility matters (no external tool dependency).
When NOT to use: When the generated output is large AND changes
frequently (diff noise). When the generator is trivial (just use
go generate at build time).
Origin: SSA compiler introduced on dev.ssa branch (2015). The rewrite rules are generated from a DSL but checked in so reviewers can see exactly what changed.
Assembly in Runtime
Location: runtime/*.s (200 files)
// runtime/asm_amd64.s
TEXT runtime·gogo(SB), NOSPLIT, $0-8
MOVQ buf+0(FP), BX // gobuf
MOVQ gobuf_g(BX), DX
...
When to use: Context switches, atomic operations, system calls, and anything that needs direct control over registers/stack. Each architecture has its own set of assembly files.
When NOT to use: Anything that can be written in Go with acceptable performance. The Go team actively converts assembly to Go where possible (e.g., crypto packages moving from asm to Go+intrinsics).
Origin: Runtime has always included assembly — Go's scheduler and GC require direct hardware control that no high-level language can provide.
Proposal Process (Committee Governance)
Location: GitHub issues, not PRs
Issue #71497 (json/v2): 201 comments, Jan 2025
Issue #56986 (GODEBUG): 70 comments, Nov 2022
Issue #56345 (slog): 841 comments, 10 months
Issue #15292 (generics): 874 comments, 5 years
When to use: Any user-visible API addition or behavior change. The proposal process ensures all edge cases are considered before committing to the compatibility promise.
When NOT to use: Internal refactoring, performance improvements that don't change API, bug fixes. These go through normal code review (Gerrit/GitHub).
Origin: The Go proposal process evolved from informal (early Go) to formalized (2015+). Russ Cox and the Go team manage a proposal review meeting that triages and decides.
The Scheduler as Documentation
Location: runtime/proc.go (8,156 lines)
// Goroutine scheduler
// The scheduler's job is to distribute ready-to-run goroutines over
// worker threads.
//
// The main concepts are:
// G - goroutine.
// M - worker thread, or machine.
// P - processor, a resource that is required to execute Go code.
When to use: Complex algorithms should document their model at the top of the file. The Go runtime uses extensive comments to explain the scheduler's invariants, the GC's phases, and the memory allocator's structure.
When NOT to use: Simple code that's self-documenting. Comments that restate what the code does rather than WHY it does it.
Origin: The G-M-P model comment dates to the scheduler rewrite (2014). The convention of architectural documentation at file-top extends throughout the runtime.
json/v2: Experiment-to-Stdlib Pipeline
Location: encoding/json/v2/ (6,387 lines, 13 files)
// encoding/json/v2 — semantic layer (uses reflection)
// encoding/jsontext — syntactic layer (no reflection dependency)
When to use: When building a major stdlib revision. The pattern:
- Build as external module (
go-json-experiment/json) - Iterate with real users for years
- Propose for stdlib with working implementation
- Ship behind GOEXPERIMENT flag
- Graduate when stable
When NOT to use: Small additions that don't need years of iteration. Features that can be backward-compatible additions to existing packages.
Origin: go-json-experiment/json existed since 2022. Proposed as
#71497 (Jan 2025, 201 comments). Landed behind flag Apr 2025 (commit
0e17905793 by Damien Neil).