Effective go
Skill radkomih/claude-code-ext/plugins/effective-go/skills/effective-go
Claude Code Plugins
npx -y skills add radkomih/claude-code-ext --skill effective-goAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 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.
What its author says it does
Copied from the file, not written here
Analyzes and refactors Go code using Effective Go principles. Use whenever Go code is written, reviewed, or modified — including goroutine issues, error handling, naming, interfaces, or formatting. Also trigger when the user asks whether their Go code is idiomatic, even without mentioning "Effective Go" by name.
SKILL.md
13.9 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it
Effective Go Skill
Comprehensive Go refactoring framework based on the official Effective Go guide and Go best practices.
Prerequisites
Always read resources/effective-go-principles.json (in this skill's directory) before starting.
It contains 25+ principles with definitions, code smells, and refactoring guidance including:
- Formatting (gofmt, semicolons)
- Naming (packages, interfaces, exported names)
- Control structures (if, for, switch, defer)
- Data structures (slices, maps, arrays)
- Functions (multiple returns, named returns, defer)
- Concurrency (goroutines, channels, select)
- Error handling (error vs panic, wrapping)
- Interfaces and methods (receivers, embedding)
Refactoring Approach
Four-Phase Strategy
Phase 1: Discovery & Analysis (15-20 min)
Understand the Codebase:
- Identify scope (package, module, or entire project)
- Check Go version and module structure
- Analyze existing code patterns
- Review dependencies and imports
Scan for Code Smells:
- No gofmt/goimports formatting
- Non-idiomatic naming (snake_case, wrong capitalization)
- Wrong receiver types (value when pointer needed)
- Missing error checks
- Goroutine leaks or race conditions
- Improper channel usage
- Panic in library code
- Mutable value types that should be immutable
- Large interfaces (>3 methods for non-standard libs)
- Primitive obsession (no custom types)
Technical Exploration — search and run:
- Run
gofmt -l . | grep -v "vendor/"— check formatting - Pattern
^func [a-z]in*.gofiles — find unexported funcs that may need export - Pattern
^type [a-z]— find unexported types - Pattern
go funcin*.gofiles — find goroutine launches - Pattern
err :=— find error assignments - Pattern
panic\(— find panic usage in library code - Pattern
type.*interface— find interface definitions
Phase 2: Strategic Refactoring Plan (10-15 min)
Based on loaded Effective Go principles:
-
Formatting and Style
- Run gofmt/goimports on all files
- Fix semicolon issues
- Ensure proper brace placement
- Clean up whitespace
-
Naming Conventions
- Fix package names (lowercase, single-word)
- Correct exported/unexported names
- Apply MixedCaps/mixedCaps consistently
- Rename interfaces (-er suffix for single-method)
-
Prioritize Refactoring
- Critical: gofmt, data races, goroutine leaks, missing error checks
- High: Wrong receivers, panic in libraries, improper channel usage
- Medium: Non-idiomatic naming, primitive obsession, large interfaces
- Low: Style improvements, comment formatting
Phase 3: Tactical Pattern Application (30-45 min)
Apply patterns systematically:
1. Formatting
- Run gofmt -w on all Go files
- Ensure tabs for indentation
- Fix opening brace placement
- Remove unnecessary semicolons
2. Naming
- Package names: short, lowercase, no underscores
- Exported names: Start with uppercase
- Unexported names: Start with lowercase
- Interfaces: Use -er suffix (Reader, Writer, Closer)
- No snake_case: Use MixedCaps or mixedCaps
- Acronyms: All caps (HTTP, URL, ID)
3. Control Structures
- Use guard clauses (early returns)
- Prefer for range over traditional for loops
- Use expression-less switch for if-else chains
- Apply defer for cleanup operations
- Avoid naked returns in long functions
4. Error Handling
- Check all errors explicitly
- Add context with fmt.Errorf and %w
- Return errors, don't panic (except in truly exceptional cases)
- Use errors.Is and errors.As for error checking
- Implement error wrapping consistently
5. Concurrency
- Fix goroutine leaks (ensure they can exit)
- Use channels for communication
- Apply proper channel closing (sender closes)
- Use select for multiplexing
- Avoid shared memory, prefer channels
- Add sync.WaitGroup for coordination
- Fix loop variable capture in goroutines (only an issue pre-Go 1.22; check go.mod)
6. Pointers vs Values
- Use pointer receivers when modifying receiver
- Use pointer receivers for large structs
- Be consistent (all pointer or all value for a type)
- Use pointer receivers for types with sync.Mutex
7. Interfaces
- Keep interfaces small (1-3 methods ideal)
- Define interfaces where used, not where implemented
- Accept interfaces, return structs
- Use empty interface sparingly
8. Data Structures
- Prefer slices over arrays
- Use make() with capacity hints
- Ensure maps are initialized with make()
- Use composite literals for initialization
- Apply append() correctly (assign result)
Phase 4: Validation & Testing (10-15 min)
Verify Improvements:
- All files pass gofmt check
- No exported names start with lowercase
- All errors are checked or explicitly ignored
- No goroutine leaks detected
- Channels properly closed from sender
- Receiver types are consistent and appropriate
- No panic calls in library code
- Interfaces are small and focused
- Code follows Go idioms
Testing Strategy:
- Run go vet on all packages
- Run golint or staticcheck
- Run go test -race to detect data races
- Use go test -cover for coverage
- Run golangci-lint for comprehensive checks
Core Effective Go Principles Reference
Formatting
- gofmt - Standard formatting, non-negotiable
- Semicolons - Automatic insertion, placement rules
Naming
- Package Names - Short, lowercase, single-word
- Exported Names - Uppercase = public
- Interface Naming - -er suffix for single-method
- MixedCaps - No underscores in identifiers
Control Structures
- Guard Clauses - Early returns, reduced nesting
- For Loop Patterns - Range, traditional, infinite
- Switch Statements - No fallthrough by default
- Type Switch - Handling interface types
- Defer - Cleanup operations
Functions
- Multiple Return Values - Return (result, error)
- Named Return Values - For documentation/defer
- new vs make - Allocation primitives
Data
- Slices - Dynamic sequences
- Maps - Key-value storage
- Printing - Format verbs (%v, %+v, %#v)
- Append - Growing slices
Initialization
- Composite Literals - Inline initialization
Methods
- Pointer vs Value Receivers - When to use each
Interfaces
- Interfaces - Implicit implementation
- Type Assertions - Safe conversion
- Embedding - Composition over inheritance
Concurrency
- Share by Communicating - Channel-based patterns
- Goroutines - Lightweight concurrency
- Channels - Communication pipes
- Select - Multiplexing channels
Errors
- Error Handling - Explicit error returns
- Panic - Only for unrecoverable errors
- Recover - Panic recovery
- Error Wrapping - Adding context with %w
Code Smell Detection Checklist
Critical Anti-Patterns
- Code not formatted with gofmt
- Exported names starting with lowercase
- Panic used in library code
- Data races (concurrent map access, shared state)
- Goroutine leaks (no way to stop)
- Sending on closed channel
High Priority Anti-Patterns
- Mixed pointer and value receivers on same type
- Missing error checks
- Errors ignored with _
- Improper channel closing (receiver closes, or closing nil channel)
- Loop variable captured in goroutine (pre-Go 1.22 only — check go.mod)
- Using new() for slices, maps, channels
- Not assigning append() result
Medium Priority Anti-Patterns
- Snake_case naming instead of MixedCaps
- Large interfaces (>5 methods)
- Missing doc comments on exported identifiers
- Using interface{} when specific type would work
- Not using defer for cleanup
- Naked returns in long functions
- Arrays when slices would be better
Low Priority Anti-Patterns
- Inconsistent naming
- Missing String() method for custom types
- Could use type switch instead of repeated assertions
- Could use composite literal instead of new()
Output Format
1. Anti-Pattern Identified
File: internal/service/order.go:45-78
Smell: Missing error check - result of operation ignored
Principle Violated: Error Handling
Impact: Silent failures, bugs go unnoticed
2. Effective Go Principle to Apply
Principle: Error Handling (from effective-go-principles.json)
Category: Errors
Key Point: Always check errors explicitly, never ignore them
When to Apply: Every function call that returns an error
3. Refactoring Steps
Step 1: Find all places where errors are returned
Step 2: Add explicit error checks with if err != nil
Step 3: Add context to errors with fmt.Errorf("operation failed: %w", err)
Step 4: Propagate or handle errors appropriately
Step 5: Use _ only for intentional ignoring (document why)
4. Code Example
// BEFORE: Missing error check (Anti-pattern)
func processOrder(id string) *Order {
order, _ := db.GetOrder(id) // ERROR: Ignoring error!
return order
}
func updateUser(user *User) {
db.Save(user) // ERROR: Not checking error!
}
// AFTER: Proper error handling (Go idiom)
func processOrder(id string) (*Order, error) {
order, err := db.GetOrder(id)
if err != nil {
return nil, fmt.Errorf("getting order %s: %w", id, err)
}
return order, nil
}
func updateUser(user *User) error {
if err := db.Save(user); err != nil {
return fmt.Errorf("saving user %s: %w", user.ID, err)
}
return nil
}
5. Impact Assessment
Benefits:
- Errors are caught and handled explicitly
- Better error context for debugging
- No silent failures
- Follows Go conventions
Metrics:
- Improved reliability
- Better error messages
- Easier debugging
- Code passes go vet checks
Language-Specific Patterns
Error Handling
// Good: Proper error handling with context
func loadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config from %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
}
// Usage
cfg, err := loadConfig("config.json")
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
Receiver Types
// Good: Consistent pointer receivers
type Counter struct {
mu sync.Mutex
value int
}
// Pointer receiver - modifies state
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
// Pointer receiver - consistency
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
// Bad: Mixed receivers
type BadCounter struct {
value int
}
func (c BadCounter) Increment() { // Value receiver - doesn't modify!
c.value++ // Modifies copy
}
func (c *BadCounter) Value() int { // Pointer receiver - inconsistent
return c.value
}
Goroutines and Channels
// Good: Proper goroutine coordination
func processBatch(items []Item) error {
var wg sync.WaitGroup
errors := make(chan error, len(items))
for _, item := range items {
wg.Add(1)
item := item // Capture variable (required pre-Go 1.22; unnecessary in 1.22+)
go func() {
defer wg.Done()
if err := process(item); err != nil {
errors <- err
}
}()
}
// Wait in separate goroutine
go func() {
wg.Wait()
close(errors) // Sender closes channel
}()
// Collect errors
for err := range errors {
return err // Return first error
}
return nil
}
// Bad: Goroutine leak
func badProcess(items []Item) {
for _, item := range items {
go func() {
process(item) // Captures loop variable - BUG!
// No coordination, no error handling, no way to stop
}()
}
// Function returns immediately, goroutines may still be running
}
Interfaces
// Good: Small, focused interfaces
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type ReadCloser interface {
Reader
Closer
}
// Define where used, not where implemented
type DataStore interface {
Save(data []byte) error
}
func SaveToStore(store DataStore, data []byte) error {
return store.Save(data)
}
// Bad: Large, unfocused interface
type Database interface {
GetUser(id string) (*User, error)
CreateUser(u *User) error
UpdateUser(u *User) error
DeleteUser(id string) error
GetOrder(id string) (*Order, error)
CreateOrder(o *Order) error
// ... 20+ more methods
}
Best Practices
Do
- Always run gofmt before committing
- Check all errors explicitly
- Use defer for cleanup operations
- Keep interfaces small
- Accept interfaces, return structs
- Use pointer receivers for large structs or when modifying
- Close channels from the sender side
- Use context for cancellation and timeouts
- Wrap errors with %w for error chains
- Use sync.WaitGroup to coordinate goroutines
Don't
- Don't ignore gofmt warnings
- Don't use panic in library code
- Don't ignore errors with _
- Don't mix pointer and value receivers
- Don't capture loop variables in goroutines without copying
- Don't send on closed channels
- Don't use naked returns in long functions
- Don't create large interfaces
- Don't share memory without synchronization
- Don't use new() for slices, maps, or channels
Resources
- Effective Go Principles: See
resources/effective-go-principles.jsonfor complete definitions - Checklist: See
CHECKLIST.mdfor full anti-pattern list - Official Guide: https://go.dev/doc/effective_go
- Go Code Review Comments: https://go.dev/wiki/CodeReviewComments
Workflow Integration
This skill can be:
- Invoked from
/go-reviewcommand - Used by
go-analyzeragent for autonomous analysis - Triggered by pre-commit hooks to check for anti-patterns
- Called from other skills for Go code improvements