Channel Creation
Fast recall
Quick revision
Core answer
Whether you understand Channel Creation beyond memorized syntax.
Giving a definition without explaining a practical trade-off.
Connect the concept to a small production example and state its complexity or failure mode.
Interview-ready guide
Interview answer
A channel is created with make and a channel type:
ch := make(chan int) // unbuffered
buf := make(chan string, 10) // buffered, capacity 10The type inside the chan keyword is the value type the channel carries — int, string, a struct, another channel, any type. Unbuffered (no capacity argument) means every send blocks until a receiver takes the value. Buffered means sends fill slots up to the capacity before blocking.
One declaration trap: the zero value of a channel is nil, and a nil channel is not a ready channel —
var ch chan int // nil — sends and receives block forever
ch <- 1 // if this were in a running program: deadlockso channels are always created with make, the same way maps are. A nil channel differs from a nil map though: writing a nil map panics, while a nil channel blocks — forever, silently, with the runtime eventually detecting the deadlock (fatal error: all goroutines are asleep - deadlock!).
The created channel is bidirectional — it can send and receive. Directional types (send-only chan<- int, receive-only <-chan int) appear in function signatures as narrowing, but you never make a directional channel; you convert one.
Channel Creation
Direct Answer
make(chan T) for unbuffered, make(chan T, n) for buffered with capacity n. Both are ready to use immediately — channels are always initialized by make, never declared nil.
Deep Dive & Architecture
Channel creation is make because a channel is a pointer to runtime structure — a hchan struct holding a circular buffer (for the capacity), send and receive wait queues, and a lock. The nil channel is a null pointer to that structure, which explains every nil-channel behavior mechanically: a send on nil needs a wait-queue to enqueue onto — there is no structure, so the goroutine parks forever; same for receive.
The capacity argument to make is the size of the circular buffer allocated up front:
make(chan int) // hchan with buffer size 0 — unbuffered
make(chan int, 5) // hchan with a 5-slot ring buffer
That buffer is what makes the two channel kinds mechanically different. Unbuffered (size 0): a send copies the value directly to a waiting receiver — a rendezvous; the sender is parked until a receiver arrives. Buffered: a send copies into the ring if a slot is free and never parks; the value sits until a receiver takes it.
That's why unbuffered channels give synchronization for free (the send completing MEANS the receive happened — no value is ever 'in flight') while buffered channels give throughput (senders run ahead of receivers up to n). Both are created in one expression, but they have different concurrency semantics — choosing the capacity is choosing the protocol.
Common Mistakes & Pitfalls
Giving a definition without explaining a practical trade-off.
Check your understanding
Answer these without looking back at the article.
- When would you use Channel Creation in a real project?
- How would you compare Channel Creation with an alternative?
- What is a common mistake when using Channel Creation?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.