golang
4
总安装量
2
周安装量
#50547
全站排名
安装命令
npx skills add https://github.com/mauromedda/agent-toolkit --skill golang
Agent 安装分布
claude-code
1
Skill 文档
ABOUTME: Go 1.26 idiomatic development skill with automated quality gates
ABOUTME: Enforces 2025 best practices, pre-commit hooks, and golangci-lint
Go 1.26 Idiomatic Development
Quick Reference
| Principle | Rule |
|---|---|
| Simplicity | Start with simplest solution; justify every abstraction |
| Explicitness | Clear code paths; no magic |
| Composition | Small interfaces; embedding over inheritance |
| Errors | Values, not exceptions; always wrap with context |
| Functions | â¤50 lines, â¤4 params, single responsibility |
| Duplication | Rule of Three before abstracting |
ð FILE OPERATION CHECKPOINT (BLOCKING)
Before EVERY Write or Edit tool call on a .go file:
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â ð STOP - GO SKILL CHECK â
â â
â You are about to modify a .go file. â
â â
â QUESTION: Is /golang skill currently active? â
â â
â If YES â Proceed with the edit â
â If NO â STOP! Invoke /golang FIRST, then edit â
â â
â This check applies to: â
â â Write tool with file_path ending in .go â
â â Edit tool with file_path ending in .go â
â â ANY Go file, regardless of conversation topic â
â â
â Examples that REQUIRE this skill: â
â - "add error handling" (edits .go file) â
â - "fix the test" (edits _test.go file) â
â - "update the handler" (edits handler.go) â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Why this matters: Go code without proper error wrapping (fmt.Errorf %w) loses
context. The skill ensures idiomatic patterns and golangci-lint compliance.
ð RESUMED SESSION CHECKPOINT
When a session is resumed from context compaction, verify Go development state:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â SESSION RESUMED - GO SKILL VERIFICATION â
â â
â Before continuing Go implementation: â
â â
â 1. Was I in the middle of writing Go code? â
â â Check summary for "implementing", "writing", ".go" â
â â
â 2. Did I follow all Go skill guidelines? â
â â Explicit error handling with wrapping â
â â Small interfaces defined by consumer â
â â Functions â¤50 lines, â¤4 params â
â â ABOUTME headers on new files â
â â
â 3. Check code quality before continuing: â
â â Run: go build ./... â
â â Run: golangci-lint run â
â â
â If implementation was in progress: â
â â Review the partial code for completeness â
â â Ensure all errors are handled and wrapped â
â â Verify golangci-lint passes with no warnings â
â â Re-invoke /golang if skill context was lost â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Core Philosophy
The Five Pillars
- Simplicity over cleverness – Start with the simplest solution
- Explicit over implicit – Clear code paths, no magic
- Composition over inheritance – Use interfaces and embedding
- Small interfaces – “The bigger the interface, the weaker the abstraction”
- Errors are values – Handle errors explicitly; don’t panic
Abstraction Rules
Rule 1: Principle of Least Abstraction
1.1 Default to a Single Function
- Solve the problem within a single function first
- Do NOT create helper functions, new types, or new packages prematurely
1.2 Justify Every Abstraction
- Before creating a new function, struct, or package, justify its existence
- If there’s no strong reason to abstract, don’t
Rule 2: Function Design
| Constraint | Limit | Action if Exceeded |
|---|---|---|
| Lines | 50 | Decompose into private helpers |
| Parameters | 4 | Group into config struct |
| Return values | 2 | Use named result struct |
2.1 Single Responsibility
- If you cannot describe what a function does in one simple sentence, it’s doing too much
2.2 Return Values
// 1-2 values: return directly
func Parse(s string) (int, error)
// 3+ related values: use struct
type ParseResult struct {
Value int
Consumed int
Warnings []string
}
func ParseDetailed(s string) (ParseResult, error)
Rule 3: Duplication vs. Abstraction
3.1 The Rule of Three
- Do NOT refactor duplicated code on first or second appearance
- Only consider abstraction on third instance
3.2 Verify True Duplication
- Confirm duplicated code represents the same core logic
- If similar by coincidence but different business rules, keep separate
Rule 4: Package and Interface Philosophy
4.1 Packages Have a Singular Purpose
- Package should represent a single concept
- DO NOT create “utility”, “common”, or “helpers” packages
4.2 Interfaces Defined by Consumer
- Do NOT define large, monolithic interfaces on producer side
- Consumer defines small interface describing only required behavior
4.3 Keep Interfaces Small
// GOOD: Single-method interface
type Reader interface {
Read(p []byte) (n int, err error)
}
// BAD: Kitchen-sink interface
type DataManager interface {
Read() ([]byte, error)
Write([]byte) error
Delete() error
List() ([]string, error)
// ... more methods
}
Go 1.26 Features
Generic Type Constraints
// Use generics when type safety matters across multiple types
func Min[T cmp.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
// Use constraints for custom types
type Number interface {
~int | ~int64 | ~float64
}
When to use generics:
- Collection operations (slices, maps)
- Type-safe data structures
- Avoiding interface{} with type assertions
When NOT to use generics:
- Single concrete type suffices
- Interface polymorphism is clearer
- Adding complexity without benefit
Standard Library Packages
import (
"cmp"
"maps"
"slices"
)
// slices package
sorted := slices.Clone(items)
slices.Sort(sorted)
idx, found := slices.BinarySearch(sorted, target)
slices.Reverse(items)
// maps package
maps.Clone(m)
maps.DeleteFunc(m, func(k, v string) bool {
return v == ""
})
// cmp package
result := cmp.Compare(a, b) // -1, 0, or 1
cmp.Or(a, b, c) // first non-zero value
Structured Logging (log/slog)
import "log/slog"
// Create logger with handler
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
// Structured logging
logger.Info("request processed",
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
slog.Duration("latency", elapsed),
slog.Int("status", status),
)
// With context
logger.InfoContext(ctx, "operation completed",
slog.String("operation", op),
)
// Group related attributes
logger.Info("user action",
slog.Group("user",
slog.String("id", user.ID),
slog.String("role", user.Role),
),
)
Context Patterns
// context.AfterFunc for cleanup
ctx, cancel := context.WithCancel(parentCtx)
stop := context.AfterFunc(ctx, func() {
cleanup()
})
// stop() to prevent callback if not needed
// Always propagate context
func ProcessItem(ctx context.Context, item Item) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// process...
}
Range-over-func Iterators
// Define an iterator
func (s *Set[T]) All() iter.Seq[T] {
return func(yield func(T) bool) {
for v := range s.items {
if !yield(v) {
return
}
}
}
}
// Use with range
for item := range mySet.All() {
process(item)
}
Error Handling
Wrapping Errors
// ALWAYS add context when wrapping
if err != nil {
return fmt.Errorf("failed to process user %s: %w", userID, err)
}
// Sentinel errors for expected conditions
var (
ErrNotFound = errors.New("resource not found")
ErrUnauthorized = errors.New("unauthorized")
)
// Custom error types for rich context
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
Error Checking
// errors.Is for sentinel errors
if errors.Is(err, ErrNotFound) {
return http.StatusNotFound, nil
}
// errors.As for error types
var validErr *ValidationError
if errors.As(err, &validErr) {
return http.StatusBadRequest, validErr
}
// Multiple wrapped errors (Go 1.20+)
err := errors.Join(err1, err2, err3)
Anti-Patterns to Avoid
// BAD: No context
if err != nil {
return err
}
// BAD: Logging and returning (double handling)
if err != nil {
log.Printf("error: %v", err)
return err
}
// BAD: Ignoring errors
result, _ := riskyOperation()
// BAD: Panic in library code
func ParseConfig(s string) Config {
// Don't do this in libraries!
if s == "" {
panic("empty config")
}
}
Concurrency Patterns
Worker Pool with Bounded Concurrency
func Process(ctx context.Context, items []Item, workers int) error {
g, ctx := errgroup.WithContext(ctx)
sem := make(chan struct{}, workers)
for _, item := range items {
item := item // capture for Go < 1.22; still good practice
g.Go(func() error {
select {
case sem <- struct{}{}:
defer func() { <-sem }()
case <-ctx.Done():
return ctx.Err()
}
return processItem(ctx, item)
})
}
return g.Wait()
}
Channel Ownership
// Producer owns the channel; producer closes
func Generate(ctx context.Context) <-chan int {
ch := make(chan int)
go func() {
defer close(ch) // Producer closes
for i := 0; ; i++ {
select {
case ch <- i:
case <-ctx.Done():
return
}
}
}()
return ch
}
sync.Once for Lazy Initialization
type Client struct {
once sync.Once
conn *Connection
connErr error
}
func (c *Client) getConn() (*Connection, error) {
c.once.Do(func() {
c.conn, c.connErr = dial()
})
return c.conn, c.connErr
}
When NOT to Use Channels
// Use mutex for simple shared state
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Inc() {
c.mu.Lock()
c.value++
c.mu.Unlock()
}
// Use atomic for single values
var counter atomic.Int64
counter.Add(1)
Testing
Table-Driven Tests
func TestParseConfig(t *testing.T) {
tests := []struct {
name string
input string
want Config
wantErr bool
}{
{
name: "valid config",
input: `{"port": 8080}`,
want: Config{Port: 8080},
},
{
name: "invalid json",
input: `{invalid}`,
wantErr: true,
},
{
name: "empty input",
input: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseConfig(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ParseConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("ParseConfig() = %v, want %v", got, tt.want)
}
})
}
}
Parallel Tests
func TestIndependentOperations(t *testing.T) {
t.Parallel() // Mark test as parallel-safe
tests := []struct{...}
for _, tt := range tests {
tt := tt // capture
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // Subtests can also be parallel
// ...
})
}
}
Test Cleanup
func TestWithResource(t *testing.T) {
db := setupTestDB(t)
t.Cleanup(func() {
db.Close() // Runs after test completes
})
// Use db...
}
// Helper that registers cleanup
func setupTestDB(t *testing.T) *DB {
t.Helper()
db, err := NewDB(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
return db
}
AST-Grep Anti-Pattern Detection
Problematic Defer
// WRONG: Arguments evaluated immediately
defer require.NoError(t, failpoint.Disable("path"))
// CORRECT: Wrap in anonymous function
defer func() {
require.NoError(t, failpoint.Disable("path"))
}()
Detection rule:
id: problematic-defer
language: go
rule:
kind: defer_statement
pattern: 'defer $A.$B(t, failpoint.$M($$))'
JSON Tag Security Issue
// WRONG: Field can still be unmarshaled with "-" key
type User struct {
IsAdmin bool `json:"-,omitempty"`
}
// CORRECT: Properly omits field
type User struct {
IsAdmin bool `json:"-"`
}
Detection rule:
id: unmarshal-tag-is-dash
language: go
rule:
pattern: '`$TAG`'
inside:
kind: field_declaration
constraints:
TAG:
regex: 'json:"-,.*"'
Documentation Standards
// Package user provides user management functionality.
// It handles user creation, authentication, and profile management.
package user
// User represents a registered user in the system.
// Zero value is not useful; use NewUser to create instances.
type User struct {
ID string // Unique identifier
Email string // Validated email address
CreatedAt time.Time // UTC timestamp
}
// NewUser creates a new User with the given email.
// It returns an error if the email format is invalid.
//
// Example:
//
// user, err := NewUser("alice@example.com")
// if err != nil {
// return fmt.Errorf("creating user: %w", err)
// }
func NewUser(email string) (*User, error) {
if !isValidEmail(email) {
return nil, &ValidationError{Field: "email", Message: "invalid format"}
}
return &User{
ID: generateID(),
Email: email,
CreatedAt: time.Now().UTC(),
}, nil
}
Project Setup
# Initialize new Go 1.26 module
go mod init github.com/org/project
# Verify go.mod contains: go 1.26
# Install development tools
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0
go install golang.org/x/tools/cmd/goimports@latest
# Set up pre-commit hook
cp ~/.claude/skills/golang/scripts/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# Copy linter config
cp ~/.claude/skills/golang/resources/.golangci.yml .golangci.yml
cp ~/.claude/skills/golang/resources/.editorconfig .editorconfig
Anti-Patterns Checklist
| Anti-Pattern | Detection | Fix |
|---|---|---|
| Empty error handling | return err without context |
Wrap with fmt.Errorf |
| Naked returns | return without values |
Always use explicit values |
| Global mutable state | Package-level var |
Pass as dependency |
Overuse of init() |
Multiple init functions | Explicit initialization |
| Ignoring context | No ctx parameter |
Propagate context always |
| Interface pollution | Unused interfaces | Define at consumer site |
| Premature channels | Channel for simple mutex case | Use sync.Mutex |
| Panic in libraries | panic() in exported funcs |
Return errors |
Scripts
| Script | Purpose | Usage |
|---|---|---|
pre-commit |
Git hook for all checks | Copy to .git/hooks/ |
lint.sh |
Run golangci-lint | ./scripts/lint.sh |
fmt-check.sh |
Verify formatting | ./scripts/fmt-check.sh |
Run setup:
~/.claude/skills/golang/scripts/lint.sh --help