Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go — House Rules

Who this is for — anyone writing or reviewing Go in these repositories. Reference material: grep it, don’t read it.

Read this if — you are about to add a dependency, a global, or a test framework.
Skip this if — you want the reasoning; it is in How to Read the House Rules.

Takeaways

  1. Standard library first; every dependency needs a reason and approval.
  2. No package-level mutable state and no panic() outside boot.
  3. Table-driven tests in the same package, standard testing only.

Go is a language designed for simplicity, readability, and speed. To maintain these qualities in agent-assisted development, we adhere to the following subset and conventions.


Language & Dependencies

  • Modern Go: Leverage current features like any and generics, but only where they explicitly reduce boilerplate/noise.
  • Minimal Dependencies: Default to the Go Standard Library. If you need a library, get user approval first.
  • Permitted CLI Library: Use github.com/spf13/cobra for CLI-based tools.
  • No Heavy Frameworks: No ORMs (write raw SQL), no logging frameworks (use standard log), no Dependency Injection (DI) containers (pass dependencies explicitly).

State Management

  • No package-level mutable variables: Pass state explicitly via function arguments or state structs (e.g. type App struct { client *http.Client }). Mutable global state creates hidden coupling and breaks tests.
  • Safe init() usage: Use init() only for static, side-effect-free registrations. Never connect to databases, services, or load files during initialization.

Spec-Driven Development

  • Avoid hard-coding application configuration, screen texts, labels, or layout variables.
  • Define them in YAML specs under spec/ (e.g., spec/strings.yaml, spec/layout.yaml) and embed them in the binary using //go:embed.
  • Write compiler/unit test assertions to verify that your Go structs match the specs.
  • The pattern in practice: Week 2 of the Emojig Chronology moved a TUI’s entire configuration out of code, and The Golden Path walks the same move — and its first bug — in this repository’s Go compiler.

Error Handling

  • Wrap errors with context: Use fmt.Errorf("module: %w", err) to preserve the original error while giving readable context.
  • No panic: Never use panic() except for truly unrecoverable boot-time configurations.
  • Return errors: Let errors bubble up to main() where they are printed and exited.

Output and Formatting

  • Functions should return values, not print them directly (unless they are explicitly printer helpers).
  • Indent output messages for visual hierarchy:
    • Print changed items with two spaces: wrote README.md
    • Print sub-details with four spaces: size: 1.2 KB

Unit Tests

  • Use standard Go table-driven tests (t.Run).
  • Avoid external testing libraries, assertions frameworks, or mock generators. Standard library testing is sufficient.
  • Put tests in the same package (e.g. package main in a test file alongside the main file) to test unexported components easily.