Effective Go Skill
Comprehensive Go refactoring framework based on the official Effective Go guide and Go best practices.
When to Use This Skill
- Refactoring Go code for idiomatic patterns
- Identifying Go-specific anti-patterns
- Improving code formatting and style
- Applying proper error handling patterns
- Optimizing concurrency patterns (goroutines, channels)
- Implementing proper naming conventions
- Analyzing interface and method design
- Reviewing receiver types (pointer vs value)
Prerequisites
CRITICAL: Always load the Effective Go principles first:
Read: plugins/effective-go/resources/effective-go-principles.json
This JSON 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:
# Check if code is gofmt'd
gofmt -l . | grep -v "vendor/"
# Find exported names that might be wrong
grep -r "^func [a-z]" --include="*.go"
grep -r "^type [a-z]" --include="*.go"
# Find potential goroutine issues
grep -r "go func" --include="*.go"
grep -r "go " --include="*.go" | grep -v "go func"
# Find error handling
grep -r "err :=" --include="*.go"
grep -r "if err" --include="*.go"
# Find panic usage
grep -r "panic(" --include="*.go"
# Find interfaces
grep -r "type.*interface" --include="*.go"
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
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:
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
High Priority Anti-Patterns
Medium Priority Anti-Patterns
Low Priority Anti-Patterns
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
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
Workflow Integration
This skill can be:
- Invoked from
/go-review command
- Used by
go-analyzer agent for autonomous analysis
- Triggered by pre-commit hooks to check for anti-patterns
- Called from other skills for Go code improvements
1---2name: effective-go3description: Analyzes and refactors Go code using Effective Go principles. Use when refactoring Go code, identifying anti-patterns, improving Go idioms, or applying Go best practices.4---5
6# Effective Go Skill
7
8Comprehensive Go refactoring framework based on the official Effective Go guide and Go best practices.
9
10## When to Use This Skill
11
12- Refactoring Go code for idiomatic patterns
13- Identifying Go-specific anti-patterns
14- Improving code formatting and style
15- Applying proper error handling patterns
16- Optimizing concurrency patterns (goroutines, channels)
17- Implementing proper naming conventions
18- Analyzing interface and method design
19- Reviewing receiver types (pointer vs value)
20
21## Prerequisites
22
23**CRITICAL**: Always load the Effective Go principles first:
24
25```bash
26Read: plugins/effective-go/resources/effective-go-principles.json
27```
28
29This JSON contains 25+ principles with definitions, code smells, and refactoring guidance including:
30- Formatting (gofmt, semicolons)
31- Naming (packages, interfaces, exported names)
32- Control structures (if, for, switch, defer)
33- Data structures (slices, maps, arrays)
34- Functions (multiple returns, named returns, defer)
35- Concurrency (goroutines, channels, select)
36- Error handling (error vs panic, wrapping)
37- Interfaces and methods (receivers, embedding)
38
39## Refactoring Approach
40
41### Four-Phase Strategy
42
43#### Phase 1: Discovery & Analysis (15-20 min)
44
45**Understand the Codebase:**
461. Identify scope (package, module, or entire project)
472. Check Go version and module structure
483. Analyze existing code patterns
494. Review dependencies and imports
50
51**Scan for Code Smells:**
52- No gofmt/goimports formatting
53- Non-idiomatic naming (snake_case, wrong capitalization)
54- Wrong receiver types (value when pointer needed)
55- Missing error checks
56- Goroutine leaks or race conditions
57- Improper channel usage
58- Panic in library code
59- Mutable value types that should be immutable
60- Large interfaces (>3 methods for non-standard libs)
61- Primitive obsession (no custom types)
62
63**Technical Exploration:**
64```bash
65# Check if code is gofmt'd
66gofmt -l . | grep -v "vendor/"
67
68# Find exported names that might be wrong
69grep -r "^func [a-z]" --include="*.go"
70grep -r "^type [a-z]" --include="*.go"
71
72# Find potential goroutine issues
73grep -r "go func" --include="*.go"
74grep -r "go " --include="*.go" | grep -v "go func"
75
76# Find error handling
77grep -r "err :=" --include="*.go"
78grep -r "if err" --include="*.go"
79
80# Find panic usage
81grep -r "panic(" --include="*.go"
82
83# Find interfaces
84grep -r "type.*interface" --include="*.go"
85```
86
87#### Phase 2: Strategic Refactoring Plan (10-15 min)
88
89Based on loaded Effective Go principles:
90
911. **Formatting and Style**
92 - Run gofmt/goimports on all files
93 - Fix semicolon issues
94 - Ensure proper brace placement
95 - Clean up whitespace
96
972. **Naming Conventions**
98 - Fix package names (lowercase, single-word)
99 - Correct exported/unexported names
100 - Apply MixedCaps/mixedCaps consistently
101 - Rename interfaces (-er suffix for single-method)
102
1033. **Prioritize Refactoring**
104 - **Critical**: gofmt, data races, goroutine leaks, missing error checks
105 - **High**: Wrong receivers, panic in libraries, improper channel usage
106 - **Medium**: Non-idiomatic naming, primitive obsession, large interfaces
107 - **Low**: Style improvements, comment formatting
108
109#### Phase 3: Tactical Pattern Application (30-45 min)
110
111Apply patterns systematically:
112
113**1. Formatting**
114- Run gofmt -w on all Go files
115- Ensure tabs for indentation
116- Fix opening brace placement
117- Remove unnecessary semicolons
118
119**2. Naming**
120- Package names: short, lowercase, no underscores
121- Exported names: Start with uppercase
122- Unexported names: Start with lowercase
123- Interfaces: Use -er suffix (Reader, Writer, Closer)
124- No snake_case: Use MixedCaps or mixedCaps
125- Acronyms: All caps (HTTP, URL, ID)
126
127**3. Control Structures**
128- Use guard clauses (early returns)
129- Prefer for range over traditional for loops
130- Use expression-less switch for if-else chains
131- Apply defer for cleanup operations
132- Avoid naked returns in long functions
133
134**4. Error Handling**
135- Check all errors explicitly
136- Add context with fmt.Errorf and %w
137- Return errors, don't panic (except in truly exceptional cases)
138- Use errors.Is and errors.As for error checking
139- Implement error wrapping consistently
140
141**5. Concurrency**
142- Fix goroutine leaks (ensure they can exit)
143- Use channels for communication
144- Apply proper channel closing (sender closes)
145- Use select for multiplexing
146- Avoid shared memory, prefer channels
147- Add sync.WaitGroup for coordination
148- Fix loop variable capture in goroutines
149
150**6. Pointers vs Values**
151- Use pointer receivers when modifying receiver
152- Use pointer receivers for large structs
153- Be consistent (all pointer or all value for a type)
154- Use pointer receivers for types with sync.Mutex
155
156**7. Interfaces**
157- Keep interfaces small (1-3 methods ideal)
158- Define interfaces where used, not where implemented
159- Accept interfaces, return structs
160- Use empty interface sparingly
161
162**8. Data Structures**
163- Prefer slices over arrays
164- Use make() with capacity hints
165- Ensure maps are initialized with make()
166- Use composite literals for initialization
167- Apply append() correctly (assign result)
168
169#### Phase 4: Validation & Testing (10-15 min)
170
171**Verify Improvements:**
172- [ ] All files pass gofmt check
173- [ ] No exported names start with lowercase
174- [ ] All errors are checked or explicitly ignored
175- [ ] No goroutine leaks detected
176- [ ] Channels properly closed from sender
177- [ ] Receiver types are consistent and appropriate
178- [ ] No panic calls in library code
179- [ ] Interfaces are small and focused
180- [ ] Code follows Go idioms
181
182**Testing Strategy:**
183- Run go vet on all packages
184- Run golint or staticcheck
185- Run go test -race to detect data races
186- Use go test -cover for coverage
187- Run golangci-lint for comprehensive checks
188
189## Core Effective Go Principles Reference
190
191### Formatting
1921. **gofmt** - Standard formatting, non-negotiable
1932. **Semicolons** - Automatic insertion, placement rules
194
195### Naming
1963. **Package Names** - Short, lowercase, single-word
1974. **Exported Names** - Uppercase = public
1985. **Interface Naming** - -er suffix for single-method
1996. **MixedCaps** - No underscores in identifiers
200
201### Control Structures
2027. **Guard Clauses** - Early returns, reduced nesting
2038. **For Loop Patterns** - Range, traditional, infinite
2049. **Switch Statements** - No fallthrough by default
20510. **Type Switch** - Handling interface types
20611. **Defer** - Cleanup operations
207
208### Functions
20912. **Multiple Return Values** - Return (result, error)
21013. **Named Return Values** - For documentation/defer
21114. **new vs make** - Allocation primitives
212
213### Data
21415. **Slices** - Dynamic sequences
21516. **Maps** - Key-value storage
21617. **Printing** - Format verbs (%v, %+v, %#v)
21718. **Append** - Growing slices
218
219### Initialization
22019. **Composite Literals** - Inline initialization
221
222### Methods
22320. **Pointer vs Value Receivers** - When to use each
224
225### Interfaces
22621. **Interfaces** - Implicit implementation
22722. **Type Assertions** - Safe conversion
22823. **Embedding** - Composition over inheritance
229
230### Concurrency
23124. **Share by Communicating** - Channel-based patterns
23225. **Goroutines** - Lightweight concurrency
23326. **Channels** - Communication pipes
23427. **Select** - Multiplexing channels
235
236### Errors
23728. **Error Handling** - Explicit error returns
23829. **Panic** - Only for unrecoverable errors
23930. **Recover** - Panic recovery
24031. **Error Wrapping** - Adding context with %w
241
242## Code Smell Detection Checklist
243
244### Critical Anti-Patterns
245- [ ] Code not formatted with gofmt
246- [ ] Exported names starting with lowercase
247- [ ] Panic used in library code
248- [ ] Data races (concurrent map access, shared state)
249- [ ] Goroutine leaks (no way to stop)
250- [ ] Sending on closed channel
251
252### High Priority Anti-Patterns
253- [ ] Mixed pointer and value receivers on same type
254- [ ] Missing error checks
255- [ ] Errors ignored with _
256- [ ] Improper channel closing (receiver closes, or closing nil channel)
257- [ ] Loop variable captured in goroutine
258- [ ] Using new() for slices, maps, channels
259- [ ] Not assigning append() result
260
261### Medium Priority Anti-Patterns
262- [ ] Snake_case naming instead of MixedCaps
263- [ ] Large interfaces (>5 methods)
264- [ ] Missing doc comments on exported identifiers
265- [ ] Using interface{} when specific type would work
266- [ ] Not using defer for cleanup
267- [ ] Naked returns in long functions
268- [ ] Arrays when slices would be better
269
270### Low Priority Anti-Patterns
271- [ ] Inconsistent naming
272- [ ] Missing String() method for custom types
273- [ ] Could use type switch instead of repeated assertions
274- [ ] Could use composite literal instead of new()
275
276## Output Format
277
278### 1. Anti-Pattern Identified
279```
280File: internal/service/order.go:45-78
281Smell: Missing error check - result of operation ignored
282Principle Violated: Error Handling
283Impact: Silent failures, bugs go unnoticed
284```
285
286### 2. Effective Go Principle to Apply
287```
288Principle: Error Handling (from effective-go-principles.json)
289Category: Errors
290Key Point: Always check errors explicitly, never ignore them
291When to Apply: Every function call that returns an error
292```
293
294### 3. Refactoring Steps
295```
296Step 1: Find all places where errors are returned
297Step 2: Add explicit error checks with if err != nil
298Step 3: Add context to errors with fmt.Errorf("operation failed: %w", err)
299Step 4: Propagate or handle errors appropriately
300Step 5: Use _ only for intentional ignoring (document why)
301```
302
303### 4. Code Example
304```go
305// BEFORE: Missing error check (Anti-pattern)
306func processOrder(id string) *Order {
307 order, _ := db.GetOrder(id) // ERROR: Ignoring error!
308 return order
309}
310
311func updateUser(user *User) {
312 db.Save(user) // ERROR: Not checking error!
313}
314
315// AFTER: Proper error handling (Go idiom)
316func processOrder(id string) (*Order, error) {
317 order, err := db.GetOrder(id)
318 if err != nil {
319 return nil, fmt.Errorf("getting order %s: %w", id, err)
320 }
321 return order, nil
322}
323
324func updateUser(user *User) error {
325 if err := db.Save(user); err != nil {
326 return fmt.Errorf("saving user %s: %w", user.ID, err)
327 }
328 return nil
329}
330```
331
332### 5. Impact Assessment
333**Benefits:**
334- Errors are caught and handled explicitly
335- Better error context for debugging
336- No silent failures
337- Follows Go conventions
338
339**Metrics:**
340- Improved reliability
341- Better error messages
342- Easier debugging
343- Code passes go vet checks
344
345## Language-Specific Patterns
346
347### Error Handling
348```go
349// Good: Proper error handling with context
350func loadConfig(path string) (*Config, error) {
351 data, err := os.ReadFile(path)
352 if err != nil {
353 return nil, fmt.Errorf("reading config from %s: %w", path, err)
354 }
355
356 var cfg Config
357 if err := json.Unmarshal(data, &cfg); err != nil {
358 return nil, fmt.Errorf("parsing config: %w", err)
359 }
360
361 return &cfg, nil
362}
363
364// Usage
365cfg, err := loadConfig("config.json")
366if err != nil {
367 log.Fatalf("Failed to load config: %v", err)
368}
369```
370
371### Receiver Types
372```go
373// Good: Consistent pointer receivers
374type Counter struct {
375 mu sync.Mutex
376 value int
377}
378
379// Pointer receiver - modifies state
380func (c *Counter) Increment() {
381 c.mu.Lock()
382 defer c.mu.Unlock()
383 c.value++
384}
385
386// Pointer receiver - consistency
387func (c *Counter) Value() int {
388 c.mu.Lock()
389 defer c.mu.Unlock()
390 return c.value
391}
392
393// Bad: Mixed receivers
394type BadCounter struct {
395 value int
396}
397
398func (c BadCounter) Increment() { // Value receiver - doesn't modify!
399 c.value++ // Modifies copy
400}
401
402func (c *BadCounter) Value() int { // Pointer receiver - inconsistent
403 return c.value
404}
405```
406
407### Goroutines and Channels
408```go
409// Good: Proper goroutine coordination
410func processBatch(items []Item) error {
411 var wg sync.WaitGroup
412 errors := make(chan error, len(items))
413
414 for _, item := range items {
415 wg.Add(1)
416 item := item // Capture variable
417
418 go func() {
419 defer wg.Done()
420 if err := process(item); err != nil {
421 errors <- err
422 }
423 }()
424 }
425
426 // Wait in separate goroutine
427 go func() {
428 wg.Wait()
429 close(errors) // Sender closes channel
430 }()
431
432 // Collect errors
433 for err := range errors {
434 return err // Return first error
435 }
436
437 return nil
438}
439
440// Bad: Goroutine leak
441func badProcess(items []Item) {
442 for _, item := range items {
443 go func() {
444 process(item) // Captures loop variable - BUG!
445 // No coordination, no error handling, no way to stop
446 }()
447 }
448 // Function returns immediately, goroutines may still be running
449}
450```
451
452### Interfaces
453```go
454// Good: Small, focused interfaces
455type Reader interface {
456 Read(p []byte) (n int, err error)
457}
458
459type Closer interface {
460 Close() error
461}
462
463type ReadCloser interface {
464 Reader
465 Closer
466}
467
468// Define where used, not where implemented
469type DataStore interface {
470 Save(data []byte) error
471}
472
473func SaveToStore(store DataStore, data []byte) error {
474 return store.Save(data)
475}
476
477// Bad: Large, unfocused interface
478type Database interface {
479 GetUser(id string) (*User, error)
480 CreateUser(u *User) error
481 UpdateUser(u *User) error
482 DeleteUser(id string) error
483 GetOrder(id string) (*Order, error)
484 CreateOrder(o *Order) error
485 // ... 20+ more methods
486}
487```
488
489## Best Practices
490
491### Do
492- Always run gofmt before committing
493- Check all errors explicitly
494- Use defer for cleanup operations
495- Keep interfaces small
496- Accept interfaces, return structs
497- Use pointer receivers for large structs or when modifying
498- Close channels from the sender side
499- Use context for cancellation and timeouts
500- Wrap errors with %w for error chains
501- Use sync.WaitGroup to coordinate goroutines
502
503### Don't
504- Don't ignore gofmt warnings
505- Don't use panic in library code
506- Don't ignore errors with _
507- Don't mix pointer and value receivers
508- Don't capture loop variables in goroutines without copying
509- Don't send on closed channels
510- Don't use naked returns in long functions
511- Don't create large interfaces
512- Don't share memory without synchronization
513- Don't use new() for slices, maps, or channels
514
515## Resources
516
517- **Effective Go Principles**: See `resources/effective-go-principles.json` for complete definitions
518- **Checklist**: See `CHECKLIST.md` for full anti-pattern list
519- **Official Guide**: https://go.dev/doc/effective_go
520- **Go Code Review Comments**: https://go.dev/wiki/CodeReviewComments
521
522## Workflow Integration
523
524This skill can be:
525- Invoked from `/go-review` command
526- Used by `go-analyzer` agent for autonomous analysis
527- Triggered by pre-commit hooks to check for anti-patterns
528- Called from other skills for Go code improvements