Go Concurrency Distilled is a new mini-book that turns Go’s concurrency primitives into a set of interactive examples you can change and run right on the page. The book covers goroutines, channels, select, wait groups, mutexes, semaphores and more, each with live code snippets you can edit and execute. There’s also a PDF version with static examples for printing or reading offline.
The book is described as a quick refresher, not a beginner’s guide. For a deeper dive, the author points readers to Gist of Go: Concurrency, a separate book with practical exercises. The whole project is AI-free, which the author notes explicitly.
Goroutines and Wait Groups
Goroutines are the building blocks of Go concurrency. You start one with the go keyword, and the Go runtime juggles them across operating system threads running on CPU cores. Goroutines are lighter than OS threads, so you can create hundreds or thousands of them without much overhead.
In the example above, two goroutines print “worker 1” and “worker 2” after being launched. A wait group (sync.WaitGroup) tracks their completion. The Add method sets the counter, Done decrements it, and Wait blocks until the counter reaches zero. That keeps the main function from exiting until both workers are finished.
There’s also a WaitGroup.Go method that combines the setup. It increments the counter, runs the function in a goroutine, and decrements the counter when the function ends.
Channels and Channel Directions
Channels let goroutines pass values to each other. A channel is a window where one goroutine throws a value and another catches it. Sending a value through a channel is synchronous: the sending goroutine blocks until the receiving goroutine takes the value.
The book shows a simple example where a goroutine sends “ping” to a channel, and the main function reads it back. The sender blocks on ch <- val, and the receiver blocks on <-ch. Only when the value is received does the sending goroutine continue.
When a channel is no longer needed, Go’s garbage collector frees its resources automatically, whether it’s closed or not. Closing a channel signals to readers that all data has been sent. The reader checks the channel’s status with a second value, called the “comma OK” status.
Here’s how the reader checks:
go
num, ok := <-in
if !ok {
break
}
If ok is false, the channel is closed and the loop stops. If ok is true, the reader gets the next value.
Directional Channels and Buffered Channels
Channels can be bidirectional, send-only, or receive-only. Bidirectional channels are the default. Send-only channels can be declared with chan<- int, and receive-only channels are declared with <-chan int.
You can’t read from a send-only channel or write to a receive-only channel, and you can’t close a receive-only channel. Setting the direction protects against accidental errors.
Buffered channels act like a FIFO queue with a fixed-size buffer. Writing to a buffered channel doesn’t block as long as the buffer has room, and reading doesn’t block as long as the buffer holds values.
The cap and len functions work on buffered channels. cap shows the buffer size, and len shows how many values are currently stored.
Reading from a closed buffered channel works the same way as a regular channel. It returns values from the buffer until the buffer is empty, then returns a zero value with ok set to false.
What This Book Offers
The interactive format is the draw. Instead of reading about concurrency, you can change the code and see the results immediately. That matters for a topic where the timing and ordering of events are often hard to grasp from text alone.
The book is also notable for its absence of AI involvement. In a field where automated tools are everywhere, this is a deliberate choice by the author, and it’s worth noting alongside the technical content.
| Topic | Key Detail |
|---|---|
| Goroutines | Started with the go keyword; tracked by sync.WaitGroup |
| Channels | Values passed synchronously; closed with close(ch) |
| Buffered channels | FIFO queue with fixed buffer size; use cap and len |
| Directional channels | chan<- for send-only, <-chan for receive-only |
The book is a fast read for anyone who already knows Go but wants to tighten their understanding of concurrency. It’s not a substitute for the full Gist of Go: Concurrency, which the author directs beginners toward, but it is a useful companion for the experienced developer looking for a quick refresher.
The interactive examples are the star. Changing a goroutine’s logic and seeing the output shift in seconds is faster than reading through a wall of prose.
The book is a welcome addition to the Go ecosystem. It makes a complex topic accessible through small, runnable examples, and it does so without leaning on automation. That combination — hands-on interaction and careful design — is rare enough to justify a look.
Source material: “Go Concurrency Distilled,” antonz.org.
Get the Notebook.
The day's best stories and every fresh verdict, in plain English, in your inbox by seven. One email a day, no more.

