| name | gopher |
| description | Portable Go style and testing habits: interfaces, errors, slog, tests, TDD mindset. Pair with each project's AGENTS.md (or equivalent) for versions, layout, commands, and tooling.
|
Gopher — Go coding and testing (generic)
Use with project docs. This skill is not a substitute for the repository’s own rules. Always read the nearest AGENTS.md (or your project’s equivalent: CONTRIBUTING.md, team wiki, Cursor rules) for:
- Go / toolchain versions and how they are pinned
- Repo layout, module boundaries, and where application code lives
- How to run lint and tests, and what “done” means before you report back
- Codegen (OpenAPI/protobuf/sqlc/etc.), mock generation, and migration commands
- Security and compliance expectations for this codebase
- Framework-specific rules (ORM, HTTP routers, DI, specialized SDKs, etc.)
Nearest project instructions win when they conflict with anything below.
Go coding guide (language-level)
Key considerations
- Accept interfaces, return structs canonical way to structure code:
- Dependencies (services, repos, clients or any other components) SHELL be accepted as consumer defined interfaces (testability, boundaries).
- Return concrete types (structs) from constructors.
- Define interfaces next to the consumer (same file by default); split only when size or reuse demands it.
- Use
log/slog (or the project’s logging facade) as an injected dependency — avoid global loggers unless the project already standardized on them.
- Wrap errors:
fmt.Errorf("context: %w", err) (or the project’s error helpers).
- Methods with more than 3 arguments (context does not count) is a warning sign. Use params struct instead.
- Names such as "tools", "helpers", "utils" e.t.c are banned. Use descriptive names instead.
- Prefer functional options for optional constructor parameters: a
type FooOption func(*Foo) (or *fooConfig), WithBar(...) functions that set fields, and NewFoo(opts ...FooOption) applying them in order. Avoid a separate NewFooWithOpts when the zero-arg NewFoo() case is the default.
Testing style and patterns (from the coding guide)
More detail is in Testing best practices below. Common points:
- Use random data in tests. Use faker (
github.com/jaswdr/faker/v2) for variable test data; think twice before using fixed literals, this is a warning sign. If you think fixed literal is fine in some cases, you're wrong unless you prove it strongly.
- Tests in the same package as production code (or
_test package if the project prefers that for black-box tests).
- Use one top-level test function per unit with nested
t.Run for methods and scenarios.
- Avoid static/shared state across tests.
- Use a single, consistent way to build test dependencies (e.g. a shared
makeMockDeps or fixture constructor if the project uses that pattern); avoid copy-pasted setup.
- Use
TestComponentName and nested t.Run per API surface, avoid separate TestComponentNameFunctionName functions per method.
- Keep helpers local: if a helper is only used in one test, nest it inside that test.
- Compare whole values (
expected vs actual struct)
- Use
require.Error / require.ErrorIs (or equivalents) for error assertions.
- Mocks: follow the project’s documented approach (codegen tool, hand-written fakes, etc.).
- Use
t.Context() over context.Background() / context.TODO() in tests when the Go version supports it.
- Avoid package-level test helpers; define them inside the relevant
TestXxx (e.g. as a closure used by nested t.Run blocks) or inside a single t.Run when only that case needs them.
Testing best practices (embedded, mindset-level)
Core Principles
Follow TDD
- Work in small steps; stub if needed, then add a failing test, then minimal code to pass; repeat.
- If the project documents a TDD workflow or other strategy, follow that document.
Testing philosophy
- Focus on business logic — what the code must guarantee.
- Prefer single test for single code branch
- Avoid excessive tests — skip scenarios that do not match real use cases.
- Avoid splitting one behavior across many tests — one behavior, one clear story when possible.
- Test behavior, not implementation — observable outcomes over internal structure.
- Avoid testing framework internals or standard library functions.
- Test only logic that exists in the component if mocked dependencies are used
If project documents specific mocking strategy, follow it. If you have to create mock manually, follow these principles:
- Pragmatic mocks — simplest thing that isolates the unit under test.
- Minimal setup — only what the case needs.
- Clear names — names should say what behavior is under test.