agentsclimarketplace

Go

Skill aj-geddes/unicorn-team/skills/go

Guides Go development with idiomatic patterns, tooling, and project structure. ALWAYS trigger on "go project", "go module", "go mod", "goroutine", "channel", "go test", "go build", "golangci-lint", "go interface", "go error handling", "go concurrency", "go struct", "go anti-pattern", "go best practices", "go tooling", "go lint". Use when setting up Go projects, writing idiomatic Go, choosing concurrency patterns, or configuring tooling. Different from testing skill which covers general test strategy; this covers Go-specific testing patterns and tooling configs.From its SKILL.md

Install
npx -y skills add aj-geddes/unicorn-team --skill go

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.5 KB, as published. Nobody here has run it

<!-- Last reviewed: 2026-03 -->

Go Domain Skill

Error Handling

Go uses explicit error returns, not exceptions. Every error is a value you handle at the call site.

// Return errors, don't panic
func ParseConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("reading config %s: %w", path, err)
    }

    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, fmt.Errorf("parsing config: %w", err)
    }
    return &cfg, nil
}

// Sentinel errors for callers to check
var (
    ErrNotFound   = errors.New("not found")
    ErrForbidden  = errors.New("forbidden")
)

// Custom error types for rich context
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: %s: %s", e.Field, e.Message)
}

// Callers use errors.Is / errors.As
if errors.Is(err, ErrNotFound) { /* handle */ }

var ve *ValidationError
if errors.As(err, &ve) { /* access ve.Field */ }

See references/error-handling.md for wrapping strategies, panic/recover, error handling in goroutines, domain error design.

Interfaces

Go interfaces are satisfied implicitly -- no implements keyword. Define them where they're consumed, not where they're implemented.

// Small interfaces at the consumer site
type UserStore interface {
    GetUser(ctx context.Context, id string) (*User, error)
}

// Accept interfaces, return structs
func NewUserService(store UserStore) *UserService {
    return &UserService{store: store}
}

// Common stdlib interfaces to know
// io.Reader, io.Writer      -- streaming data
// fmt.Stringer               -- string representation
// sort.Interface             -- custom sorting
// encoding.BinaryMarshaler  -- serialization
// context.Context            -- cancellation and deadlines
// http.Handler               -- HTTP request handling
GuidelineWhy
1-3 method interfacesEasier to implement, compose, mock
Define at consumerDecouples packages, avoids import cycles
Accept interface, return structCallers get flexibility, producers stay concrete
Embed for compositionio.ReadWriter = io.Reader + io.Writer

Concurrency

// Goroutines + channels for concurrent work
func FetchAll(ctx context.Context, urls []string) ([]Result, error) {
    g, ctx := errgroup.WithContext(ctx)
    results := make([]Result, len(urls))

    for i, url := range urls {
        g.Go(func() error {
            res, err := fetch(ctx, url)
            if err != nil {
                return err
            }
            results[i] = res  // safe: each goroutine owns its index
            return nil
        })
    }

    if err := g.Wait(); err != nil {
        return nil, err
    }
    return results, nil
}

// Context for cancellation and timeouts
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Select for multiplexing channels
select {
case msg := <-msgCh:
    handle(msg)
case <-ctx.Done():
    return ctx.Err()
}

See references/concurrency-patterns.md for worker pools, fan-out/fan-in, pipelines, sync primitives, context propagation.

Testing

// Table-driven tests -- the Go standard
func TestParseSize(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    int64
        wantErr bool
    }{
        {name: "bytes", input: "100B", want: 100},
        {name: "kilobytes", input: "2KB", want: 2048},
        {name: "empty", input: "", wantErr: true},
        {name: "invalid", input: "abc", wantErr: true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ParseSize(tt.input)
            if tt.wantErr {
                if err == nil {
                    t.Fatal("expected error, got nil")
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if got != tt.want {
                t.Errorf("ParseSize(%q) = %d, want %d", tt.input, got, tt.want)
            }
        })
    }
}

// HTTP handler testing
func TestHealthHandler(t *testing.T) {
    req := httptest.NewRequest(http.MethodGet, "/health", nil)
    w := httptest.NewRecorder()

    HealthHandler(w, req)

    if w.Code != http.StatusOK {
        t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
    }
}

Coverage target: 80%+

go test -cover -coverprofile=coverage.out ./...
go tool cover -func=coverage.out       # summary
go tool cover -html=coverage.out       # visual report

See references/testing-go.md for subtests, benchmarks, fuzz testing, testify, httptest patterns, test helpers.

Tooling

Go Modules

go mod init github.com/org/project   # initialize
go mod tidy                          # sync deps
go mod vendor                        # vendored deps (optional)
go get github.com/pkg/[email protected]  # add/update dep

golangci-lint (Linting)

Runs 50+ linters in parallel. Single tool replaces go vet, staticcheck, errcheck, gosec, and more.

