TL;DR: Recursion is when a function calls itself to break a big problem into smaller versions of the same problem. Every recursive function needs a base case — a stopping condition — or it will crash with a stack overflow. Your AI uses recursion for nested data like file trees, nested JSON, and folder structures. For flat data like simple arrays, ask it to use a loop instead.

Why Vibe Coders Keep Running Into Recursion

You did not set out to learn computer science. You are building things — tools, apps, automations — and your AI is doing the heavy lifting. But then you open a project and see something like this in the generated code:

function walkDirectory(path) {
  const entries = fs.readdirSync(path);
  for (const entry of entries) {
    if (entry.isDirectory()) {
      walkDirectory(entry.path); // <-- calls itself
    } else {
      processFile(entry.path);
    }
  }
}

That walkDirectory(entry.path) inside walkDirectory — the function calling itself — is recursion. If you have never seen it explained, it looks like a typo or a bug. It is not. It is a deliberate technique, and your AI reaches for it in specific situations.

Here is why it matters to you: when recursion works, it is elegant. When it fails, the error messages are cryptic. "Maximum call stack size exceeded" is the crash you get when recursion goes wrong, and if you do not know what recursion is, you cannot debug it or tell your AI how to fix it.

You also need to know when to push back. Sometimes your AI generates a recursive function for a problem that a simple loop would handle better. Knowing the difference saves you complexity, memory, and bugs.

The Construction Metaphor: Estimating a Renovation

Imagine you are estimating a renovation job on a house you have never been inside. You need to calculate the total square footage of every room — but you do not have a floor plan, and some rooms have rooms inside them (think: a suite with an attached bathroom, a closet with a storage room off it).

Here is your process:

  1. You walk into the first space. Measure it. Write it down.
  2. You check: does this space have any sub-rooms?
  3. If yes — walk into the sub-room and start the same process over again from step 1.
  4. If no — you are done with this branch. Walk back out and continue where you left off.

You are doing the same job (measure a space, check for sub-rooms) at every level of the building. You do not need a different process for each floor or each wing — the process is identical, and you just keep applying it until there are no more rooms to check.

That is recursion. The same process, applied to itself, going deeper until it hits a stopping point (no more sub-rooms), then unwinding back to where it started.

The stopping point — "no more sub-rooms" — is what programmers call the base case. Without it, you would walk into sub-rooms forever. With it, the process terminates and you get your total.

What Recursion Actually Looks Like in Code

Let's start with the simplest possible recursive function — counting down to zero:

function countdown(n) {
  // BASE CASE: stop here
  if (n <= 0) {
    console.log("Done!");
    return;
  }

  // Do the work for this level
  console.log(n);

  // RECURSIVE CALL: call itself with a smaller version
  countdown(n - 1);
}

countdown(5);
// Output:
// 5
// 4
// 3
// 2
// 1
// Done!

Two things to notice:

  • The base case (if (n <= 0)) — this is the stop sign. When we hit zero, we do not call ourselves again.
  • The recursive call (countdown(n - 1)) — this calls the same function with a smaller input. Each call gets closer to the base case.

Every recursive function you will ever see follows this same structure: a base case that stops the chain, and a recursive call that moves toward that base case. If you can spot these two things in AI-generated code, you can read any recursive function.

Why AI Reaches for Recursion

Your AI has seen millions of codebases. It knows which tool fits which job. Recursion is the right tool when the problem has unknown or variable depth — when you cannot know in advance how many levels deep the data goes.

Here are the specific situations where you will see your AI generate recursive functions:

1. File and Directory Scanning

A folder can contain folders that contain folders. You do not know how deep the nesting goes until you look. A loop does not handle this elegantly — you would need to write a loop inside a loop inside a loop, and how many loops do you write? Recursion handles it naturally:

const fs = require('fs');
const path = require('path');

function getAllFiles(dirPath, fileList = []) {
  // BASE CASE: readdirSync handles empty dirs, loop handles no subdirs

  const entries = fs.readdirSync(dirPath, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(dirPath, entry.name);

    if (entry.isDirectory()) {
      // RECURSIVE CALL: go deeper into the subdirectory
      getAllFiles(fullPath, fileList);
    } else {
      fileList.push(fullPath);
    }
  }

  return fileList;
}

const files = getAllFiles('./my-project');
console.log(`Found ${files.length} files`);

This will find every file in every folder no matter how deeply nested — without you specifying the depth in advance.

