# Go Reviewer

> WHEN: Go project review, error handling, goroutines, interfaces, testing WHAT: Error handling patterns + Concurrency safety + Interface design + Testing + Idiomatic Go WHEN NOT: Go API frameworks → go-api-reviewer, Rust → rust-reviewer

- Skill: `majiayu000/go-reviewer` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/go-reviewer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/go-reviewer/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/go-reviewer

---


# Go Reviewer Skill

## Purpose
Reviews Go code for idiomatic patterns, error handling, concurrency, and best practices.

## When to Use
- Go project code review
- Error handling review
- Goroutine/channel review
- Interface design review
- Go testing patterns

## Project Detection
- `go.mod` in project root
- `.go` files
- `cmd/`, `internal/`, `pkg/` structure
- `*_test.go` test files

## Workflow

### Step 1: Analyze Project
```
**Go Version**: 1.21+
**Module**: github.com/org/project
**Structure**: Standard Go layout
**Testing**: go test / testify
**Linter**: golangci-lint
```

### Step 2: Select Review Areas
**AskUserQuestion:**
```
"Which areas to review?"
Options:
- Full Go review (recommended)
- Error handling patterns
- Concurrency and goroutines
- Interface design
- Testing and benchmarks
multiSelect: true
```

## Detection Rules

### Error Handling
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Ignored error | Always handle errors | CRITICAL |
| err != nil only | Add context with fmt.Errorf | MEDIUM |
| Panic for errors | Return error instead | HIGH |
| No error wrapping | Use %w for wrapping | MEDIUM |

```go
// BAD: Ignored error
data, _ := ioutil.ReadFile("config.json")

// GOOD: Handle error
data, err := os.ReadFile("config.json")
if err != nil {
    return fmt.Errorf("reading config: %w", err)
}

// BAD: No context
if err != nil {
    return err
}

// GOOD: Add context
if err != nil {
    return fmt.Errorf("failed to process user %d: %w", userID, err)
}

// BAD: Panic for recoverable error
func GetUser(id int) *User {
    user, err := db.FindUser(id)
    if err != nil {
        panic(err)  // Don't panic!
    }
    return user
}

// GOOD: Return error
func GetUser(id int) (*User, error) {
    user, err := db.FindUser(id)
    if err != nil {
        return nil, fmt.Errorf("getting user %d: %w", id, err)
    }
    return user, nil
}
```

### Concurrency
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Data race potential | Use mutex or channels | CRITICAL |
| Goroutine leak | Ensure goroutines exit | HIGH |
| Unbuffered channel deadlock | Use buffered or select | HIGH |
| No context cancellation | Pass context.Context | MEDIUM |

```go
// BAD: Data race
type Counter struct {
    count int
}

func (c *Counter) Increment() {
    c.count++  // Race condition!
}

// GOOD: Mutex protection
type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// BAD: Goroutine leak
func process(items []Item) {
    for _, item := range items {
        go processItem(item)  // No way to wait or cancel!
    }
}

// GOOD: WaitGroup and context
func process(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)

    for _, item := range items {
        item := item  // Capture loop variable
        g.Go(func() error {
            return processItem(ctx, item)
        })
    }

    return g.Wait()
}

// BAD: No timeout
resp, err := client.Do(req)

// GOOD: With context timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

req = req.WithContext(ctx)
resp, err := client.Do(req)
```

### Interface Design
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Large interface | Keep interfaces small | MEDIUM |
| Interface in implementation | Define at consumer | MEDIUM |
| Concrete type in signature | Accept interfaces | MEDIUM |
| No interface for testing | Add interface for mocking | MEDIUM |

```go
// BAD: Large interface
type UserService interface {
    GetUser(id int) (*User, error)
    CreateUser(u *User) error
    UpdateUser(u *User) error
    DeleteUser(id int) error
    ListUsers() ([]*User, error)
    SearchUsers(query string) ([]*User, error)
    // ... 20 more methods
}

// GOOD: Small, focused interfaces
type UserGetter interface {
    GetUser(ctx context.Context, id int) (*User, error)
}

type UserCreator interface {
    CreateUser(ctx context.Context, u *User) error
}

// Consumer defines the interface it needs
type UserHandler struct {
    getter UserGetter  // Only what it needs
}

// BAD: Concrete type dependency
func ProcessFile(f *os.File) error {
    // Hard to test
}

// GOOD: Interface dependency
func ProcessFile(r io.Reader) error {
    // Easy to test with strings.Reader, bytes.Buffer, etc.
}
```

### Code Organization
| Check | Recommendation | Severity |
|-------|----------------|----------|
| No package structure | Use cmd/internal/pkg | MEDIUM |
| Exported unnecessary | Keep internal private | LOW |
| Package name mismatch | Match directory name | LOW |
| Circular import | Restructure packages | HIGH |

```
// GOOD: Standard Go project layout
project/
├── cmd/
│   └── server/
│       └── main.go
├── internal/           # Private packages
│   ├── service/
│   │   └── user.go
│   └── repository/
│       └── user.go
├── pkg/               # Public packages
│   └── client/
│       └── client.go
├── go.mod
└── go.sum
```

### Testing
| Check | Recommendation | Severity |
|-------|----------------|----------|
| No table-driven tests | Use test tables | MEDIUM |
| No test helpers | Extract common setup | LOW |
| No benchmarks | Add for hot paths | LOW |
| Mocking concrete types | Use interfaces | MEDIUM |

```go
// GOOD: Table-driven test
func TestParseSize(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    int64
        wantErr bool
    }{
        {"bytes", "100", 100, false},
        {"kilobytes", "1KB", 1024, false},
        {"megabytes", "1MB", 1048576, false},
        {"invalid", "abc", 0, true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ParseSize(tt.input)
            if (err != nil) != tt.wantErr {
                t.Errorf("ParseSize() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("ParseSize() = %v, want %v", got, tt.want)
            }
        })
    }
}

// GOOD: Benchmark
func BenchmarkParseSize(b *testing.B) {
    for i := 0; i < b.N; i++ {
        ParseSize("1MB")
    }
}
```

## Response Template
```
## Go Code Review Results

**Project**: [name]
**Go**: 1.22 | **Linter**: golangci-lint

### Error Handling
| Status | File | Issue |
|--------|------|-------|
| CRITICAL | service.go:45 | Ignored error from db.Query |

### Concurrency
| Status | File | Issue |
|--------|------|-------|
| HIGH | worker.go:23 | Potential goroutine leak |

### Interface Design
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | handler.go:12 | Concrete type in function signature |

### Testing
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | service_test.go | No table-driven tests |

### Recommended Actions
1. [ ] Handle all returned errors
2. [ ] Add context cancellation to goroutines
3. [ ] Define interfaces at consumer side
4. [ ] Convert tests to table-driven format
```

## Best Practices
1. **Errors**: Always handle, wrap with context
2. **Concurrency**: Use context, errgroup, proper sync
3. **Interfaces**: Small, defined at consumer
4. **Testing**: Table-driven, interfaces for mocking
5. **Linting**: Use golangci-lint

## Integration
- `go-api-reviewer`: API framework patterns
- `security-scanner`: Go security audit
- `perf-analyzer`: Go performance profiling

