Go Best Practices
Style and best practices guide for Go applications. Contains 60+ rules across 10 categories, synthesized from authoritative sources.
Core skill for: [[Dev]] (implementing Go code) and [[Architect]] (designing Go architectures).
Linting (MANDATORY)
Always run golangci-lint before completing any Go work:
# Run linter on all packages golangci-lint run ./... # Run with auto-fix for safe fixes golangci-lint run --fix ./...
The project uses .golangci.yml which enables:
- •Default linters: errcheck, gosimple, govet, ineffassign, staticcheck, unused
- •Bug detection: bodyclose, nilerr, rowserrcheck, sqlclosecheck, noctx
- •Security: gosec
- •Style: gofumpt, revive, misspell, whitespace
- •Performance: prealloc
For reviews: Run linter first. Any issues found should be tracked as tasks using bd create.
Core Principles
- •Clarity over cleverness - Code is read more than written
- •Simplicity - The best code is no code; the second best is simple code
- •Consistency - Follow established patterns in the codebase
- •Explicit over implicit - Make dependencies and behavior obvious
When to Apply
Reference these guidelines when:
- •Writing new Go packages or functions
- •Implementing error handling
- •Working with concurrency (goroutines, channels)
- •Designing interfaces
- •Writing tests
- •Reviewing Go code for style issues
- •Refactoring existing Go code
Auto-Detection
This skill auto-activates when working with:
- •Files matching
*.go,*_test.go - •Imports from standard library or common Go packages
- •Patterns like
func,type,interface,go func,chan,select - •Error handling patterns like
if err != nil
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Error Handling | CRITICAL | err- |
| 2 | Naming & Style | HIGH | name- |
| 3 | Interfaces | HIGH | iface- |
| 4 | Concurrency | HIGH | conc- |
| 5 | Package Design | MEDIUM-HIGH | pkg- |
| 6 | Functions & Methods | MEDIUM | func- |
| 7 | Testing | MEDIUM | test- |
| 8 | Performance | MEDIUM | perf- |
| 9 | Documentation | LOW-MEDIUM | doc- |
| 10 | Project Structure | LOW | struct- |
Quick Reference
1. Error Handling (CRITICAL)
- •
err-handle- Always handle errors, never use_ - •
err-wrap- Wrap errors with context usingfmt.Errorf("...: %w", err) - •
err-types- Use sentinel errors or custom types for expected errors - •
err-strings- Error strings: lowercase, no punctuation - •
err-flow- Indent error handling, keep happy path unindented - •
err-panic- Don't panic for normal errors, only unrecoverable situations - •
err-inband- Avoid in-band errors; use multiple returns
2. Naming & Style (HIGH)
- •
name-mixedcaps- Use MixedCaps, not underscores - •
name-initialisms- Keep initialisms uppercase:URL,HTTP,ID - •
name-short- Short names for short scopes (i,r,ctx) - •
name-receivers- Receiver names: 1-2 letters, consistent across methods - •
name-getters- NoGetprefix for getters (Name()notGetName()) - •
name-packages- Package names: short, lowercase, singular - •
name-avoid- Avoid util, common, misc, base package names
3. Interfaces (HIGH)
- •
iface-consumer- Define interfaces where used, not where implemented - •
iface-small- Prefer small interfaces (1-3 methods) - •
iface-accept- Accept interfaces, return concrete types - •
iface-verify- Verify interface compliance at compile time - •
iface-no-mock- Don't define interfaces just for mocking
4. Concurrency (HIGH)
- •
conc-goroutine-lifetime- Document when/whether goroutines exit - •
conc-channel-ownership- Establish clear channel ownership - •
conc-prefer-sync- Prefer synchronous functions over async - •
conc-mutex-embed- Embed mutex, don't expose it - •
conc-context-first- Context as first parameter - •
conc-no-context-struct- Don't store context in structs
5. Package Design (MEDIUM-HIGH)
- •
pkg-single-purpose- One package, one purpose - •
pkg-internal- Use internal/ for private packages - •
pkg-avoid-init- Avoid init() when possible - •
pkg-import-blank- Side-effect imports only in main - •
pkg-circular- Avoid circular dependencies
6. Functions & Methods (MEDIUM)
- •
func-receiver-type- Use pointer receiver for mutations, large structs - •
func-named-returns- Use named returns only for documentation - •
func-naked-returns- Avoid naked returns except in tiny functions - •
func-options- Use functional options for complex configuration - •
func-pass-values- Don't pass pointers to small immutable types
7. Testing (MEDIUM)
- •
test-table-driven- Use table-driven tests - •
test-useful-failures- Test failures should say: got X, want Y - •
test-examples- Write testable Example functions - •
test-parallel- Use t.Parallel() for independent tests - •
test-helpers- Use t.Helper() in test helper functions - •
test-subtests- Use t.Run() for subtests
8. Performance (MEDIUM)
- •
perf-preallocate- Preallocate slices when size is known - •
perf-strings-builder- Use strings.Builder for concatenation - •
perf-sync-pool- Use sync.Pool for frequently allocated objects - •
perf-avoid-reflect- Avoid reflection in hot paths - •
perf-benchmark- Benchmark before optimizing
9. Documentation (LOW-MEDIUM)
- •
doc-package- Every package needs a package comment - •
doc-exported- Document all exported names - •
doc-sentences- Comments are full sentences, start with name - •
doc-examples- Include runnable examples in docs
10. Project Structure (LOW)
- •
struct-cmd- Main packages in cmd/ - •
struct-internal- Private code in internal/ - •
struct-file-size- Keep files under 400 lines when practical - •
struct-pkg-flat- Prefer flat package structure over deep nesting
How to Use
Quick lookup: Reference the rule name from the Quick Reference above, then read the detailed rule:
rules/{rule-name}.md
Example: For err-wrap, read rules/err-wrap.md
Each rule file contains:
- •Brief explanation of why it matters
- •Incorrect code example with explanation
- •Correct code example with explanation
- •References to official documentation
Full document: For the complete guide with all rules expanded: AGENTS.md
Integration with Orchestrators
Implementation (auto-dev)
When implementing Go tasks:
- •Check applicable rules before writing code
- •Apply CRITICAL rules (err-*) by default
- •Reference specific rules when reviewing implementations
Architect (Archie)
When designing Go architectures:
- •Use pkg-* and iface-* rules for package design
- •Reference struct-* rules for project layout
- •Include rule citations in ADRs
Key Sources
- •Effective Go - Official Go style guide
- •Go Code Review Comments - Community code review checklist
- •Google Go Style Guide - Google's internal style guide
- •Uber Go Style Guide - Uber's Go conventions
- •Standard Go Project Layout - Community project structure