Visual walkthrough — Recursion — call stack visualization, base case, recursive case
Before anything else, three plain words we will lean on the whole page:
Our target, stated once so every symbol below is earned:
Step 1 — Spot the self-similar shape
WHAT. Look at the product and peel off the last number, : So . The term means "the same factorial question, but one size smaller".
WHY. Recursion only pays off when a big problem contains a smaller copy of itself. Here the copy is literally sitting inside: to know you only need and the answer to . That embedded copy is the recursive case.
PICTURE. The nested boxes below: the outer box is ; peeling the label off reveals an identical-shaped inner box , and so on inward.

Step 2 — Find the floor where shrinking stops
WHAT. Keep peeling: . And we simply define to equal (the empty product — multiply nothing, get the "do-nothing" number ).
WHY. A recursive machine that only shrinks would fall forever. We need one input whose answer is known outright, with zero further recursion. That is the base case. Choosing (not ) also handles the input correctly for free.
PICTURE. The descending staircase: each step drops the input by one; the base case is the solid ground the staircase lands on.

Step 3 — Write the two-case rule
WHAT. Glue Steps 1 and 2 into one definition. The brace means "pick the line whose condition is true":
WHY. Every recursive function needs exactly these two parts: a stop (base case) and a shrink-and-combine (recursive case). The guarantees progress toward the base; the is the "combine" that reassembles the answer.
PICTURE. A fork-in-the-road: the diamond tests n == 0; one branch returns , the other multiplies and calls a smaller self.

def fact(n):
if n == 0: # base case → the STOP
return 1
return n * fact(n-1) # recursive case → shrink & combineStep 4 — Winding: watch the frames pile up
WHAT. Call fact(3). It cannot finish, because it needs fact(2) first — so it pauses and a new frame is pushed. That one needs fact(1), pauses, pushes. And so on:
Each boxed part is an unknown-so-far answer the paused call is waiting on.
WHY. A call can only return once its sub-call gives an answer. So the whole chain freezes going down — this descent is the winding phase. Crucially, each frame keeps its own private (frame for and frame for coexist — they are not shared).
PICTURE. Four sticky-notes stacked, top = most recent (fact(0)), each note showing its private and "waiting for ..." arrow to the note above.

Step 5 — The base case lands
WHAT. The top frame is fact(0). It matches n == 0, so it returns immediately — no new frame, no multiplication.
WHY. This is the only moment recursion produces a value out of thin air. Without this line the pile would keep growing past Python's depth limit (~1000) and crash with RecursionError. The base case is the hinge between winding and unwinding.
PICTURE. The top sticky-note flips over to reveal a green "1", the first concrete value in the whole pile.

Step 6 — Unwinding: multiply on the way back up
WHAT. Now peel notes off the top (LIFO) and let each finish its paused multiplication using the answer just handed up:
WHY. Because the deepest (last-pushed) frame finishes first, the multiplications happen outside-in on the way up — the ordering is forced by LIFO, not chosen by us. fact(3) is the last to complete and delivers the final .
PICTURE. The stack shrinking downward, a value climbing hand-over-hand as each frame pops.

Step 7 — The degenerate & edge inputs
WHAT & WHY, case by case:
- — the smallest legal input. Hits the base case on the very first call. Zero frames stack up beyond the first; answer returned instantly. This is why we chose (not ) as the base — the function is correct at its floor with no special-casing.
- — one step above the floor. Winds once to
fact(0), unwinds once: . ✅ (Correct: .) - — the trap. Try
fact(-1): it is ? No. Is it ? No. So it skips the base case and callsfact(-2), thenfact(-3), … forever. The pile overflows →RecursionError. The lesson: your base case must actually catch every path that shrinking can take. A safer guard isif n <= 0: return 1.
PICTURE. Three mini-stacks side by side: fact(0) (one note, instant), fact(1) (two notes), and fact(-1) (an ever-growing red tower crashing through a ceiling).

The one-picture summary
The whole story in one V-shape: down the left arm the frames wind (each pausing, each keeping its own ), the base case sits at the bottom of the V, and up the right arm they unwind, multiplying .

Recall Feynman: the whole walkthrough in plain words
You want . You don't compute it directly — you say " is just times ", and pin up a sticky-note reminding you to multiply by later. To get you say "that's times " — another note. For : " times " — another note. Now is the one you know cold: it's , no thinking needed. That's the base case, the ground you were walking down toward. Now you peel the notes off the top in reverse: , then , then . Going down you only made promises (winding); coming back up you cashed them in (unwinding). The pile of promises was the call stack, and because you always peel the top note first — the last one you pinned — the deepest call is the first to finish. Give the function a negative number and there's no ground to land on: you keep pinning notes until you run out of wall. That crash is a RecursionError.
Active recall
Which frame in fact(3) returns a value first, and why?
fact(0) — it is the base case and also the last-pushed (deepest) frame, so LIFO pops it first.In the frozen expression , which nesting direction is winding?
Why does fact(-1) crash instead of returning?
What single change makes fact safe against negative inputs?
if n <= 0: return 1 so every downward path is caught.Related: Iteration — for and while loops · Fibonacci and overlapping subproblems · Big-O and Recursion Depth · Memoization and Dynamic Programming · Tree and Graph Traversal