Skip to main content
🎓 Claude Code Masterclass Learn AI-assisted development on Udemy — plus the companion book on Leanpub & Amazon. Start Learning
Jesús Espino presenting on the Go select statement at the Go Amsterdam meetup, JetBrains office
Programming

Go Meetup Amsterdam: A Deep Dive into the select Statement

Notes from Jesús Espino's Go Amsterdam talk on the select statement, channel multiplexing, timer pitfalls, cancellation, and preventing goroutine leaks.

LB
Luca Berton
· 1 min read

Tonight I attended the Go Amsterdam meetup at the JetBrains office in Amsterdam, and one talk stood out for anyone who writes concurrent Go: Jesús Espino’s “Deep dive into the select statement.” The select statement is one of those features every Gopher uses but few dig into past the first example, and Jesús took it apart from the ground up.

Why select Matters

At its core, select lets a goroutine wait on multiple channel operations at once. It is the concurrency primitive that turns Go’s “share memory by communicating” philosophy into something you can actually build on — multiplexing events, fanning in streams, and building responsive servers.

The shape is deliberately close to a switch:

select {
case v := <-ch1:
    fmt.Println("from ch1:", v)
case ch2 <- 42:
    fmt.Println("sent to ch2")
default:
    fmt.Println("no channel ready")
}

But the semantics are where the depth lives.

What Jesús Covered

A few points from the talk that are easy to get wrong in production:

  • Random fair selection. When several cases are ready, Go picks one at random rather than top-to-bottom. That fairness is a feature — it prevents accidental priority starvation — but it also means you cannot rely on case order.
  • The default case makes select non-blocking. Without it, select blocks until at least one channel is ready. With it, the statement returns immediately if nothing is ready, which is the backbone of polling loops and timeouts.
  • time.After inside a select can leak. A common gotcha: case <-time.After(...) allocates a new timer on every iteration. In a long-lived loop that never fires, those timers pile up. Prefer a reusable time.NewTimer / time.NewTicker you reset or stop.
  • nil channels disable a case. Setting a channel variable to nil removes its case from consideration — a neat trick for dynamically enabling or disabling branches without restructuring the select.
  • context.Context is the clean way to cancel. case <-ctx.Done(): is how you bail out of a select when the work is no longer needed, avoiding goroutine leaks.

The recurring theme: select is simple to read and easy to misuse. Most “my goroutine is stuck / my memory is climbing” bugs in concurrent Go trace back to a select that never had a way out.

Takeaway

If you write Go services, the select statement is where responsiveness and correctness meet. Treat every select as a small state machine: make sure every branch can exit, prefer context for cancellation, and watch timer allocations in hot loops. Jesús’s deep dive was a good reminder that the language’s most elegant concurrency primitive rewards actually understanding it.

#Go #Golang #meetup #Amsterdam #JetBrains #concurrency #channels #select #goroutines
Share:
Free Consultation

Need help implementing this?

I help enterprises design AI infrastructure, Kubernetes platforms, and automation strategies. Free 30-minute discovery call.

Luca Berton — AI & Cloud Advisor, Docker Captain

Luca Berton

AI & Cloud Advisor · Docker Captain · KubeCon Speaker

15+ years in enterprise infrastructure. Author of 8 technical books, creator of Ansible Pilot (1M+ YouTube views, 648K site users). Former Red Hat engineer. Speaker at KubeCon EU 2026 and Red Hat Summit 2026.

Free 30-min AI & Cloud consultation

Book Now