--- name: golang-rules description: "Go coding rules: style, patterns, security, testing. Triggers: .go, go.mod, go.sum, Gin, Echo, Gorilla, testing, gofmt." effort: medium user-invocable: false allowed-tools: Read --- # Go Rules These rules come from `app/rules/golang/` in ai-toolkit. They cover the project's standards for coding style, frameworks, patterns, security, and testing in Go. Apply them when writing or reviewing Go code. # Go Coding Style ## Naming - MixedCaps/mixedCaps only. No underscores in Go names (except test functions). - Exported: `PascalCase`. Unexported: `camelCase`. Acronyms: `HTTPClient`, `userID`. - Short variable names in small scopes: `i`, `r`, `w`, `ctx`, `err`. - Descriptive names in larger scopes: `userRepository`, `requestTimeout`. - Package names: short, lowercase, singular (`auth`, `user`, not `utils`, `helpers`). ## Packages - One package per directory. Package name = directory name. - Avoid `util`, `common`, `helpers` packages. Name by what it provides. - Keep package APIs small. Export only what consumers need. - Use `internal/` directory for packages not meant for external consumption. ## Functions - Accept interfaces, return structs. - First parameter `ctx context.Context` if the function does I/O or may be cancelled. - Return `(result, error)` tuple. Error is always last return value. - Use named return values only for documentation, not for naked returns. - Keep functions short. If >40 lines, consider splitting. ## Error Handling - Always check errors. Never use `_` to discard errors silently. - Wrap errors with context: `fmt.Errorf("fetching user %s: %w", id, err)`. - Use sentinel errors (`var ErrNotFound = errors.New(...)`) for expected conditions. - Use `errors.Is()` and `errors.As()` for error checking, not type assertions. ## Formatting - Use `gofmt` / `goimports`. No formatting debates in Go. - Use `golangci-lint` with a `.golangci.yml` config in CI. - Use `go vet` as minimum static analysis. ## Struct Design - Use struct embedding for composition, not inheritance. - Prefer value receivers for small structs, pointer receivers for large or mutable. - Be consistent: all methods on a type use the same receiver type. - Use struct literals with field names: `User{Name: "Ada", Age: 30}`. ## Concurrency - Do not start goroutines without a plan to stop them. - Use `sync.WaitGroup` or `errgroup.Group` to coordinate goroutines. - Use channels for communication, mutexes for state protection. - Prefer `context.Context` for cancellation and timeouts over manual signaling. # Go Frameworks ## Standard Library HTTP - Use `http.NewServeMux()` (Go 1.22+ with method patterns) for simple APIs. - Use `http.HandlerFunc` for handlers. Compose with middleware pattern. - Use `context.Context` from `r.Context()` in all handlers. - Use `http.TimeoutHandler` to prevent slow handlers from hanging. ## Chi / Gorilla Mux - Use Chi for routing with middleware chains and URL params. - Use `chi.URLParam(r, "id")` to extract path parameters. - Use middleware groups: `r.Group(func(r chi.Router) { r.Use(authMiddleware) })`. - Prefer Chi over Gorilla Mux (Gorilla was archived, Chi actively maintained). ## Gin / Echo - Use Gin for high-performance APIs with built-in validation. - Use binding tags: `binding:"required,email"` on struct fields. - Use middleware for cross-cutting: logging, recovery, CORS, auth. - Use `c.ShouldBindJSON()` over `c.BindJSON()` to handle errors yourself. ## GORM / sqlx / pgx - Use `sqlx` for SQL-first with struct scanning (lightweight). - Use `pgx` directly for PostgreSQL-specific features and performance. - Use GORM only when rapid prototyping outweighs SQL control. - Always use prepared statements or parameterized queries. - Use `sqlx.In()` for dynamic IN clauses safely. ## gRPC - Define services in `.proto` files. Generate Go code with `protoc`. - Use interceptors for auth, logging, and tracing (equivalent to middleware). - Use deadlines (context timeout) on every RPC call. - Use streaming RPCs for real-time data, unary for request-response. ## Configuration - Use `envconfig` or `viper` for configuration from env/files. - Use struct tags for env mapping: `envconfig:"DATABASE_URL"`. - Validate config at startup. Fail fast on invalid configuration. - Use `flag` package for CLI arguments in tools and utilities. ## Observability - Use `slog` (Go 1.21+) for structured logging. Replace `log` package. - Use OpenTelemetry for distributed tracing and metrics. - Export metrics via Prometheus endpoint. - Use `pprof` for CPU and memory profiling in development. ## Project Layout - Follow Standard Go Project Layout: `cmd/`, `internal/`, `pkg/`. - Entry points in `cmd/appname/main.go`. - Business logic in `internal/`. Shared libraries in `pkg/`. - Use `Makefile` for common tasks: build, test, lint, run. # Go Patterns ## Error Handling - Wrap errors with context at each call site: `fmt.Errorf("loading config: %w", err)`. - Define domain error types with `errors.New()` or custom error structs. - Use `errors.Is()` for sentinel errors, `errors.As()` for typed errors. - Return errors, do not panic. Reserve `panic` for truly unrecoverable states. - Handle errors immediately after the call. No deferred error checking. ## Concurrency - Use `errgroup.Group` for concurrent operations that may fail. - Use `sync.Once` for one-time initialization (singleton pattern). - Use `sync.Map` only for append-mostly maps with concurrent access. - Use buffered channels as semaphores: `sem := make(chan struct{}, maxConcurrency)`. - Prefer `context.WithTimeout` over manual timers for deadline management. ## Interface Design - Keep interfaces small: 1-3 methods. Compose larger interfaces from smaller ones. - Define interfaces where they are consumed, not where they are implemented. - Use `io.Reader`, `io.Writer`, `fmt.Stringer` and standard interfaces where applicable. - Avoid returning interfaces from functions. Return concrete types. ## Options Pattern - Use functional options for constructors with many optional parameters. - Pattern: `func WithTimeout(d time.Duration) Option { return func(c *Client) { c.timeout = d } }`. - Provide sensible defaults. Options override defaults. - Use `Option` type alias: `type Option func(*Config)`. ## Dependency Injection - Pass dependencies through constructor functions, not global variables. - Accept interfaces in constructors: `func NewService(repo UserRepo) *Service`. - Use `wire` or manual wiring in `main()` for dependency graph. - Avoid init() functions for anything other than simple registration. ## Resource Management - Use `defer` for cleanup immediately after acquiring a resource. - Use `context.Context` for cancellation propagation across goroutines. - Close channels from the sender side, never the receiver. - Use `sync.Pool` for frequently allocated temporary objects (buffers). ## Anti-Patterns - Global mutable state: use dependency injection instead. - `interface{}` / `any` everywhere: use generics (Go 1.18+) or specific types. - Goroutine leaks: always ensure goroutines can exit. - Ignoring `context.Context`: propagate it through all I/O paths. - Large interfaces: split into focused, composable pieces. # Go Security ## Input Validation - Validate all input at API boundaries. Use struct tags or manual validation. - Use `validator` package for struct validation: `validate:"required,email"`. - Parse and validate numeric IDs: `strconv.Atoi()` with error checking. - Limit request body size: `http.MaxBytesReader(w, r.Body, maxBytes)`. ## SQL Injection - Always use parameterized queries: `db.Query("SELECT * FROM users WHERE id = $1", id)`. - Never concatenate user input into SQL strings. - Use `sqlx.In()` for safe dynamic IN clauses. - Use ORM query builders (GORM, Ent) for dynamic query construction. ## Command Injection - Use `exec.Command("binary", args...)` with separate arguments, not shell strings. - Never use `exec.Command("sh", "-c", userInput)`. - Validate and sanitize file paths against traversal attacks. - Use `filepath.Clean()` and verify paths are within allowed directories. ## Cryptography - Use `crypto/rand` for random values, never `math/rand` for security. - Use `bcrypt` or `argon2` for password hashing: `golang.org/x/crypto/bcrypt`. - Use `crypto/subtle.ConstantTimeCompare()` for timing-safe comparisons. - Use `crypto/tls` with `tls.Config{MinVersion: tls.VersionTLS12}`. ## Secrets - Load secrets from environment variables: `os.Getenv("SECRET_KEY")`. - Never hardcode secrets, tokens, or API keys in source code. - Use `go-envconfig` or similar for validated env var loading. - Use Go build tags or ldflags for build-time configuration. ## HTTP Security - Set `ReadTimeout`, `WriteTimeout`, `IdleTimeout` on `http.Server`. - Use `helmet`-equivalent headers: HSTS, X-Content-Type-Options, X-Frame-Options. - Implement rate limiting with `golang.org/x/time/rate` or middleware. - Use `net/http` with TLS. Never serve production HTTP without encryption. ## Concurrency Safety - Use `sync.Mutex` or `sync.RWMutex` for shared mutable state. - Run `go test -race` in CI to detect data races. - Avoid shared state where possible. Prefer channels for communication. - Use `atomic` package for simple counters and flags. ## Dependencies - Run `govulncheck ./...` in CI to check for known vulnerabilities. - Use `go mod tidy` to remove unused dependencies. - Pin dependencies via `go.sum`. Review dependency changes in PRs. - Audit transitive dependencies. Use `go mod graph` to inspect the tree. ## Error Information Disclosure - Never expose internal error messages to clients. - Log detailed errors server-side, return generic messages to clients. - Use error codes for machine-readable error classification. - Do not include stack traces in production API responses. # Go Testing ## Framework - Use the standard `testing` package. No external test frameworks required. - Use `testify/assert` and `testify/require` for readable assertions. - Use `testify/mock` or `mockgen` for generating mocks. - Use `go test -race` in CI to detect data races. ## File Naming - Test files: `*_test.go` in the same package. - Black-box tests: use `package foo_test` to test only exported API. - White-box tests: use `package foo` to test internals. - Test helpers: `testutil_test.go` or `testdata/` directory. ## Table-Driven Tests - Use table-driven tests for functions with multiple input/output cases. - Name each case: `{name: "empty input returns error", input: "", wantErr: true}`. - Use `t.Run(tc.name, func(t *testing.T) { ... })` for subtests. - Use `t.Parallel()` in subtests when tests are independent. ## Test Helpers - Use `t.Helper()` in helper functions for correct line reporting. - Use `t.Cleanup()` for teardown instead of defer in test functions. - Use `testing.TB` interface to share helpers between tests and benchmarks. - Use `testdata/` directory for test fixtures (excluded from build). ## Mocking - Define interfaces at the consumer, not the provider. - Use `mockgen` to auto-generate mocks from interfaces. - Use `httptest.NewServer()` for HTTP integration tests. - Use `httptest.NewRecorder()` for handler unit tests. ## Integration Tests - Use build tags: `//go:build integration` to separate from unit tests. - Use `testcontainers-go` for database/service containers in tests. - Use `t.Setenv()` (Go 1.17+) for environment variable testing. ## Benchmarks - Use `func BenchmarkXxx(b *testing.B)` with `b.N` loop. - Use `b.ResetTimer()` after expensive setup. - Use `b.ReportAllocs()` to track allocations. - Run: `go test -bench=. -benchmem`. ## Coverage - Run: `go test -coverprofile=coverage.out ./...`. - View: `go tool cover -html=coverage.out`. - Set minimum coverage threshold in CI. - Focus coverage on business logic, not generated code.