go-concurrencylisted
Install: claude install-skill AllThingsSmitty/agent-skills
## Goroutines
Goroutines are cheap (a few KB of stack) but not free. Every goroutine must have a clear exit condition — otherwise it leaks. A goroutine leak happens when a goroutine blocks forever on a channel send, channel receive, or lock with no one to unblock it.
```go
// Bad: no exit condition — leaks if done is never closed
go func() {
for item := range items {
process(item)
}
}()
// Good: exits on context cancellation
go func() {
for {
select {
case <-ctx.Done():
return
case item, ok := <-items:
if !ok {
return
}
process(item)
}
}
}()
```
## Channels
Unbuffered channels provide a synchronous rendezvous — sender and receiver must both be ready. Buffered channels decouple them up to the buffer size.
```go
unbuffered := make(chan int) // blocks until receiver is ready
buffered := make(chan int, 10) // blocks only when full
```
Use directional channel types in function signatures to make intent explicit:
```go
func produce(out chan<- int) { out <- 42 }
func consume(in <-chan int) { v := <-in; _ = v }
```
Only the sender should close a channel. Closing from the receiver side or closing twice panics.
```go
// Range exits automatically when the channel is closed
for item := range ch {
process(item)
}
```
## select
`select` lets a goroutine wait on multiple channel operations simultaneously.
**Timeout with context:**
```go
select {