A Go concurrency bug that's easy to miss (and how to catch it) Ran into this in a production serv...A Go concurrency bug that's easy to miss (and how to catch it) Ran into this in a production serv...
The network for creativity
Join 1.25M professional creatives like you
Connect with clients, get discovered, and run your business 100% commission-free
Creatives on Contra have earned over $150M and we are just getting started
A Go concurrency bug that's easy to miss (and how to catch it)
Ran into this in a production service last month — a classic goroutine leak that didn't show up until load testing.
The pattern looked innocent:
func processOrders(orders <-chan Order) {
for order := range orders {
go func() {
handle(order) // bug: order is captured by reference
}()
}
}
Before Go 1.22, order was a shared loop variable — every goroutine could end up processing the same (usually last) order instead of its own. Even after the Go 1.22 fix, it's worth knowing why this broke so many codebases for years.
The fix (pre-1.22 style, still good practice for clarity):
for order := range orders {
order := order // shadow it
go func() {
handle(order)
}()
}
Or simpler — pass it as a parameter:
go func(o Order) {
handle(o)
}(order)
Small thing, but it's the kind of bug that only shows up under concurrent load — never in a quick local test. Worth a lint rule or code review checklist item if your team ships concurrent Go code.
Back to feed
The network for creativity
Join 1.25M professional creatives like you
Connect with clients, get discovered, and run your business 100% commission-free
Creatives on Contra have earned over $150M and we are just getting started