golang
npx skills add https://github.com/monkescience/skills --skill golang
Agent 安装分布
Skill 文档
Go â Opinionated Coding Standards
Execution Order
When implementing or changing Go code, run this sequence unless project tooling defines a different workflow:
- Detect project commands in this order:
maketargets (ifMakefileexists)magetargets (ifmagefile.goexists)- raw Go commands
- Run checks in this order:
fmt->lint->test->build
Default fallback commands when project tooling is missing:
- Format:
golangci-lint fmt - Lint:
golangci-lint run - Test:
go test ./... - Build:
go build ./...
Project Structure
Choose structure based on project type:
Libraries / small tools â flat structure:
mylib/
mylib.go
mylib_test.go
helpers.go
Services / microservices â standard layout:
cmd/
server/main.go
worker/main.go
internal/
handler/
service/
repository/
model/
go.mod
Rules:
- Use
internal/for private packages. cmd/contains onlymain.goentrypoints. Keep them thin â parse config, wire dependencies, callrun().- Group by domain, not by technical layer, when the service grows beyond a few packages.
- One package = one responsibility. If a package name needs “and” to describe it, split it.
Code Style & Naming
- Format code with
golangci-lint fmt(runsgofumpt+goimportsvia config). - Prefer descriptive names over short names, especially for exported symbols and function parameters.
- Acceptable short names:
ctx,err,t,i,nfor well-established Go idioms. - Bad:
func Process(d []byte, f string)â Good:func Process(data []byte, filename string)
- Acceptable short names:
- Exported names must be self-documenting. Comment every exported type, function, and constant.
- Unexported helpers can be terser but must still be clear in context.
- Avoid stuttering:
user.Useris fine,user.UserServiceis not â useuser.Service. - Acronyms are all-caps:
HTTPClient,ID,URL. NotHttpClient,Id,Url. - Struct tags use
snake_casefor JSON, YAML, XML, and BSON.
Constants â No iota
Never use iota. It is implicit, fragile to reordering, and produces opaque values that are hard to grep, log, and debug. Always use explicit string-typed constants.
// Bad â iota: implicit values, break on reorder, meaningless in logs
const (
assertJSON assertType = iota
assertYAML
assertHTML
assertFile
)
// Good â explicit string constants
const (
assertJSON assertType = "json"
assertYAML assertType = "yaml"
assertHTML assertType = "html"
assertFile assertType = "file"
)
Do not use iota for new code in this skill.
Interfaces
- Keep interfaces small: 1-2 methods. Compose larger interfaces from small ones.
- Accept interfaces, return concrete structs.
- Define interfaces at the consumer side, not the implementation side.
- Name single-method interfaces with the
-ersuffix:Reader,Writer,Closer,Validator.
// Defined where it's used, not where it's implemented
type OrderStore interface {
GetOrder(ctx context.Context, id string) (*Order, error)
}
type OrderService struct {
store OrderStore // depends on interface
}
Error Handling
Use sentinel errors + wrapping with fmt.Errorf and %w.
var (
ErrNotFound = errors.New("not found")
ErrConflict = errors.New("conflict")
ErrForbidden = errors.New("forbidden")
)
func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
user, err := s.repo.Find(ctx, id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
- Always wrap errors with context about what operation failed.
- Never discard errors silently. If intentionally ignoring, assign to
_with a comment. - No
pkg/errorsor third-party error packages. Stdlib is sufficient. - Return errors; do not panic. Reserve
panicfor truly unrecoverable programmer errors. - Error messages are lowercase, no punctuation:
"get user: not found".
Testing
Use stdlib testing + testastic. Test the public API through external _test packages. Use same-package tests only for complex internals that are not meaningfully reachable through the public surface. Use // given: / // when: / // then: comments in every test case. Hand-roll mocks, no additional mocking frameworks.
Choose the right testing approach based on project type:
- Libraries / packages â unit tests. Test the public API through
_testpackage suffix. See references/unit-testing.md. - Services / microservices â integration tests. Test components working together (handlers, DB, external APIs). See references/integration-testing.md.
Linting
Use golangci-lint v2. Run golangci-lint run for linting and golangci-lint fmt for formatting.
For the full reference config, enabled/disabled linter rationale, and rules, see references/linting.md.
Key rules:
- All code must pass
golangci-lint runbefore committing. - No
//nolintwithout a comment explaining why. - Test files have relaxed rules (funlen, dupl, mnd, wrapcheck, err113, gocognit, gosec excluded).
Context
ctx context.Contextis always the first parameter. No exceptions.- Never store
context.Contextin a struct. Pass it through function calls. - Use
context.WithTimeoutorcontext.WithCancelto scope work. - Respect context cancellation: check
ctx.Err()orselectonctx.Done()in long operations.
Concurrency
- Do not start goroutines without a clear ownership and shutdown strategy.
- Use
errgroup.Groupfor concurrent operations that can fail. - Channels for communication, mutexes for state protection. Do not mix unnecessarily.
- Always handle goroutine lifecycle:
context.Contextfor cancellation,sync.WaitGrouporerrgroupfor joining. - Prefer
sync.Onceoverinit()for lazy initialization. - Never fire-and-forget goroutines in production code.
- Buffer channels only with a clear reason and known capacity.
g, ctx := errgroup.WithContext(ctx)
for _, item := range items {
g.Go(func() error {
return process(ctx, item)
})
}
if err := g.Wait(); err != nil {
return fmt.Errorf("process items: %w", err)
}
Dependency Injection
- Constructor-based injection. No global mutable state. No DI frameworks.
- Constructors return concrete types, accept interfaces.
- Wire dependencies in
main()or a dedicatedrun()function incmd/.
func NewOrderService(store OrderStore) *OrderService {
return &OrderService{store: store}
}
Options Pattern
Use the functional options pattern for configurable constructors or functions with optional parameters. Do not use config structs with zero-value ambiguity.
// Option type is an unexported function that modifies config.
type Option func(*serverConfig)
// serverConfig holds all configurable fields with sensible defaults.
type serverConfig struct {
port int
readTimeout time.Duration
writeTimeout time.Duration
}
// Exported option constructors â one per configurable field.
func WithPort(port int) Option {
return func(c *serverConfig) {
c.port = port
}
}
func WithReadTimeout(timeout time.Duration) Option {
return func(c *serverConfig) {
c.readTimeout = timeout
}
}
// Constructor applies defaults, then options.
func NewServer(opts ...Option) *Server {
cfg := serverConfig{
port: 8080,
readTimeout: 5 * time.Second,
writeTimeout: 10 * time.Second,
}
for _, opt := range opts {
opt(&cfg)
}
return &Server{cfg: cfg}
}
// Usage: clean, readable, self-documenting.
srv := NewServer(
WithPort(9090),
WithReadTimeout(10 * time.Second),
)
Rules:
- Config struct is unexported. Options are the public API.
- Set sensible defaults in the constructor, not in each option.
- Each option constructor is a
With*function returningOption. - Option constructors return the
Optiontype (not the concrete closure) â this is a valid exception to “return concrete types”. - Use this pattern when a function has more than 2-3 optional parameters. Do not use it for required parameters â those stay as regular arguments.
Logging
Use log/slog with context-aware logging. Configure the default logger once in main() via slog.SetDefault(). Always log with context using slog.InfoContext(ctx, ...), slog.ErrorContext(ctx, ...), etc. Do not pass *slog.Logger as a parameter â ctx already carries correlation IDs, trace IDs, and other request-scoped data.
For setup patterns, handler configuration, and context attribute extraction, see references/logging.md.
No init(), No Globals
- No
init()functions. They make testing hard and hide side effects. - No mutable global state. Pass dependencies explicitly.
- Package-level
varis acceptable only for sentinel errors, constants, andsync.Once.
Dependency Management
- Use Go modules (
go.mod). Rungo mod tidybefore committing. - Pin direct dependency versions. Do not use
latest. - Review
go.sumchanges in PRs. - Avoid vendoring unless required by build environment constraints.
- Minimize external dependencies. Prefer stdlib when reasonable.
Ask Before Proceeding
Ask the user before:
- Adding a new third-party dependency.
- Introducing or switching framework-level choices (router, ORM, logging stack, queue client).
- Using same-package tests for internals as the primary strategy in a package.
- Choosing integration test runtime strategy (containers, docker-compose, external shared services).
Done Criteria
A Go change is done when all are true:
- Formatting passes (
golangci-lint fmtor project equivalent). - Lint passes (
golangci-lint runor project equivalent). - Relevant tests pass (
go test ./...or project equivalent). - Build passes (
go build ./...or project equivalent for changed binaries/packages). - No unresolved placeholders, partial implementations, or undocumented exceptions to rules.