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
- Standard library first; every dependency needs a reason and approval.
- No package-level mutable state and no
panic()outside boot.- Table-driven tests in the same package, standard
testingonly.
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
anyand 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/cobrafor 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: Useinit()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
- Print changed items with two spaces:
Unit Tests
- Use standard Go table-driven tests (
t.Run). - Avoid external testing libraries, assertions frameworks, or mock generators. Standard library
testingis sufficient. - Put tests in the same package (e.g.
package mainin a test file alongside the main file) to test unexported components easily.