# Golang Concurrency

> Design or review concurrent Go code involving goroutines, channels, locks, atomics, worker pools, or race and leak symptoms. Use when ownership, synchronization, cancellation, backpressure, or shutdown is central; not for ordinary sequential code that merely accepts a context.

- Skill: `reagin/golang-concurrency` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add reagin/golang-concurrency`
- Raw SKILL.md: https://api.skillmd.com/api/skills/reagin/golang-concurrency/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: reagin (https://skillmd.com/u/reagin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/reagin/golang-concurrency

---


# Go Concurrency

Prioritize correctness, bounded resource use, and understandable ownership. Concurrency is justified by a concrete need such as overlapping I/O, parallel computation, independent lifecycle work, or coordination—not by stylistic preference.

## Inspect before changing

1. Read repository instructions, `go.mod`, nearby synchronization conventions, tests, and shutdown wiring.
2. Trace each relevant goroutine from creation to completion: owner, inputs, outputs, cancellation, errors, panic behavior, and who waits.
3. Identify shared mutable state and the synchronization that establishes a happens-before relationship for every access.
4. Define expected concurrency, queue limits, overload behavior, ordering, and partial-result semantics.
5. Preserve existing lifecycle and API contracts unless the requested task requires changing them.

## Core invariants

- Every spawned goroutine needs a defined owner and completion condition. Expose cancellation or waiting when the surrounding lifecycle must control or observe it.
- Every channel needs a clear protocol: who sends, who receives, whether it closes, and who is allowed to close it. Usually the component that knows no more sends can occur closes it; receivers do not close merely to stop a sender.
- A send of a pointer is not inherently unsafe. The real requirement is explicit ownership, immutability, confinement, or synchronization for the pointed-to data.
- Add a cancellation case when a potentially blocking operation belongs to cancellable work. A `ctx.Done()` arm is not useful in a select whose operation must complete regardless of caller cancellation.
- Concurrency and queues should be bounded when input can outpace service. Define what happens at capacity: block, reject, drop, coalesce, or spill.
- Do not hold a lock while calling unknown code or performing slow I/O unless the protected invariant truly requires it and the consequence is documented.
- Closing a channel is a broadcast about future sends, not a general resource cleanup mechanism. Nil channels block forever; a receive from a closed channel yields the zero value and `ok == false`.

For channel protocols and cancellation details, read [channels and select](references/channels-and-select.md).

## Choose the simplest primitive

| Need | Usual starting point |
| --- | --- |
| Guard related fields or a multi-step invariant | `sync.Mutex` |
| Coordinate ownership or stream values | Channel |
| Wait for tasks that do not return errors | `sync.WaitGroup` |
| Propagate errors/cancel siblings and the project already uses it | `errgroup` |
| Independent numeric flag or counter with a precise atomic invariant | Typed `sync/atomic` value |
| One-time initialization | `sync.Once` or a supported convenience wrapper |
| Specialized concurrent map access | `sync.Map`, only after its documented use cases fit |

Do not choose `RWMutex`, atomics, `sync.Map`, or `sync.Pool` from a generic performance claim. Their benefit and complexity depend on access patterns; profile or benchmark hot paths. See [synchronization primitives](references/sync-primitives.md).

## Pipelines and worker pools

Use a pipeline only when stages have useful independent lifecycles or concurrency. For an in-process transformation that is naturally sequential, a loop or iterator is simpler. When concurrency is warranted, ensure cancellation reaches blocked receives and sends, output is closed exactly once, worker count is bounded, and error semantics are explicit. See [pipelines and worker pools](references/pipelines.md).

## Verification

Exercise the repository's focused tests first. When scope and runtime make it practical, run the race detector on affected packages:

```bash
go test -race ./path/to/affected/...
```

The race detector finds executed data races, not deadlocks, leaks, logical races, or untested paths. Add deterministic tests around shutdown, cancellation, capacity, and error paths; avoid sleep-based timing where a channel or barrier can synchronize the test.

In review, report the concrete interleaving and consequence. “Could race” is incomplete without identifying the unsynchronized accesses or violated protocol.

