Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Idiomatic Go patterns covering error handling, concurrency, interface design, zero values, and package organization.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
SKILL.md
1---2name: golang-patterns3description: >4Go-specific design patterns and best practices including functional options,5small interfaces, dependency injection, concurrency patterns, error handling,6and package organization. Use when working with Go code to apply idiomatic7Go patterns.8metadata:9origin: ECC10globs: ["**/*.go", "**/go.mod", "**/go.sum"]11---1213# Go Patterns1415> This skill provides comprehensive Go patterns extending common design principles with Go-specific idioms.1617## Functional Options1819Use the functional options pattern for flexible constructor configuration:2021```go22type Option func(*Server)2324func WithPort(port int) Option {25return func(s *Server) { s.port = port }26}2728func NewServer(opts ...Option) *Server {29s := &Server{port: 8080}30for _, opt := range opts {31opt(s)32}33return s34}35```3637**Benefits:**38- Backward compatible API evolution39- Optional parameters with defaults40- Self-documenting configuration4142## Small Interfaces4344Define interfaces where they are used, not where they are implemented.4546**Principle:** Accept interfaces, return structs4748```go49// Good: Small, focused interface defined at point of use50type UserStore interface {51GetUser(id string) (*User, error)52}5354func ProcessUser(store UserStore, id string) error {55user, err := store.GetUser(id)56// ...57}58```5960**Benefits:**61- Easier testing and mocking62- Loose coupling63- Clear dependencies6465## Dependency Injection6667Use constructor functions to inject dependencies:6869```go70func NewUserService(repo UserRepository, logger Logger) *UserService {71return &UserService{72repo: repo,73logger: logger,74}75}76```7778**Pattern:**79- Constructor functions (New* prefix)80- Explicit dependencies as parameters81- Return concrete types82- Validate dependencies in constructor8384## Concurrency Patterns8586### Worker Pool8788```go89func workerPool(jobs <-chan Job, results chan<- Result, workers int) {90var wg sync.WaitGroup91for i := 0; i < workers; i++ {92wg.Add(1)93go func() {94defer wg.Done()95for job := range jobs {96results <- processJob(job)97}98}()99}100wg.Wait()101close(results)102}103```104105### Context Propagation106107Always pass context as first parameter:108109```go110func FetchUser(ctx context.Context, id string) (*User, error) {111// Check context cancellation112select {113case <-ctx.Done():114return nil, ctx.Err()115default:116}117// ... fetch logic118}119```120121## Error Handling122123### Error Wrapping124125```go126if err != nil {127return fmt.Errorf("failed to fetch user %s: %w", id, err)128}129```130131### Custom Errors132133```go134type ValidationError struct {135Field string136Msg string137}138139func (e *ValidationError) Error() string {140return fmt.Sprintf("%s: %s", e.Field, e.Msg)141}142```143144### Sentinel Errors145146```go147var (148ErrNotFound = errors.New("not found")149ErrInvalid = errors.New("invalid input")150)151152// Check with errors.Is153if errors.Is(err, ErrNotFound) {154// handle not found155}156```157158## Package Organization159160### Structure161162```163project/164├── cmd/ # Main applications165│ └── server/166│ └── main.go167├── internal/ # Private application code168│ ├── domain/ # Business logic169│ ├── handler/ # HTTP handlers170│ └── repository/ # Data access171└── pkg/ # Public libraries172```173174### Naming Conventions175176- Package names: lowercase, single word177- Avoid stutter: `user.User` not `user.UserModel`178- Use `internal/` for private code179- Keep `main` package minimal180181## Testing Patterns182183### Table-Driven Tests184185```go186func TestValidate(t *testing.T) {187tests := []struct {188name string189input string190wantErr bool191}{192{"valid", "[email protected]", false},193{"invalid", "not-an-email", true},194}195196for _, tt := range tests {197t.Run(tt.name, func(t *testing.T) {198err := Validate(tt.input)199if (err != nil) != tt.wantErr {200t.Errorf("got error %v, wantErr %v", err, tt.wantErr)201}202})203}204}205```206207### Test Helpers208209```go210func testDB(t *testing.T) *sql.DB {211t.Helper()212db, err := sql.Open("sqlite3", ":memory:")213if err != nil {214t.Fatalf("failed to open test db: %v", err)215}216t.Cleanup(func() { db.Close() })217return db218}219```220221## When to Use This Skill222223- Designing Go APIs and packages224- Implementing concurrent systems225- Structuring Go projects226- Writing idiomatic Go code227- Refactoring Go codebases228