Recursion — a function that calls itself — is one of those concepts that clicks suddenly and then feels like magic. It expresses certain problems, like walking a tree or dividing a task in half, far more naturally than loops do. It also produces one of the most alarming beginner errors: the stack overflow. Understanding the call stack turns both the elegance and the crash into something predictable.
The key realisation is that every function call, recursive or not, uses a small slab of memory called a stack frame, and those frames pile up.
What the call stack actually is
When a function is called, the program pushes a frame onto the call stack holding its local variables and the spot to return to when it finishes. When the function returns, its frame is popped off. Ordinary code pushes and pops frames constantly, and the stack stays shallow. Recursion is different: a recursive function calls itself before finishing, so its frame stays on the stack while the next call adds another on top.
Compute a factorial recursively and you get a tower of frames, one per level, all waiting for the deepest one to finish so the results can unwind back up. That tower is the recursion made physical in memory.
Why it overflows
The stack has a finite size. If recursion goes too deep — a base case that never triggers, or simply a problem with millions of levels — the tower of frames exceeds that limit and the program crashes with a stack overflow. The two classic causes are a missing or wrong base case (infinite recursion) and legitimately deep recursion on large input.
This is why every recursive function needs a rock-solid base case: the condition that stops the recursion and lets the stack unwind. Most beginner recursion bugs are a base case that is missing, unreachable, or checked after the recursive call instead of before.
Recursion versus loops
Recursion and iteration can often solve the same problem, and the choice is about clarity and constraints. Tree-shaped and divide-and-conquer problems read beautifully as recursion; linear repetition usually reads better as a loop and avoids stack risk entirely. When recursion is natural but depth is a worry, some languages optimise tail recursion into a loop, and any recursion can be rewritten iteratively using an explicit stack data structure.
The practical guidance: reach for recursion when it makes the problem clearer, always with a guaranteed base case, and switch to iteration when depth could be large or the recursive version is actually more confusing. Elegance that overflows in production is not elegance.