Persona: You are a Go concurrency engineer. You assume every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.
Modes:
- Write mode — implement concurrent code (goroutines, channels, sync primitives, worker pools, pipelines). Follow the sequential instructions below.
- Review mode — reviewing a PR's concurrent code changes. Focus on the diff: check for goroutine leaks, missing context propagation, ownership violations, and unprotected shared state. Sequential.
- Audit mode — auditing existing concurrent code across a codebase. Use up to 5 parallel sub-agents as described in the "Parallelizing Concurrency Audits" section.
Community default. A company skill that explicitly supersedes samber/cc-skills-golang@golang-concurrency skill takes precedence.
Go Concurrency Best Practices
Go's concurrency model is built on goroutines and channels. Goroutines are cheap but not free — every goroutine you spawn is a resource you must manage. The goal is structured concurrency: every goroutine has a clear owner, a predictable exit, and proper error propagation.
Core Principles
- Every goroutine must have a clear exit — without a shutdown mechanism (context, done channel, WaitGroup), they leak and accumulate until the process crashes
- Share memory by communicating — channels transfer ownership explicitly; mutexes protect shared state but make ownership implicit
- Send copies, not pointers on channels — sending pointers creates invisible shared memory, defeating the purpose of channels
- Only the sender closes a channel — closing from the receiver side panics if the sender writes after close
- Specify channel direction (
chan<-, <-chan) — the compiler prevents misuse at build time
- Default to unbuffered channels — larger buffers mask backpressure; use them only with measured justification
- Always include
ctx.Done() in select — without it, goroutines leak after caller cancellation
- Avoid repeated
time.After in hot loops — each call allocates a timer and creates unnecessary churn; use time.NewTimer + Reset for long-running loops
- Track goroutine leaks in tests with
go.uber.org/goleak
For detailed channel/select code examples, see Channels and Select Patterns.
Channel vs Mutex vs Atomic
| 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. Concurrent map read/write causes a hard crash |
| Caching expensive computations |
sync.Once / singleflight |
Execute once or deduplicate |
WaitGroup vs errgroup
| Need |
Use |
Why |
| Wait for goroutines, errors not needed |
sync.WaitGroup |
Fire-and-forget |
| Wait + collect first error |
errgroup.Group |
Error propagation |
| Wait + cancel siblings on first error |
errgroup.WithContext |
Context cancellation on error |
| Wait + limit concurrency |
errgroup.SetLimit(n) |
Built-in worker pool |
Sync Primitives Quick Reference
| Primitive |
Use case |
Key notes |
sync.Mutex |
Protect shared state |
Keep critical sections short; never hold across I/O |
sync.RWMutex |
Many readers, few writers |
Never upgrade RLock to Lock (deadlock) |
sync/atomic |
Simple counters, flags |
Prefer typed atomics (Go 1.19+): atomic.Int64, atomic.Bool |
sync.Map |
Concurrent map, read-heavy |
No explicit locking; use RWMutex+map when writes dominate |
sync.Pool |
Reuse temporary objects |
Always Reset() before Put(); reduces GC pressure |
sync.Once |
One-time initialization |
Go 1.21+: OnceFunc, OnceValue, OnceValues |
sync.WaitGroup |
Waiting for simple goroutines |
Go 1.25+: prefer wg.Go(func(){ ... }) for fire-and-wait tasks that do not panic and do not need error propagation. For Go <1.25 use Add/Done. For errors/cancellation/limits, use errgroup with context. |
x/sync/singleflight |
Deduplicate concurrent calls |
Cache stampede prevention |
x/sync/errgroup |
Goroutine group + errors |
SetLimit(n) replaces hand-rolled worker pools |
For detailed examples and anti-patterns, see Sync Primitives Deep Dive.
Concurrency Checklist
Before spawning a goroutine, answer:
Pipelines and Worker Pools
For pipeline patterns (fan-out/fan-in, bounded workers, generator chains, Go 1.23+ iterators, samber/ro), see Pipelines and Worker Pools.
Parallelizing Concurrency Audits
When auditing concurrency across a large codebase, use up to 5 parallel sub-agents (Agent tool):
- Find all goroutine spawns (
go func, go method) and verify shutdown mechanisms
- Search for mutable globals and shared state without synchronization
- Audit channel usage — ownership, direction, closure, buffer sizes
- Find
time.After in loops, missing ctx.Done() in select, unbounded spawning
- Check mutex usage,
sync.Map, atomics, and thread-safety documentation
Common Mistakes
| Mistake |
Fix |
| Fire-and-forget goroutine |
Provide stop mechanism (context, done channel) |
| Closing channel from receiver |
Only the sender closes |
time.After in hot loop |
Reuse time.NewTimer + Reset |
Missing ctx.Done() in select |
Always select on context to allow cancellation |
| Unbounded goroutine spawning |
Use errgroup.SetLimit(n) or semaphore |
| Sharing pointer via channel |
Send copies or immutable values |
wg.Add inside goroutine |
Call Add before go — Wait may return early otherwise |
Forgetting -race in CI |
Always run go test -race ./... |
| Mutex held across I/O |
Keep critical sections short |
Cross-References
- -> See
samber/cc-skills-golang@golang-performance skill for false sharing, cache-line padding, sync.Pool hot-path patterns
- -> See
samber/cc-skills-golang@golang-context skill for cancellation propagation and timeout patterns
- -> See
samber/cc-skills-golang@golang-safety skill for concurrent map access and race condition prevention
- -> See
samber/cc-skills-golang@golang-troubleshooting skill for debugging goroutine leaks and deadlocks
- -> See
samber/cc-skills-golang@golang-design-patterns skill for graceful shutdown patterns
- -> See
samber/cc-skills-golang@golang-continuous-integration skill for automated AI-driven code review in CI using these guidelines
Go 1.26 experimental goroutine leak profile
For Go 1.26 diagnostics, there is an experimental goroutine leak profile. It is useful for production-oriented leak investigation, but is gated by GOEXPERIMENT=goroutineleakprofile; do not rely on it as default stable behavior.
Typical usage when the experiment is enabled:
curl http://localhost:6060/debug/pprof/goroutineleak?debug=2
go tool pprof http://localhost:6060/debug/pprof/goroutineleak
Keep existing tools:
- tests:
go.uber.org/goleak
- runtime count:
runtime.NumGoroutine()
- stack dump:
/debug/pprof/goroutine?debug=2
- race checks:
go test -race ./...
References
Source: andmitr/ai-tools — distributed by TomeVault.
1---2name: golang-concurrency3description: Golang concurrency patterns. Use when writing or reviewing concurrent Go code involving goroutines, channels, select, locks, sync primitives, errgroup, singleflight, worker pools, or fan-out/fan-in pipelines. Also triggers when you detect goroutine leaks, race conditions, channel ownership issues, or need to choose between channels and mutexes. Use when this capability is needed.4---56**Persona:** You are a Go concurrency engineer. You assume every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.78**Modes:**910- **Write mode** — implement concurrent code (goroutines, channels, sync primitives, worker pools, pipelines). Follow the sequential instructions below.11- **Review mode** — reviewing a PR's concurrent code changes. Focus on the diff: check for goroutine leaks, missing context propagation, ownership violations, and unprotected shared state. Sequential.12- **Audit mode** — auditing existing concurrent code across a codebase. Use up to 5 parallel sub-agents as described in the "Parallelizing Concurrency Audits" section.1314> **Community default.** A company skill that explicitly supersedes `samber/cc-skills-golang@golang-concurrency` skill takes precedence.1516# Go Concurrency Best Practices1718Go's concurrency model is built on goroutines and channels. Goroutines are cheap but not free — every goroutine you spawn is a resource you must manage. The goal is structured concurrency: every goroutine has a clear owner, a predictable exit, and proper error propagation.1920## Core Principles21221. **Every goroutine must have a clear exit** — without a shutdown mechanism (context, done channel, WaitGroup), they leak and accumulate until the process crashes232. **Share memory by communicating** — channels transfer ownership explicitly; mutexes protect shared state but make ownership implicit243. **Send copies, not pointers** on channels — sending pointers creates invisible shared memory, defeating the purpose of channels254. **Only the sender closes a channel** — closing from the receiver side panics if the sender writes after close265. **Specify channel direction** (`chan<-`, `<-chan`) — the compiler prevents misuse at build time276. **Default to unbuffered channels** — larger buffers mask backpressure; use them only with measured justification287. **Always include `ctx.Done()` in select** — without it, goroutines leak after caller cancellation298. **Avoid repeated `time.After` in hot loops** — each call allocates a timer and creates unnecessary churn; use `time.NewTimer` + `Reset` for long-running loops309. **Track goroutine leaks in tests** with `go.uber.org/goleak`3132For detailed channel/select code examples, see [Channels and Select Patterns](channels-and-select.md).3334## Channel vs Mutex vs Atomic3536| Scenario | Use | Why |37| --- | --- | --- |38| Passing data between goroutines | Channel | Communicates ownership transfer |39| Coordinating goroutine lifecycle | Channel + context | Clean shutdown with select |40| Protecting shared struct fields | `sync.Mutex` / `sync.RWMutex` | Simple critical sections |41| Simple counters, flags | `sync/atomic` | Lock-free, lower overhead |42| Many readers, few writers on a map | `sync.Map` | Optimized for read-heavy workloads. **Concurrent map read/write causes a hard crash** |43| Caching expensive computations | `sync.Once` / `singleflight` | Execute once or deduplicate |4445## WaitGroup vs errgroup4647| Need | Use | Why |48| --- | --- | --- |49| Wait for goroutines, errors not needed | `sync.WaitGroup` | Fire-and-forget |50| Wait + collect first error | `errgroup.Group` | Error propagation |51| Wait + cancel siblings on first error | `errgroup.WithContext` | Context cancellation on error |52| Wait + limit concurrency | `errgroup.SetLimit(n)` | Built-in worker pool |5354## Sync Primitives Quick Reference5556| Primitive | Use case | Key notes |57| --- | --- | --- |58| `sync.Mutex` | Protect shared state | Keep critical sections short; never hold across I/O |59| `sync.RWMutex` | Many readers, few writers | Never upgrade RLock to Lock (deadlock) |60| `sync/atomic` | Simple counters, flags | Prefer typed atomics (Go 1.19+): `atomic.Int64`, `atomic.Bool` |61| `sync.Map` | Concurrent map, read-heavy | No explicit locking; use `RWMutex`+map when writes dominate |62| `sync.Pool` | Reuse temporary objects | Always `Reset()` before `Put()`; reduces GC pressure |63| `sync.Once` | One-time initialization | Go 1.21+: `OnceFunc`, `OnceValue`, `OnceValues` |64| `sync.WaitGroup` | Waiting for simple goroutines | Go 1.25+: prefer `wg.Go(func(){ ... })` for fire-and-wait tasks that do not panic and do not need error propagation. For Go <1.25 use `Add`/`Done`. For errors/cancellation/limits, use `errgroup` with context. |65| `x/sync/singleflight` | Deduplicate concurrent calls | Cache stampede prevention |66| `x/sync/errgroup` | Goroutine group + errors | `SetLimit(n)` replaces hand-rolled worker pools |6768For detailed examples and anti-patterns, see [Sync Primitives Deep Dive](sync-primitives.md).6970## Concurrency Checklist7172Before spawning a goroutine, answer:7374- [ ] **How will it exit?** — context cancellation, channel close, or explicit signal75- [ ] **Can I signal it to stop?** — pass `context.Context` or done channel76- [ ] **Can I wait for it?** — `sync.WaitGroup` or `errgroup`77- [ ] **Who owns the channels?** — creator/sender owns and closes78- [ ] **Should this be synchronous instead?** — don't add concurrency without measured need7980## Pipelines and Worker Pools8182For pipeline patterns (fan-out/fan-in, bounded workers, generator chains, Go 1.23+ iterators, `samber/ro`), see [Pipelines and Worker Pools](AI/skills/golang-concurrency/references/pipelines.md).8384## Parallelizing Concurrency Audits8586When auditing concurrency across a large codebase, use up to 5 parallel sub-agents (Agent tool):87881. Find all goroutine spawns (`go func`, `go method`) and verify shutdown mechanisms892. Search for mutable globals and shared state without synchronization903. Audit channel usage — ownership, direction, closure, buffer sizes914. Find `time.After` in loops, missing `ctx.Done()` in select, unbounded spawning925. Check mutex usage, `sync.Map`, atomics, and thread-safety documentation9394## Common Mistakes9596| Mistake | Fix |97| --- | --- |98| Fire-and-forget goroutine | Provide stop mechanism (context, done channel) |99| Closing channel from receiver | Only the sender closes |100| `time.After` in hot loop | Reuse `time.NewTimer` + `Reset` |101| Missing `ctx.Done()` in select | Always select on context to allow cancellation |102| Unbounded goroutine spawning | Use `errgroup.SetLimit(n)` or semaphore |103| Sharing pointer via channel | Send copies or immutable values |104| `wg.Add` inside goroutine | Call `Add` before `go` — `Wait` may return early otherwise |105| Forgetting `-race` in CI | Always run `go test -race ./...` |106| Mutex held across I/O | Keep critical sections short |107108## Cross-References109110- -> See `samber/cc-skills-golang@golang-performance` skill for false sharing, cache-line padding, `sync.Pool` hot-path patterns111- -> See `samber/cc-skills-golang@golang-context` skill for cancellation propagation and timeout patterns112- -> See `samber/cc-skills-golang@golang-safety` skill for concurrent map access and race condition prevention113- -> See `samber/cc-skills-golang@golang-troubleshooting` skill for debugging goroutine leaks and deadlocks114- -> See `samber/cc-skills-golang@golang-design-patterns` skill for graceful shutdown patterns115- -> See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines116117### Go 1.26 experimental goroutine leak profile118119For Go 1.26 diagnostics, there is an experimental goroutine leak profile. It is useful for production-oriented leak investigation, but is gated by `GOEXPERIMENT=goroutineleakprofile`; do not rely on it as default stable behavior.120121Typical usage when the experiment is enabled:122123```bash124curl http://localhost:6060/debug/pprof/goroutineleak?debug=2125go tool pprof http://localhost:6060/debug/pprof/goroutineleak126```127128Keep existing tools:129130- tests: `go.uber.org/goleak`131- runtime count: `runtime.NumGoroutine()`132- stack dump: `/debug/pprof/goroutine?debug=2`133- race checks: `go test -race ./...`134135## References136137- [Go Concurrency Patterns: Pipelines](https://go.dev/blog/pipelines)138- [Effective Go: Concurrency](https://go.dev/doc/effective_go#concurrency)139140---141> Source: [andmitr/ai-tools](https://github.com/andmitr/ai-tools) — distributed by [TomeVault](https://tomevault.io).142<!-- tomevault:4.0:skill_md:2026-06-24 -->