Three ways to think about Go channels

Overall sentiment

  • Many appreciate Go’s channels/goroutines as a powerful, simple concurrency model when used with discipline.
  • Others report that in large codebases, channel-heavy code becomes unreadable and “spaghetti-like,” comparable to misuse of goto.

Channels as a concurrency primitive

  • Unbuffered channels are praised for combining message passing with synchronization and being easy to reason about.
  • Several argue unbuffered should have been the default; buffered channels and mixed usage increase complexity.
  • Channels are described as low-level, multipurpose tools (queue, future, signal), which makes intent harder to read.

Maintainability, misuse, and patterns

  • Common complaints: channels sprinkled everywhere, no clear ownership, senders/receivers spread across files.
  • Recommended best practices:
    • Treat each channel instance as a specific, named role (e.g., a dedicated type alias).
    • Only producers close channels; consumers should not.
    • Prefer single “stop” signaling via context.Context and its Done() channel.
    • Use higher-level patterns and libraries (e.g., sync.WaitGroup, x/sync/errgroup, worker pools) instead of ad‑hoc select/cancel logic.

Channels vs async/await and other models

  • Debate over whether Go should add async/await:
    • Some think async/await and exceptions would simplify common cases.
    • Others argue goroutines + channels are superior and async function-coloring is a major downside.
  • Clarifications that async/await is about cooperative asynchrony; concurrency/parallelism are orthogonal.
  • Comparisons with Rust (borrow checker, async complexity), Erlang/Elixir (actor model, named processes/mailboxes), and CSP origins.

Error handling and exceptions

  • Long subthread on Go’s error values vs panic/recover vs traditional exceptions.
  • Many defend explicit if err != nil as readable and practical for systems programming.
  • Critics say errors are too easy to ignore and lack automatic stack traces; exceptions would force propagation.
  • Consensus that neither exceptions nor Go-style errors “solve” discipline; testing and tooling (linters) remain crucial.

Debugging, observability, and leaks

  • Channels and goroutines are hard to debug: unnamed, anonymous, and difficult to correlate with logs.
  • Goroutine leaks (blocked on never-satisfied channel ops) are cited as a frequent, subtle bug.
  • Suggestions: use “done” channels or contexts for cancellation, add heartbeats/observability for long-lived goroutines, and be wary of libraries spawning hidden goroutines.