Claude-skill-registry go-pro
Expert Go developer specializing in idiomatic patterns, concurrency, error handling, and clean package design. This skill should be used PROACTIVELY when working on any Go code - implementing features, designing APIs, debugging issues, or reviewing code quality. Use unless a more specific subagent role applies.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-pro" ~/.claude/skills/majiayu000-claude-skill-registry-go-pro && rm -rf "$T"
skills/data/go-pro/SKILL.mdGo Pro
Senior-level Go expertise for production projects. Focuses on idiomatic patterns, simplicity, and Go's design philosophy.
When Invoked
- Review
andgo.mod
for project conventions.golangci.yml - For build system setup, invoke the just-pro skill
- Apply Go idioms and established project patterns
Core Standards
Non-Negotiable:
- All exported identifiers have doc comments
- All errors checked and handled (no
)_ = err - NO
for recoverable errorspanic() - golangci-lint passes with project configuration
- Table-driven tests for multiple cases
Foundational Principles:
- Single Responsibility: One package = one purpose, one function = one job
- No God Objects: Split large structs; if it has 10+ fields or methods, decompose
- Dependency Injection: Pass dependencies, don't create them internally
- Small Interfaces: 1-3 methods max; compose larger behaviors from small interfaces
Project Setup (Go 1.25+)
Version Management with mise
mise manages language runtimes per-project. Ensures all contributors use the same Go version—no "works on my machine" issues.
# Install mise (once) curl https://mise.run | sh # In project root mise use go@1.25 # Creates .mise.toml — commit it # Team members just run: mise install
New Project Quick Start
# Initialize go mod init github.com/org/project go mod edit -go=1.25 # Add toolchain dependencies (tracked in go.mod) go get -tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest go get -tool golang.org/x/tools/cmd/goimports@latest # Copy configs from this skill's references/ directory: # references/gitignore → .gitignore # references/golangci-v2.yml → .golangci.yml # For build system, invoke just-pro skill # Verify just check # Or: go tool golangci-lint run
Developer Onboarding
git clone <repo> && cd <repo> just setup # Runs mise trust/install + go mod download just check # Verify everything works
Or manually:
mise trust && mise install # Get pinned Go version go mod download # Get dependencies
Why
? Tools versioned in go.mod = reproducible builds, same versions for all devs, no separate installation needed.go get -tool
Build System
Invoke the
skill for build system setup. It covers:just-pro
- Simple repos vs monorepos
- Hierarchical justfile modules
- Go-specific templates (
)references/package-go.just
Why just? Consistent toolchain frontend between agents and humans. Instead of remembering
go tool golangci-lint run --fix, use just fix.
Quality Assurance
Auto-Fix First - Always try auto-fix before manual fixes:
just fix # Or: go tool golangci-lint run --fix && go tool goimports -w .
Verification:
just check # Or: go tool golangci-lint run && go test -race ./...
Quick Reference
Error Handling
| Pattern | Use |
|---|---|
| Propagate unchanged (internal errors) |
| Wrap with context (cross-boundary) |
| Check specific error |
| Extract typed error |
Sentinel Errors - Define package-level errors for expected conditions:
var ErrNotFound = errors.New("not found") var ErrInvalidInput = errors.New("invalid input")
Generics
// Constrained generics - prefer specific constraints func Map[T, U any](items []T, fn func(T) U) []U { result := make([]U, len(items)) for i, item := range items { result[i] = fn(item) } return result } // Type constraints - use interfaces type Ordered interface { ~int | ~int64 | ~float64 | ~string } func Max[T Ordered](a, b T) T { if a > b { return a } return b } // Avoid: overly generic signatures that lose type safety // Prefer: concrete types until generics are clearly needed
Structured Logging (slog)
import "log/slog" // Package-level logger with context func NewService(logger *slog.Logger) *Service { return &Service{ log: logger.With("component", "service"), } } // Structured logging with levels s.log.Info("request processed", "method", r.Method, "path", r.URL.Path, "duration", time.Since(start), ) s.log.Error("operation failed", "err", err, "user_id", userID, )
Iterators (Go 1.23+)
import "iter" // Return iterators for large collections func (db *DB) Users() iter.Seq[User] { return func(yield func(User) bool) { rows, _ := db.Query("SELECT * FROM users") defer rows.Close() for rows.Next() { var u User rows.Scan(&u.ID, &u.Name) if !yield(u) { return } } } } // Consume with range for user := range db.Users() { process(user) } // Seq2 for key-value pairs func (m *Map[K, V]) All() iter.Seq2[K, V]
Concurrency
| Pattern | Use |
|---|---|
| Wait for goroutines |
/ | Protect shared state |
| Cancellation/timeouts |
| Concurrent with error collection |
// Context-aware work func DoWork(ctx context.Context, arg string) error { select { case <-ctx.Done(): return ctx.Err() default: } // ... work }
Testing
// Table-driven tests with subtests func TestParse(t *testing.T) { tests := []struct { name string input string want Result wantErr bool }{ {name: "valid", input: "foo", want: Result{Value: "foo"}}, {name: "empty", input: "", wantErr: true}, {name: "special", input: "a@b", want: Result{Value: "a@b"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := Parse(tt.input) if (err != nil) != tt.wantErr { t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("Parse() = %v, want %v", got, tt.want) } }) } } // Testify for complex assertions import "github.com/stretchr/testify/assert" import "github.com/stretchr/testify/require" func TestService(t *testing.T) { require.NoError(t, err) // Fail fast assert.Equal(t, expected, actual) // Continue on failure assert.Len(t, items, 3) assert.Contains(t, items, target) }
Pointer vs Value Receivers
// Use pointer receivers when: // - Method modifies the receiver // - Receiver is large (avoid copy) // - Consistency: if any method needs pointer, use pointer for all func (s *Service) UpdateConfig(cfg Config) { s.cfg = cfg } // Use value receivers when: // - Receiver is small (int, string, small struct) // - Method is read-only and receiver is immutable func (p Point) Distance(other Point) float64 { ... }
Package Organization
project/ ├── cmd/appname/main.go # Entry point ├── internal/ # Private packages │ ├── api/ # Handlers │ └── domain/ # Business logic ├── go.mod ├── .golangci.yml └── justfile
Rules: One package = one purpose. Use
internal/ for implementation. Avoid util, common, helpers packages.
DX Patterns
Doctor Recipe with Version Validation
Doctor scripts should validate that toolchain versions meet requirements, not just check existence:
# Validate toolchain versions meet requirements doctor: #!/usr/bin/env bash set -euo pipefail echo "Checking toolchain..." # Validate Go version (requires 1.25+) GO_VERSION=$(go version | grep -oE 'go[0-9]+\.[0-9]+' | sed 's/go//') if [[ "$(printf '%s\n' "1.25" "$GO_VERSION" | sort -V | head -1)" != "1.25" ]]; then echo "FAIL: Go $GO_VERSION < 1.25 required" exit 1 fi echo "✓ Go $GO_VERSION" # Add more version checks as needed echo "All checks passed"
Port Conflict Detection
For services that bind ports, check availability before starting:
# Check if required ports are available before starting check-ports: #!/usr/bin/env bash for port in 8080 5432; do if lsof -i :$port >/dev/null 2>&1; then echo "FAIL: Port $port already in use" exit 1 fi done echo "All ports available"
First-Run Detection
Avoid redundant setup work with first-run detection:
# Setup with first-run detection setup: #!/usr/bin/env bash if [[ -f .setup-complete ]]; then echo "Already set up. Run 'just setup-force' to reinstall." exit 0 fi mise trust && mise install go mod download touch .setup-complete echo "Setup complete" setup-force: rm -f .setup-complete @just setup
Anti-Patterns
for recoverable errors (usepanic()
)return err- Ignoring errors with
_ - Exported package-level mutable variables
- Channels when mutex suffices
- Getter/setter methods (Go isn't Java)
with side effectsinit()- God structs with 10+ fields/methods
orinterface{}
when specific types workany- Premature generics (concrete types first)
AI Agent Guidelines
Before writing code:
- Read
for module path and Go versiongo.mod - Check
for project-specific lint rules.golangci.yml - Identify existing patterns in the codebase to follow
When writing code:
- Handle all errors explicitly - never use
_ = err - Add doc comments to exported identifiers immediately
- Use existing project abstractions over creating new ones
- Prefer concrete types; add generics only when pattern repeats 3+ times
Before committing:
- Run
(standard for projects using just)just check - Fallback:
go tool golangci-lint run --fix && go tool golangci-lint run - Fallback:
to catch race conditionsgo test -race ./...