Problems with Go channels (2016)
State of Go Channels and CSP Usage
- Many commenters say the 2016 criticisms still hold: the language and channel semantics haven’t changed meaningfully.
- Broad consensus that channels were overhyped early on; experienced Go devs now treat them as a sharp, specialized tool, not a default primitive.
- Some report successful CSP-style systems, but only in tightly controlled topologies with good design docs and few developers.
Main Problems Identified
- Lifecycle and shutdown: coordinating when to close shared channels and tear down goroutines is error‑prone, especially with multiple producers.
- Deadlocks and “dead goroutines”: hard to reason about when everything is wired via channels; control flow becomes a hidden graph rather than stack calls.
- API design: using channels in exported interfaces is widely discouraged; it leaks concurrency concerns and makes mocking/maintenance harder.
- Semantics:
close,nilchannels, andrangeover channels are seen as inconsistent or “cray-cray,” leading to subtle bugs. - CSP purity (channels for everything) usually degenerates into ad‑hoc “shutdown” channels and complex cancellation logic.
Suggested Best Practices and Alternatives
- Prefer mutexes, atomics, and
sync.WaitGroup/errgroupfor shared state or coordination; use channels mainly for signaling and simple work queues. - Hide channels inside modules; expose synchronous APIs instead.
- Rule of thumb: writer owns
close, but multi-writer channels make this hard; many recommend avoiding that pattern entirely. - Use
context.Contextor explicit counters/flags for cancellation and lifecycle management instead of elaborate channel schemes.
Performance and Buffering
- Disagreement over performance: some claim channels are slower because they use mutexes; others show benchmarks where channels beat
sync.Mutexunder heavy contention. - Buffered channels are called a major footgun: they remain blocking and large buffers are often a misguided “optimization” that masks deadlocks.
Comparisons and Meta‑Discussion
- Several compare Go’s channels unfavorably with Erlang/Elixir (unbounded queues, supervision) and Rust async channels (no “dead goroutine” GC issues).
- Broader critique: Go’s design is seen by some as simplistic and dismissive of PL expertise; others argue its simplicity and success in real systems vindicate the choices.