# .golangci.yml
linters:
  enable:
    - errcheck       # unchecked errors
    - govet          # suspicious constructs
    - staticcheck    # advanced analysis
    - unused         # unused code
    - gosimple       # simplifications
    - ineffassign    # ineffective assignments
    - gocritic       # opinionated checks
    - gosec          # security issues
    - errname        # error naming conventions
    - exhaustive     # enum exhaustiveness

linters-settings:
  govet:
    enable-all: true
  gocritic:
    enabled-tags: [diagnostic, style, performance]

issues:
  exclude-use-default: false
  max-issues-per-linter: 0
  max-same-issues: 0
golangci-lint run ./...

See references/tooling-config.md for complete linter configs, Makefile patterns, CI setup, build tags.

Structs and Methods

// Struct with tags for serialization
type User struct {
    ID        string    `json:"id" db:"id"`
    Name      string    `json:"name" db:"name"`
    Email     string    `json:"email" db:"email"`
    CreatedAt time.Time `json:"created_at" db:"created_at"`
}

// Constructor function (not a Go "constructor" -- just convention)
func NewUser(name, email string) *User {
    return &User{
        ID:        uuid.NewString(),
        Name:      name,
        Email:     email,
        CreatedAt: time.Now(),
    }
}

// Value receiver: doesn't modify receiver, safe on copies
func (u User) DisplayName() string {
    return fmt.Sprintf("%s <%s>", u.Name, u.Email)
}

// Pointer receiver: modifies receiver or is large
func (u *User) SetEmail(email string) error {
    if !strings.Contains(email, "@") {
        return &ValidationError{Field: "email", Message: "invalid format"}
    }
    u.Email = email
    return nil
}
UseReceiverWhy
Read-only, small structValue (u User)No mutation, safe copy
Mutates statePointer (u *User)Changes visible to caller
Large struct (>3 fields)Pointer (u *User)Avoid copy overhead
Implements interface with pointer methodsPointer (u *User)Consistency required

Project Structure

myproject/
├── cmd/
│   └── myapp/
│       └── main.go          # entry point, wiring only
├── internal/                 # private to this module
│   ├── server/               # HTTP server setup
│   ├── user/                 # user domain logic
│   └── storage/              # database layer
├── pkg/                      # importable by other projects (use sparingly)
├── go.mod
├── go.sum
├── Makefile
└── .golangci.yml

See references/project-structure.md for multi-binary repos, dependency injection, config patterns, build tags.

Anti-patterns Quick Reference

Anti-patternFix
panic() for expected errorsReturn error
Ignoring errors _ = f()Handle or explicitly document why safe
interface{} / any everywhereUse generics (1.18+) or specific types
Goroutine leak (no exit path)Use context.Context + select
Shared state without syncsync.Mutex, channels, or atomic
init() with side effectsExplicit initialization in main()
Giant interfaces (>5 methods)Split into focused 1-3 method interfaces
Package-level mutable stateDependency injection
Premature channel/goroutine useStart sequential, add concurrency when needed
log.Fatal in library codeReturn errors, let caller decide
Naked returns in long functionsNamed returns only for short functions or godoc
Missing defer for cleanupdefer f.Close() immediately after open

Naming Conventions

ElementConventionExample
Packageshort, lowercase, singularuser, http, json
Exported functionPascalCase, verb-nounParseConfig, NewServer
Unexported functioncamelCasevalidateInput, buildQuery
Interface (1 method)Method + "er"Reader, Stringer, Handler
Interface (multi)Descriptive nounUserStore, EventBus
Error variableErr + descriptionErrNotFound, ErrTimeout
Error typeDescription + ErrorValidationError, TimeoutError
ConstantsPascalCase (exported) or camelCaseMaxRetries, defaultTimeout
AcronymsAll capsHTTPServer, userID, xmlParser

Project Setup Checklist

# 1. Initialize module
go mod init github.com/org/project

# 2. Create directory structure
mkdir -p cmd/myapp internal/{server,storage}

# 3. Install tooling
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# 4. Create .golangci.yml (see Tooling section)

# 5. Create Makefile
# make build, make test, make lint, make run

# 6. Full quality check
go vet ./... && golangci-lint run ./... && go test -race -cover ./...

Commands

go build ./...                                      # Build all packages
go test -v -race -cover ./...                       # Test with race detector + coverage
go test -run TestSpecific ./internal/user/           # Run specific test
go vet ./...                                        # Static analysis
golangci-lint run ./...                             # Lint (50+ linters)
go mod tidy                                         # Sync dependencies

Reference Files

  • references/concurrency-patterns.md - Goroutines, channels, select, errgroup, worker pools, pipelines
  • references/error-handling.md - Wrapping, sentinel errors, custom types, panic/recover, strategies
  • references/testing-go.md - Table-driven tests, subtests, benchmarks, fuzz, testify, httptest
  • references/project-structure.md - Module layout, cmd/internal/pkg, DI, config, build tags
  • references/tooling-config.md - golangci-lint, go vet, gofumpt, Makefile, CI/CD configs

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.