Go, End to End
Defer, Panic, and Recover
These three are related but solve different problems, and confusing them is one of the more common ways newcomers write Go that technically works but reads oddly to anyone else.
defer
defer schedules a function call to run when the surrounding function returns, regardless of how it returns (normal return, or a panic unwinding through it). The classic use is guaranteeing cleanup happens next to the code that created the thing needing cleanup, instead of scattered at every possible exit point:
Multiple defers in one function run in LIFO order, last deferred, first executed, which matters when the cleanups have a dependency order (close the file you opened last, first):
In real code, defer is how you close files (defer f.Close()), unlock mutexes (defer mu.Unlock()), and generally pair "acquire" with "release" right next to each other instead of trusting every return path to remember.
panic
panic is Go's mechanism for "this program cannot continue correctly", and it's deliberately distinct from error. An error is an expected, recoverable outcome your caller should handle (a file not found, a network timeout). A panic is for programmer mistakes and truly unrecoverable states: an index out of range, a nil map write, an explicit panic("invariant violated") when your own code detects it's in a state that should be impossible.
Mixing these up is the actual pitfall: don't panic for things a caller could reasonably expect and handle (that's what error is for), and don't return an error for a genuine programming bug you'd rather crash loudly on during development.
recover
recover stops a panic from propagating, but only when called directly inside a deferred function. Called anywhere else, it does nothing and returns nil.
Recover at most at a goroutine or request boundary (an HTTP handler, a worker goroutine's top level), so one bad input can't take down the whole process. Don't use panic/recover as a general substitute for normal error returns, that's not what it's for and it makes control flow much harder to follow.
Try it yourself: write a function that recovers from a panic caused by indexing past the end of a slice, and returns a normal error instead. Confirm the program keeps running afterward instead of crashing.