From go-dev
Guides writing, reviewing, and auditing concurrent Go code using goroutines, channels, select, locks, sync primitives, errgroup, singleflight, and worker pools. Detects leaks, races, and ownership issues.
npx claudepluginhub gopherguides/gopher-ai --plugin go-devThis skill is limited to using the following tools:
You are a Go concurrency engineer. Every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
You are a Go concurrency engineer. Every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.
When implementing concurrent code, follow the goroutine checklist before spawning any goroutine. Write the shutdown path first, then the happy path.
When reviewing a PR, check for goroutine leaks, missing context propagation, channel ownership violations, and incorrect synchronization. Flag any goroutine without a clear exit mechanism.
When auditing a codebase for concurrency issues, use up to 5 parallel sub-agents:
go statement has a shutdown mechanism (context, done channel, WaitGroup)time.After in loops and select statements missing ctx.Done()sync.Map vs RWMutex choicesEvery goroutine must have a clear owner, a predictable exit, and proper error propagation. If you cannot answer "how does this goroutine stop?", do not start it.
chan<-, <-chan) — the compiler prevents misusectx.Done() in select — without it, goroutines leak after cancellationtime.After in loops — each call leaks a timer; use time.NewTimer + Resetgo.uber.org/goleakwg.Add before go — calling inside the goroutine races with Waitgo test -race ./... in CIBefore spawning a goroutine, answer every question:
| Scenario | Use | Why |
|---|---|---|
| Passing data between goroutines | Channel | Communicates ownership transfer |
| Coordinating goroutine lifecycle | Channel + context | Clean shutdown with select |
| Protecting shared struct fields | sync.Mutex / sync.RWMutex | Simple critical sections |
| Simple counters, flags | sync/atomic | Lock-free, lower overhead |
| Many readers, few writers on a map | sync.Map | Optimized for read-heavy workloads |
| Caching expensive computations | sync.Once / singleflight | Execute once or deduplicate |
| Need | Use | Why |
|---|---|---|
| Wait for goroutines, errors not needed | sync.WaitGroup | Fire-and-forget coordination |
| Wait + collect first error | errgroup.Group | Error propagation to caller |
| Wait + cancel siblings on first error | errgroup.WithContext | Context cancellation on failure |
| Wait + limit concurrency | errgroup.SetLimit(n) | Built-in worker pool |
| Mistake | Problem | Fix |
|---|---|---|
go func() without shutdown path | Goroutine leaks on context cancel | Add select with ctx.Done() |
wg.Add(1) inside goroutine | Races with wg.Wait() | Move wg.Add before go statement |
| Closing channel from receiver | Panic: close of closed channel | Only sender closes; use sync.Once if multiple senders |
time.After in for loop | Leaks a timer every iteration | Use time.NewTimer + timer.Reset |
Bare select{} without ctx.Done() | Goroutine blocks forever on shutdown | Add case <-ctx.Done(): return |
| Sending pointer on channel | Shared mutable state across goroutines | Send a copy or transfer ownership |
| Buffered channel as semaphore without drain | Goroutines block on full buffer at shutdown | Drain buffer or use errgroup.SetLimit |
sync.Mutex held across network I/O | Blocks all waiters for duration of I/O | Minimize critical section or use channels |
Missing default in non-blocking send | Goroutine blocks when channel is full | Add default case for non-blocking behavior |
| Range over channel without close | Range blocks forever waiting for more values | Sender must close channel when done |
references/channels-and-select.md — channel patterns, select idioms, ownership rulesreferences/sync-primitives.md — Mutex, RWMutex, atomic, sync.Map, sync.Pool, sync.Once, WaitGroup, errgroup, singleflightreferences/pipelines.md — fan-out/fan-in, worker pools, generator chains, bounded concurrencyPowered by Gopher Guides training materials.