10x Go
This skill defines rules to write robust, maintainable, and idiomatic production Go code.
Reference Guide
Load the relevant reference when the task involves:
| Topic | File | Load When |
|---|---|---|
| Concurrency | references/concurrency.md |
goroutines, channels, context, sync primitives, worker pools |
| Generics | references/generics.md |
type parameters, constraints, generic data structures |
| Interface Design | references/interfaces.md |
interface composition, functional options, io patterns, DI |
| Testing | references/testing.md |
tests, benchmarks, fuzzing, mocking, coverage |
| Project Structure | references/project-structure.md |
module layout, go.mod, Makefile, Dockerfile, monorepo |
| Error Handling | references/errors.md |
sentinel errors, wrapping, custom types, GORM context |
| API Projects | references/api.md |
gin, GORM, JWT, swagger, CORS |
| OpenAPI / Swagger | references/openapi.md |
swaggo annotations, spec generation, Swagger UI, security schemes |
| CLI Projects | references/cli.md |
cobra, CLI directory layout |
| REST Patterns | references/rest-patterns.md |
URI patterns, HTTP status code, naming conventions |
| Memory & Resources | references/memory.md |
request/response body lifecycle, goroutine limits, heap escape, sync.Pool |
Architecture Principles
- Favor simplicity. Do not over-engineer the design.
- Depend on interfaces, not concrete types (DI). Prefer small, single-method interfaces (ISP). Each function/type has one responsibility (SRP).
- Favor generic functions over specific ones (
hasRole(string)instead ofhasAdminRole()andhasWriterRole()). - ALWAYS make small, atomic, incremental changes rather than big-bang rewrites.
- Introduce interfaces when needed to enable loose coupling.
MUST DO
- Run
gofmtandgolangci-linton all generated code - Run
go vet ./...on all generated code (catches common correctness issues gofmt misses) - Pass
context.Contextas the first argument to all blocking or I/O-bound functions - Handle all errors explicitly — no naked
_discards without justification - Wrap errors with
fmt.Errorf("operationName: %w", err)— seereferences/errors.md - Write table-driven tests with
t.Runsubtests for all non-trivial functions - Document all exported functions, types, and constants with a docstring
- Run tests with
-raceflag:go test -race ./... - Always apply
http.MaxBytesReader, drain, and closer.Bodyin HTTP handlers - Always limit, drain, and close
resp.Bodyin HTTP clients — useio.LimitedReader{R: resp.Body, N: limit+1}and checklimited.N == 0to detect (and error on) overflow; never use bareio.ReadAll(resp.Body) - Always close
resp.Bodyonclient.Do()error: ifresp != nil { resp.Body.Close() }before returning - Compile regular expressions once at package level (
var re = regexp.MustCompile(...)) — never inside functions called per request - Cap goroutine counts with
errgroup.SetLimitorsemaphore.NewWeighted— never spawn unbounded goroutines over user-supplied input - Pre-allocate slices and maps when final size is known:
make([]T, 0, n) - Use
errors.Is()anderrors.As()for error inspection - Use
anyinstead ofinterface{} - Use type switches instead of repeated type assertions
MUST NOT DO
- Use
panicfor recoverable errors - Use
http.Get,http.Post, orhttp.DefaultClientin production code — always create a dedicated*http.Clientwith an explicitTimeout - Use
io.LimitReaderwhen you need truncation detection — useio.LimitedReader{N: limit+1}and checkN==0instead;io.LimitReadersilently truncates - Create goroutines without a clear termination strategy (WaitGroup, errgroup, or channel signaling)
- Ignore context cancellation in long-running operations
- Hardcode configuration values — use environment variables or functional options
- Use reflection without measurable performance justification
- Return errors without wrapping context (
return erralone loses the call site) - Log AND return the same error at the same level — choose one
- Box value types into
any/interface{}on hot paths without profiling justification - Use
fmt.Sprintffor string building in loops — usestrings.Builderinstead - Store pointers to pooled objects outside the
sync.Poolscope
Coding Style
- Use PascalCase for exported types/methods, camelCase for variables
- Group imports: standard library, then third-party, then project-specific
- Package names and all exported entities must have docstrings
- Code must be self-documenting with clear, consistent naming
- Avoid nested logic — follow the "happy path" principle
Quality Standards
- Minimum Go version: 1.22+
- Functions must be small, focused, and easily testable.
- Dependencies must be minimal and well-justified.
- Performance optimizations must be measured, not assumed. Profile with pprof before optimizing.
- Log at Debug level by default; log at Info level for one-time or important events (initialization, configuration).
- Never log secrets, tokens, or PII — scrub before logging.
- Use parameterized GORM queries; never concatenate user input into raw SQL.
- Run
govulncheck ./...in CI to detect known vulnerabilities in dependencies.
Module Preferences
go.uber.org/zapfor structured logginggithub.com/stretchr/testifyand its submodules (require,assert,suite) for testing
Tests
Tests are contracts with the user. See references/testing.md for full guidance. Key rules are in MUST DO above.
Agent Behavior
- Reduce redundancy — use tree-sitter (if available) to identify similar code patterns before generating new code.
- Use tree-sitter (if available) to analyze function complexity before refactoring.
- Preserve test intent: you may refactor test structure and helpers freely, but you MUST ASK for confirmation before changing test assertions, removing test cases, or altering expected behavior.
- When adding new test cases: add with a
// TODO: uncomment and validatecomment and notify the user.