2. Nested JSON and Object Structures

When your app stores hierarchical data — a comment thread where replies have replies, a product category with subcategories, an org chart — the JSON structure can be arbitrarily deep. Your AI will write recursive functions to process it:

// A nested comment structure from a database
const comments = [
  {
    id: 1,
    text: "Great post!",
    replies: [
      {
        id: 2,
        text: "Agreed!",
        replies: [
          { id: 3, text: "Me too!", replies: [] }
        ]
      }
    ]
  }
];

function countAllComments(comments) {
  // BASE CASE: empty array means no comments at this level
  if (comments.length === 0) return 0;

  let total = 0;
  for (const comment of comments) {
    total += 1; // Count this comment
    total += countAllComments(comment.replies); // RECURSIVE: count replies
  }
  return total;
}

console.log(countAllComments(comments)); // 3

A flat loop could only count the top-level comments. The recursive version counts every comment at every depth — and it works whether comments are nested 2 levels or 20 levels deep.

3. Tree Traversal

The DOM — the HTML structure of every web page — is a tree. So are menu systems, category hierarchies, and permission structures in databases. Navigating trees is one of the most common things recursive functions do:

// Find all nodes with a specific class in a DOM-like tree
function findByClass(node, className, results = []) {
  // BASE CASE: no children to search
  if (!node) return results;

  if (node.className === className) {
    results.push(node);
  }

  // RECURSIVE CALL: check each child node
  if (node.children) {
    for (const child of node.children) {
      findByClass(child, className, results);
    }
  }

  return results;
}

This is also how the browser itself works internally. The DOM is a tree, and navigating it requires visiting nodes and their children — recursion is the natural fit.

4. Flattening Nested Arrays

You might ask your AI to flatten a deeply nested array — turning [1, [2, [3, [4]]]] into [1, 2, 3, 4]. Without knowing the nesting depth, a loop cannot do this cleanly. Your AI will likely write:

function flattenDeep(arr) {
  // BASE CASE: not an array, return it as-is
  const result = [];

  for (const item of arr) {
    if (Array.isArray(item)) {
      // RECURSIVE CALL: item is itself an array, flatten it too
      result.push(...flattenDeep(item));
    } else {
      result.push(item);
    }
  }

  return result;
}

console.log(flattenDeep([1, [2, [3, [4]]]]));
// [1, 2, 3, 4]

Note: JavaScript now has arr.flat(Infinity) built in for this exact case. Your AI may use that shortcut, but the recursive version is what is happening under the hood.

Prompt to Understand Recursive Code

Explain this recursive function step by step.
What is the base case? What does each recursive
call do? Walk me through what happens with a
concrete small example.

[paste your recursive function here]

The Base Case: The Most Important Part

If there is one thing to remember about recursion, it is this: the base case is everything.

Think back to the renovation job. If you are checking every room for sub-rooms, what happens if you keep finding sub-rooms that also have sub-rooms, indefinitely? You never finish. You are stuck in an infinite loop of rooms.

In code, that infinite loop crashes the program. Every time a function calls itself, the computer has to remember where it was so it can come back. That memory lives on something called the call stack — a list of all the function calls currently in progress.

When your recursive function never hits its base case, the call stack grows and grows until the program crashes with:

RangeError: Maximum call stack size exceeded

That is a stack overflow — the call stack got too large. Every stack overflow you will ever see from recursive code has the same root cause: the base case was missing, wrong, or unreachable.

Stack Overflow Checklist

If you see "Maximum call stack size exceeded," ask your AI to check three things: (1) Does the recursive function have a base case? (2) Is the data passed to each recursive call actually getting smaller? (3) Could the input data be more deeply nested than expected?

How to Read AI-Generated Recursive Code

When you open AI-generated code and see a function calling itself, do not panic. Read it in this order:

  1. Find the base case first. Look for an if statement near the top that returns early or returns a simple value. That is your stopping condition.
  2. Find the recursive call. Look for the function name appearing inside the function body. That is where it calls itself.
  3. Ask: what changes each time? The input to the recursive call should be a smaller or simpler version of the original input — one level down in a tree, one item removed from an array, one step closer to the base case.

Here is an annotated example to practice on:

function sumNested(value) {
  // STEP 1: Find the base case
  // If value is a plain number, just return it — no recursion needed
  if (typeof value === 'number') {
    return value;
  }

  // STEP 2: Find the recursive call
  // value must be an array — add up everything inside it
  let total = 0;
  for (const item of value) {
    // STEP 3: What changes? Each item might be a number OR a nested array
    // The recursive call handles both cases
    total += sumNested(item);
  }
  return total;
}

