debug a problem involving Send Receive Operations
Fast recall
Quick revision
Core answer
Practical understanding of Send Receive Operations.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
Channels have no drop mode. The three possible outcomes of a send, exhaustively:
- 1The value is delivered (unbuffered: a receiver took it; buffered: a slot holds it) — possibly after blocking.
- 2The send blocks because the channel is full or nil — the sender waits.
- 3The channel is closed — panic.
There is no fourth outcome where the value vanishes. That guarantee is what makes channels correct-by-construction for handoffs: a completed send is a delivered value, full stop.
But real systems need dropping — a metrics sink that shouldn't block the app when the collector is slow, a UI event queue that discards stale clicks. Go's answer is you build the drop with select:
select {
case ch <- v:
// delivered
default:
// channel full — drop, log, count, never block the caller
}The default case makes the select non-blocking: if no case is ready immediately, default runs. This is the idiom for bounded-queue-with-drop semantics on top of a buffered channel.
The time-bounded variant — a send that waits up to a deadline then gives up:
select {
case ch <- v:
// delivered in time
case <-time.After(100 * time.Millisecond):
// give up — timeout path
}The interview point: the primitive is strict (block or panic), and the policy (drop, timeout, retry) is yours, built from select. Go keeps the channel honest — it will never silently lose data — and puts the lossy decisions where they belong: in explicit, visible application code.
debug a problem involving Send Receive Operations
Direct Answer
Go channels never drop messages — a send either blocks until delivered (or buffered) or panics on a closed channel. Dropping requires building it yourself: select with default, or a time-bounded select.
Deep Dive & Architecture
The never-drop guarantee is structural: the send operation enqueues the value with the sender's goroutine state — there is no code path where a send returns without the value being committed to the channel (receiver's stack, buffer slot, or the panic). Contrast languages where queues have offer/try modes returning success flags — Go's send is a statement, not a question, and its return IS the commitment.
select with default is the non-blocking probe built into the language, and its mechanics matter: select locks ALL its channels, checks every case for readiness, and if none is ready AND a default exists, runs default — atomically, under all the locks. The try-send built this way is race-free: two goroutines both doing select-default-send on the same full channel — one wins the single free slot, the other takes default, with no lost or duplicated values, because the whole check-and-commit is under the channel locks.
That composition — one guaranteed-delivery primitive (the send) plus one atomic choice primitive (select) — generates every messaging policy: drop-when-busy (default), wait-then-drop (time.After), first-available (plain select across channels). The pattern to recognize in code review: any 'I need best-effort delivery' requirement maps to select+default over a buffered channel, and the buffer size IS the explicit tolerance for how far ahead the producer may run before losses start. The system's lossiness becomes a number you chose — not a default the runtime chose for you.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Send Receive Operations, and when would you use it?
- When would you use Send Receive Operations in a real project?
- How would you compare Send Receive Operations with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.