{"id":"c729cb28-b265-49ed-9a82-14c0d64141c5","shortId":"hVXV79","kind":"skill","title":"golang-concurrency","tagline":"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","description":"**Persona:** You are a Go concurrency engineer. You assume every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.\n\n**Modes:**\n\n- **Write mode** — implement concurrent code (goroutines, channels, sync primitives, worker pools, pipelines). Follow the sequential instructions below.\n- **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.\n- **Audit mode** — auditing existing concurrent code across a codebase. Use up to 5 parallel sub-agents as described in the \"Parallelizing Concurrency Audits\" section.\n\n> **Community default.** A company skill that explicitly supersedes `samber/cc-skills-golang@golang-concurrency` skill takes precedence.\n\n# Go Concurrency Best Practices\n\nGo'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.\n\n## Core Principles\n\n1. **Every goroutine must have a clear exit** — without a shutdown mechanism (context, done channel, WaitGroup), they leak and accumulate until the process crashes\n2. **Share memory by communicating** — channels transfer ownership explicitly; mutexes protect shared state but make ownership implicit\n3. **Send copies, not pointers** on channels — sending pointers creates invisible shared memory, defeating the purpose of channels\n4. **Only the sender closes a channel** — closing from the receiver side panics if the sender writes after close\n5. **Specify channel direction** (`chan<-`, `<-chan`) — the compiler prevents misuse at build time\n6. **Default to unbuffered channels** — larger buffers mask backpressure; use them only with measured justification\n7. **Always include `ctx.Done()` in select** — without it, goroutines leak after caller cancellation\n8. **Never use `time.After` in loops** — each call creates a timer that lives until it fires, accumulating memory. Use `time.NewTimer` + `Reset`\n9. **Track goroutine leaks in tests** with `go.uber.org/goleak`\n\nFor detailed channel/select code examples, see [Channels and Select Patterns](references/channels-and-select.md).\n\n## Channel vs Mutex vs Atomic\n\n| Scenario | Use | Why |\n| --- | --- | --- |\n| Passing data between goroutines | Channel | Communicates ownership transfer |\n| Coordinating goroutine lifecycle | Channel + context | Clean shutdown with select |\n| Protecting shared struct fields | `sync.Mutex` / `sync.RWMutex` | Simple critical sections |\n| Simple counters, flags | `sync/atomic` | Lock-free, lower overhead |\n| Many readers, few writers on a map | `sync.Map` | Optimized for read-heavy workloads. **Concurrent map read/write causes a hard crash** |\n| Caching expensive computations | `sync.Once` / `singleflight` | Execute once or deduplicate |\n\n## WaitGroup vs errgroup\n\n| Need | Use | Why |\n| --- | --- | --- |\n| Wait for goroutines, errors not needed | `sync.WaitGroup` | Fire-and-forget |\n| Wait + collect first error | `errgroup.Group` | Error propagation |\n| Wait + cancel siblings on first error | `errgroup.WithContext` | Context cancellation on error |\n| Wait + limit concurrency | `errgroup.SetLimit(n)` | Built-in worker pool |\n\n## Sync Primitives Quick Reference\n\n| Primitive | Use case | Key notes |\n| --- | --- | --- |\n| `sync.Mutex` | Protect shared state | Keep critical sections short; never hold across I/O |\n| `sync.RWMutex` | Many readers, few writers | Never upgrade RLock to Lock (deadlock) |\n| `sync/atomic` | Simple counters, flags | Prefer typed atomics (Go 1.19+): `atomic.Int64`, `atomic.Bool` |\n| `sync.Map` | Concurrent map, read-heavy | No explicit locking; use `RWMutex`+map when writes dominate |\n| `sync.Pool` | Reuse temporary objects | Always `Reset()` before `Put()`; reduces GC pressure |\n| `sync.Once` | One-time initialization | Go 1.21+: `OnceFunc`, `OnceValue`, `OnceValues` |\n| `sync.WaitGroup` | Wait for goroutine completion | `Add` before `go`; Go 1.24+: `wg.Go()` simplifies usage |\n| `x/sync/singleflight` | Deduplicate concurrent calls | Cache stampede prevention |\n| `x/sync/errgroup` | Goroutine group + errors | `SetLimit(n)` replaces hand-rolled worker pools |\n\nFor detailed examples and anti-patterns, see [Sync Primitives Deep Dive](references/sync-primitives.md).\n\n## Concurrency Checklist\n\nBefore spawning a goroutine, answer:\n\n- [ ] **How will it exit?** — context cancellation, channel close, or explicit signal\n- [ ] **Can I signal it to stop?** — pass `context.Context` or done channel\n- [ ] **Can I wait for it?** — `sync.WaitGroup` or `errgroup`\n- [ ] **Who owns the channels?** — creator/sender owns and closes\n- [ ] **Should this be synchronous instead?** — don't add concurrency without measured need\n\n## Pipelines and Worker Pools\n\nFor pipeline patterns (fan-out/fan-in, bounded workers, generator chains, Go 1.23+ iterators, `samber/ro`), see [Pipelines and Worker Pools](references/pipelines.md).\n\n## Parallelizing Concurrency Audits\n\nWhen auditing concurrency across a large codebase, use up to 5 parallel sub-agents (Agent tool):\n\n1. Find all goroutine spawns (`go func`, `go method`) and verify shutdown mechanisms\n2. Search for mutable globals and shared state without synchronization\n3. Audit channel usage — ownership, direction, closure, buffer sizes\n4. Find `time.After` in loops, missing `ctx.Done()` in select, unbounded spawning\n5. Check mutex usage, `sync.Map`, atomics, and thread-safety documentation\n\n## Common Mistakes\n\n| Mistake | Fix |\n| --- | --- |\n| Fire-and-forget goroutine | Provide stop mechanism (context, done channel) |\n| Closing channel from receiver | Only the sender closes |\n| `time.After` in hot loop | Reuse `time.NewTimer` + `Reset` |\n| Missing `ctx.Done()` in select | Always select on context to allow cancellation |\n| Unbounded goroutine spawning | Use `errgroup.SetLimit(n)` or semaphore |\n| Sharing pointer via channel | Send copies or immutable values |\n| `wg.Add` inside goroutine | Call `Add` before `go` — `Wait` may return early otherwise |\n| Forgetting `-race` in CI | Always run `go test -race ./...` |\n| Mutex held across I/O | Keep critical sections short |\n\n## Cross-References\n\n- -> See `samber/cc-skills-golang@golang-performance` skill for false sharing, cache-line padding, `sync.Pool` hot-path patterns\n- -> See `samber/cc-skills-golang@golang-context` skill for cancellation propagation and timeout patterns\n- -> See `samber/cc-skills-golang@golang-safety` skill for concurrent map access and race condition prevention\n- -> See `samber/cc-skills-golang@golang-troubleshooting` skill for debugging goroutine leaks and deadlocks\n- -> See `samber/cc-skills-golang@golang-design-patterns` skill for graceful shutdown patterns\n- -> See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines\n\n## References\n\n- [Go Concurrency Patterns: Pipelines](https://go.dev/blog/pipelines)\n- [Effective Go: Concurrency](https://go.dev/doc/effective_go#concurrency)","tags":["golang","concurrency","skills","samber","agent","agent-skills","antigravity","claude","claude-code","code","codex","coding"],"capabilities":["skill","source-samber","skill-golang-concurrency","topic-agent","topic-agent-skills","topic-antigravity","topic-claude","topic-claude-code","topic-code","topic-codex","topic-coding","topic-copilot","topic-cursor","topic-gemini","topic-gemini-cli-extension"],"categories":["cc-skills-golang"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/samber/cc-skills-golang/golang-concurrency","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add samber/cc-skills-golang","source_repo":"https://github.com/samber/cc-skills-golang","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 1725 github stars · SKILL.md body (7,196 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:00.194Z","embedding":null,"createdAt":"2026-04-18T20:31:39.020Z","updatedAt":"2026-05-18T18:53:00.194Z","lastSeenAt":"2026-05-18T18:53:00.194Z","tsv":"'/blog/pipelines)':944 '/doc/effective_go#concurrency)':950 '/fan-in':30,672 '/goleak':351 '1':202,707 '1.19':521 '1.21':556 '1.23':678 '1.24':569 '2':226,720 '3':243,730 '4':261,739 '5':124,280,700,750 '6':293 '7':308 '8':321 '9':342 'access':890 'accumul':221,337 'across':118,500,693,842 'add':565,657,823 'agent':128,704,705 'ai':928 'ai-driven':927 'allow':800 'also':32 'alway':309,543,795,835 'answer':611 'anti':597 'anti-pattern':596 'assum':50 'atom':367,519,755 'atomic.bool':523 'atomic.int64':522 'audit':112,114,135,689,691,731 'autom':926 'backpressur':301 'best':154 'bound':673 'buffer':299,737 'build':291 'built':161,477 'built-in':476 'cach':427,577,861 'cache-lin':860 'call':328,576,822 'caller':319 'cancel':320,461,468,617,801,876 'case':487 'caus':423 'chain':676 'chan':284,285 'chang':93 'channel':17,41,74,165,216,231,249,260,267,282,297,358,363,375,382,618,633,645,732,775,777,813 'channel/select':354 'cheap':168 'check':98,751 'checklist':606 'ci':834,933 'clean':384 'clear':191,208 'close':265,268,279,619,649,776,783 'closur':736 'code':14,72,92,117,355,930 'codebas':120,696 'collect':454 'come':64 'common':761 'communic':230,376 'communiti':137 'compani':140 'compil':287 'complet':564 'comput':429 'concurr':3,5,12,47,71,91,116,134,148,153,158,186,420,473,525,575,605,658,688,692,888,939,947 'condit':40,893 'context':103,214,383,467,616,773,798,873 'context.context':630 'continu':922 'coordin':379 'copi':245,815 'core':200 'correct':59 'counter':398,515 'crash':225,426 'creat':252,329 'creator/sender':646 'critic':395,495,845 'cross':849 'cross-refer':848 'ctx.done':311,745,792 'data':372 'deadlock':512,906 'debug':902 'dedupl':435,574 'deep':602 'default':138,294 'defeat':256 'describ':130 'design':911 'detail':353,593 'detect':36 'diff':97 'direct':283,735 'dive':603 'document':760 'domin':538 'done':215,632,774 'driven':929 'earli':829 'effect':945 'engin':48 'errgroup':22,438,641 'errgroup.group':457 'errgroup.setlimit':474,806 'errgroup.withcontext':466 'error':198,445,456,458,465,470,583 'everi':51,172,187,203 'exampl':356,594 'execut':432 'exist':115 'exit':195,209,615 'expens':428 'explicit':143,234,531,621 'fals':858 'fan':28,670 'fan-out':27,669 'field':391 'find':708,740 'fire':336,450,766 'fire-and-forget':449,765 'first':455,464 'fix':764 'flag':399,516 'focus':94 'follow':80 'forget':452,768,831 'free':171,403 'freedom':63 'func':713 'gc':548 'generat':675 'global':724 'go':13,46,152,156,520,555,567,568,677,712,714,825,837,938,946 'go.dev':943,949 'go.dev/blog/pipelines)':942 'go.dev/doc/effective_go#concurrency)':948 'go.uber.org':350 'go.uber.org/goleak':349 'goal':183 'golang':2,4,147,854,872,884,898,910,921 'golang-concurr':1,146 'golang-context':871 'golang-continuous-integr':920 'golang-design-pattern':909 'golang-perform':853 'golang-safeti':883 'golang-troubleshoot':897 'goroutin':16,37,52,73,100,163,166,173,188,204,316,344,374,380,444,563,581,610,710,769,803,821,903 'grace':915 'group':582 'guidelin':936 'hand':588 'hand-rol':587 'hard':425 'heavi':418,529 'held':841 'hold':499 'hot':786,866 'hot-path':865 'i/o':501,843 'immut':817 'implement':70 'implicit':242 'includ':310 'initi':554 'insid':820 'instead':654 'instruct':83 'integr':923 'invis':253 'involv':15 'iter':679 'justif':307 'keep':494,844 'key':488 'larg':695 'larger':298 'leak':38,62,101,219,317,345,904 'leak-freedom':61 'liabil':55 'lifecycl':381 'limit':472 'line':862 'live':333 'lock':19,402,511,532 'lock-fre':401 'loop':326,743,787 'lower':404 'make':240 'manag':181 'mani':406,503 'map':412,421,526,535,889 'mask':300 'may':827 'measur':306,660 'mechan':213,719,772 'memori':228,255,338 'method':715 'miss':102,744,791 'mistak':762,763 'misus':289 'mode':67,69,86,113 'model':159 'must':180,205 'mutabl':723 'mutex':235,365,752,840 'n':475,585,807 'necessari':58 'need':439,447,661 'never':322,498,507 'note':489 'object':542 'oncefunc':557 'oncevalu':558,559 'one':552 'one-tim':551 'optim':414 'otherwis':830 'overhead':405 'own':643,647 'owner':192 'ownership':105,233,241,377,734 'pad':863 'panic':273 'parallel':125,133,687,701 'pass':371,629 'path':867 'pattern':6,361,598,668,868,880,912,917,940 'perform':66,855 'persona':42 'pipelin':31,79,662,667,682,941 'pointer':247,251,811 'pool':25,78,480,591,665,685 'pr':89 'practic':155 'preced':151 'predict':194 'prefer':517 'pressur':549 'prevent':288,579,894 'primit':21,76,482,485,601 'principl':201 'process':224 'propag':104,199,459,877 'proper':197 'protect':236,388,491 'proven':57 'provid':770 'purpos':258 'put':546 'quick':483 'race':39,832,839,892 'read':417,528 'read-heavi':416,527 'read/write':422 'reader':407,504 'receiv':271,779 'reduc':547 'refer':484,850,937 'references/channels-and-select.md':362 'references/pipelines.md':686 'references/sync-primitives.md':604 'replac':586 'reset':341,544,790 'resourc':178 'return':828 'reus':540,788 'review':11,85,87,931 'rlock':509 'roll':589 'run':836 'rwmutex':534 'safeti':759,885 'samber/cc-skills-golang':145,852,870,882,896,908,919 'samber/ro':680 'scenario':368 'search':721 'section':136,396,496,846 'see':357,599,681,851,869,881,895,907,918 'select':18,313,360,387,747,794,796 'semaphor':809 'send':244,250,814 'sender':264,276,782 'sequenti':82,111 'setlimit':584 'share':109,227,237,254,389,492,726,810,859 'short':497,847 'shutdown':212,385,718,916 'sibl':462 'side':272 'signal':622,625 'simpl':394,397,514 'simplifi':571 'singleflight':23,431 'size':738 'skill':141,149,856,874,886,900,913,924 'skill-golang-concurrency' 'source-samber' 'spawn':175,608,711,749,804 'specifi':281 'stamped':578 'state':110,238,493,727 'stop':628,771 'struct':390 'structur':185 'sub':127,703 'sub-ag':126,702 'supersed':144 'sync':20,75,481,600 'sync.map':413,524,754 'sync.mutex':392,490 'sync.once':430,550 'sync.pool':539,864 'sync.rwmutex':393,502 'sync.waitgroup':448,560,639 'sync/atomic':400,513 'synchron':653,729 'take':150 'temporari':541 'test':347,838 'thread':758 'thread-safeti':757 'time':292,553 'time.after':324,741,784 'time.newtimer':340,789 'timeout':879 'timer':331 'tool':706 'topic-agent' 'topic-agent-skills' 'topic-antigravity' 'topic-claude' 'topic-claude-code' 'topic-code' 'topic-codex' 'topic-coding' 'topic-copilot' 'topic-cursor' 'topic-gemini' 'topic-gemini-cli-extension' 'track':343 'transfer':232,378 'trigger':33 'troubleshoot':899 'type':518 'unbound':748,802 'unbuff':296 'unprotect':108 'upgrad':508 'usag':572,733,753 'use':7,121,302,323,339,369,440,486,533,697,805,934 'valu':818 'verifi':717 'via':812 'violat':106 'vs':364,366,437 'wait':442,453,460,471,561,636,826 'waitgroup':217,436 'wg.add':819 'wg.go':570 'without':210,314,659,728 'worker':24,77,479,590,664,674,684 'workload':419 'write':9,68,277,537 'writer':409,506 'x/sync/errgroup':580 'x/sync/singleflight':573","prices":[{"id":"2763c93b-cc4a-4b1a-ada9-c6ab4b60dfb5","listingId":"c729cb28-b265-49ed-9a82-14c0d64141c5","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"samber","category":"cc-skills-golang","install_from":"skills.sh"},"createdAt":"2026-04-18T20:31:39.020Z"}],"sources":[{"listingId":"c729cb28-b265-49ed-9a82-14c0d64141c5","source":"github","sourceId":"samber/cc-skills-golang/golang-concurrency","sourceUrl":"https://github.com/samber/cc-skills-golang/tree/main/skills/golang-concurrency","isPrimary":false,"firstSeenAt":"2026-04-18T21:55:04.884Z","lastSeenAt":"2026-05-18T18:53:00.194Z"},{"listingId":"c729cb28-b265-49ed-9a82-14c0d64141c5","source":"skills_sh","sourceId":"samber/cc-skills-golang/golang-concurrency","sourceUrl":"https://skills.sh/samber/cc-skills-golang/golang-concurrency","isPrimary":true,"firstSeenAt":"2026-04-18T20:31:39.020Z","lastSeenAt":"2026-05-07T22:40:26.439Z"}],"details":{"listingId":"c729cb28-b265-49ed-9a82-14c0d64141c5","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"samber","slug":"golang-concurrency","github":{"repo":"samber/cc-skills-golang","stars":1725,"topics":["agent","agent-skills","ai","antigravity","claude","claude-code","code","codex","coding","copilot","cursor","gemini","gemini-cli-extension","openclaw","opencode","plugin","skills","skillsmp","vibe-coding"],"license":"mit","html_url":"https://github.com/samber/cc-skills-golang","pushed_at":"2026-05-18T17:36:00Z","description":"🧑‍🎨 A collection of Golang agentic skills that works","skill_md_sha":"b81fec0727675f67601d01b4f1ea9c4d6dd02916","skill_md_path":"skills/golang-concurrency/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/samber/cc-skills-golang/tree/main/skills/golang-concurrency"},"layout":"multi","source":"github","category":"cc-skills-golang","frontmatter":{"name":"golang-concurrency","license":"MIT","description":"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.","compatibility":"Designed for Claude Code or similar AI coding agents, and for projects using Golang."},"skills_sh_url":"https://skills.sh/samber/cc-skills-golang/golang-concurrency"},"updatedAt":"2026-05-18T18:53:00.194Z"}}