console.log(sumNested([1, [2, 3], [4, [5, 6]]]));
// 1 + 2 + 3 + 4 + 5 + 6 = 21

Base case: typeof value === 'number' — when we hit a number, return it directly. Recursive call: sumNested(item) — each item in the array goes through the same process. Each call gets either a number (base case) or a smaller array. It terminates.

Recursion vs. Loops: When to Ask Your AI for a Different Approach

Your AI is not always right about which tool to reach for. Recursion is powerful but carries costs — and for many common tasks, a simple loop is the better choice.

Use Recursion When...

  • Data depth is unknown or variable (nested folders, comment threads)
  • The structure is a tree or graph
  • Each level of the problem looks the same as the whole problem
  • You are traversing a DOM or similar hierarchy

Use a Loop When...

  • Data is flat — a list, an array, database rows
  • Depth is fixed and known in advance
  • You are just repeating something N times
  • Performance matters and the list could be large

The practical rule: if your data is flat, always ask for a loop. If your data is nested with unknown depth, recursion makes sense. When in doubt, ask your AI to explain why it chose recursion — if it cannot give you a clear reason related to nested structure, that is a sign a loop would be cleaner.

Prompt to Get a Loop Instead

You generated a recursive function here, but my
data is a flat list with no nesting. Rewrite this
using a regular loop. Keep the same logic and
add a comment explaining the approach.

[paste the recursive function]

Prompt to Add Stack Overflow Protection

This recursive function could hit a stack overflow
if the data is deeply nested. Add a depth limit
parameter with a default of 100 and throw a clear
error if it is exceeded.

[paste your recursive function]

Stack Overflow: The Real Risk and How to Handle It

Stack overflow is the failure mode you need to know about before shipping anything with recursion. Here is exactly what happens:

Every function call in JavaScript takes up a slot on the call stack. When countdown(5) calls countdown(4), the program has to remember it was in the middle of countdown(5) so it can return there afterward. That memory slot stays open until the inner call finishes.

With recursion, you can have hundreds of these open slots at once — one for each level of depth. JavaScript allows somewhere around 10,000–15,000 levels before it crashes (the exact limit depends on the environment). That sounds like a lot, but:

  • A missing base case means you never stop, and you will hit the limit fast
  • A deeply nested file system or organizational hierarchy could legitimately hit hundreds of levels
  • A malformed JSON object from an external API could be unexpectedly deep

The fix is either to add a depth limit, or to rewrite the recursion iteratively using an explicit stack (a pattern your AI knows well). For error handling in production code, always add protection:

function walkTree(node, depth = 0) {
  const MAX_DEPTH = 100;

  // Guard against runaway recursion
  if (depth > MAX_DEPTH) {
    throw new Error(`Tree exceeds maximum depth of ${MAX_DEPTH}`);
  }

  // BASE CASE: leaf node with no children
  if (!node.children || node.children.length === 0) {
    return node.value;
  }

  // RECURSIVE CALL: go one level deeper
  return node.children.map(child => walkTree(child, depth + 1));
}

That depth parameter tracks how deep you have gone. If it exceeds your limit, you get a clear, informative error instead of a cryptic "Maximum call stack size exceeded" crash in production.

Real-World Examples From AI-Built Projects

Here are the specific recursive patterns you are most likely to see in projects your AI builds:

Rendering a Nested Navigation Menu

Multi-level navigation menus — where categories have subcategories — are a classic recursion use case. Your AI will build this with a recursive component or function:

function renderMenu(items, level = 0) {
  if (!items || items.length === 0) return '';

  const indent = '  '.repeat(level);
  let html = `${indent}<ul>\n`;

  for (const item of items) {
    html += `${indent}  <li>${item.label}`;

    if (item.children && item.children.length > 0) {
      html += '\n';
      html += renderMenu(item.children, level + 1); // RECURSIVE
      html += `${indent}  `;
    }

    html += `</li>\n`;
  }

  html += `${indent}</ul>\n`;
  return html;
}

const menu = [
  { label: 'Products', children: [
    { label: 'Software', children: [
      { label: 'AI Tools', children: [] }
    ]},
    { label: 'Hardware', children: [] }
  ]},
  { label: 'About', children: [] }
];

console.log(renderMenu(menu));

