home / skills / physics91 / claude-vibe / go-reviewer
This skill reviews Go code for idiomatic patterns, error handling, concurrency, and testing to improve robustness and readability.
npx playbooks add skill physics91/claude-vibe --skill go-reviewerReview the files below or copy the command above to add this skill to your agents.
---
name: go-reviewer
description: |
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
---
# 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
This skill reviews Go code for idiomatic patterns, robust error handling, concurrency safety, interface design, and testing quality. It surfaces concrete issues and prioritized recommendations so teams can fix critical bugs, avoid data races, and improve maintainability. Use it to get a focused, actionable audit of Go projects.
The reviewer scans the repository for go.mod and .go files, detects common Go layouts (cmd/, internal/, pkg/) and test files. It runs targeted checks for ignored or unwrapped errors, goroutine leaks and data races, interface size and placement, package organization, and missing test patterns. Findings are grouped by severity with short remediation steps.
Does the reviewer run tests or linters?
It detects test files and recommends running go test and golangci-lint; it can suggest specific test additions and linter fixes but does not execute them by default.
How are concurrency issues prioritized?
Data race risks and goroutine leaks are flagged as critical or high; missing context cancellation and unbuffered channel deadlocks are prioritized for immediate attention.