golang
SKILL.md
Go patterns
Basic rules
- Follow the Effective Go guidelines for writing idiomatic Go code.
- Use
gofmtto format your code consistently. - Organize code into packages, each with a clear purpose.
- Use interfaces to define behavior and promote decoupling.
- Handle errors explicitly and avoid using panic for regular error handling.
- Write tests using the
testingpackage and aim for high test coverage. - For simple projects favour a simple structure that mirrors opensource projects, if the project grows or has complexity and lots of business logic consider using a more suitable pattern such as hexagonal (ports and adaptors) architecture
- Avoid complexity, keep functions small and focused on a single task.
- Avoid implementing patterns from other languages that don't fit well with Go's design philosophy, such as factory patterns or heavy use of inheritance.
- Use goroutines and channels for concurrency, but be mindful of potential pitfalls like race conditions.
- Document your code using Go's documentation conventions, including comments for exported functions and types, but dont explain the obvious and over document.
- Use go modules for dependency management.
- Regularly review and refactor code to improve readability and maintainability.
- Leverage the standard library as much as possible before reaching for third-party packages.
- Tag tests with build tags to separate unit, integration, and end-to-end tests where appropriate.
- When writing integration tests, use test containers or mocks to isolate external dependencies.
- when writing e2e tests, ensure they run against a real service, no mocks, by specifying a real endpoint.
- Ensure there is CI/CD that:
- Runs tests, linters and static analysis (golangci-lint) on every commit.
- Builds and validates the project (binaries, libraries, or containers as appropriate).
- For containerized applications: publishes to a container registry and scans images for vulnerabilities before deployment.
Language specific rules
Rule 1: Respect Value Semantics
- All arguments are passed by value.
- Use pointers when mutating data or avoiding large copies.
- If any method mutates a struct, all methods use pointer receivers.
Rule 2: Be Explicit About nil
- Do not return
nilslices or maps from public APIs. - Prefer empty slices (
[]T{}) unlessnilhas meaning. - Never assume
niland empty are interchangeable.
Rule 3: Never Ignore Errors
- Do not discard returned errors.
- Wrap errors using
%w. - Return errors early with contextual messages.
Rule 4: Use defer Correctly
deferexecutes at function return, not block exit.- Never
deferinside loops unless wrapped in a function literal. - Place
deferimmediately after resource acquisition.
Rule 5: Fix Loop Variable Capture
- Note: Go 1.22+ fixed this issue by scoping loop variables per-iteration by default.
- For Go <1.22: Never capture loop variables directly in closures or goroutines.
- For Go <1.22: Always shadow loop variables inside the loop.
Rule 6: Concurrency Requires Ownership
- Do not start goroutines without:
- A clear owner
- A termination condition
- Avoid concurrency unless it provides real benefit.
- Prefer simple, synchronous code by default.
Rule 7: Always Respect context.Context
- If a function accepts
context.Context, it must observe cancellation. - Check
ctx.Done()in loops and before blocking work. - Never store contexts in structs.
Rule 8: Maps Are Not Thread-Safe
- Never read and write maps concurrently without synchronization.
- Use
sync.Mutex,sync.RWMutex, or justifiedsync.Map. - Never rely on map iteration order.
Rule 9: Prefer Go Idioms Over Patterns
- Avoid Java/C++ design patterns.
- Prefer composition over abstraction.
- Accept interfaces, return concrete types.
- Keep interfaces small.
- Use generics (Go 1.18+) for type-safe data structures, but don't over-abstract.
- Avoid using generics when a simple interface or concrete type would suffice.
Rule 10: Optimize for Clarity, Not Cleverness
- Write boring, explicit code.
- Avoid over-engineering.
- Favor readability over brevity.
- Assume the code will be maintained by humans.
Final Constraint (Hard Rule)
If correctness, simplicity, and idiomatic Go conflict with cleverness or abstraction — choose simplicity.