Searching Nested Data from a Database

When your database stores hierarchical data — like a product taxonomy or a permission tree — and you need to find a specific item by ID, your AI will write a recursive search:

function findById(tree, targetId) {
  // BASE CASE: this node is the one we want
  if (tree.id === targetId) {
    return tree;
  }

  // BASE CASE: no children to search
  if (!tree.children || tree.children.length === 0) {
    return null;
  }

  // RECURSIVE CALL: search each child
  for (const child of tree.children) {
    const found = findById(child, targetId);
    if (found) return found; // Stop as soon as we find it
  }

  return null; // Not found in this branch
}

When You See Deep Nesting in Your Data

If your app is slow and you have recursion processing large nested datasets, tell your AI: "This recursive function is being called on large datasets. Can you rewrite it iteratively using a stack-based approach, or tell me if there is a more efficient algorithm for this case?"

What to Tell Your AI About Recursion

Now that you understand what recursion is and when it makes sense, here are specific prompts that let you direct your AI more precisely:

When You Need to Process Nested Data

I have a nested category structure from my database
where each category can have subcategories to unknown
depth. Write a recursive function to flatten all
category names into a single array. Include a depth
limit of 50 and a base case comment.

When You Get a Stack Overflow

I'm getting "Maximum call stack size exceeded" from
this function. Find the issue — is the base case
missing or unreachable? Fix it and explain what
was wrong.

[paste the function]

When You Want to Understand Generated Code

Walk me through this recursive function using a
concrete small example. Show me exactly what happens
at each level of recursion, what the base case is,
and when the function stops calling itself.

[paste the function]

When You Think a Loop Would Be Better

You wrote a recursive function here but my data is
a flat list. Is there a reason you used recursion
instead of a loop? If not, rewrite it as a simple
for...of loop.

What to Learn Next

Recursion connects to several other concepts that will sharpen how you read and direct AI-generated code:

  • What Is a Function? — recursion is just a function calling itself, so a solid understanding of functions is the foundation.
  • What Are Loops? — know the alternative so you can choose (or ask your AI to choose) the right tool.
  • What Are Arrays? — most recursive functions process arrays or tree-shaped data; understanding arrays helps you read what is being passed around.
  • What Is Error Handling? — stack overflow crashes need proper error handling to produce useful messages instead of cryptic crashes.
  • What Is JSON? — the nested data structures that most commonly trigger AI-generated recursion are represented as JSON.
  • What Is JavaScript? — the call stack, single-threaded execution, and stack overflow all come from how JavaScript itself works.

Next Step

Ask your AI to scan a folder in your current project and list all files. Look at the code it generates. Find the base case. Find the recursive call. If it uses recursion, ask: could this have been a loop? Now you can have that conversation with your AI instead of just accepting whatever it produces.

FAQ

Recursion is when a function calls itself as part of its own work. Instead of using a loop, the function breaks a problem into a smaller version of the same problem and calls itself with that smaller version. It keeps doing this until it hits a stopping condition called the base case, then it unwinds and returns the final result.

A base case is the stopping condition in a recursive function — the situation where the function stops calling itself and returns a direct answer. Without a base case, the function would call itself forever and crash with a stack overflow error. Every recursive function must have at least one base case.

AI reaches for recursion when the data or problem has unknown depth — nested folders, nested JSON objects, comment threads, org charts, file systems. These structures can be ten levels deep or two levels deep — you do not know in advance. A loop does not handle unknown depth elegantly. Recursion does, because the function just keeps calling itself until it hits the bottom.

A stack overflow happens when a recursive function calls itself too many times without hitting the base case. Each function call gets added to the call stack — a list of active function calls the program is tracking. When that list gets too long (usually a few thousand levels deep), the program crashes with a "Maximum call stack size exceeded" error. It is caused by a missing or unreachable base case, or data that is simply too deeply nested.

Ask for a loop when the data is flat or has a known, fixed depth — like processing a list of items, going through database rows, or repeating an action a set number of times. Loops are simpler to read, use less memory, and never risk stack overflow. Use recursion (or let AI use it) when depth is unknown or the data is genuinely tree-shaped.

Usually no — loops are faster because they do not have the overhead of adding function calls to the call stack. For most practical tasks in a vibe-coded app (flat lists, database rows, fixed iterations), a loop will perform better. Recursion trades some performance for clarity when dealing with complex nested structures where a loop would require you to manually manage a stack yourself.