AI Code Explainer

Understand code with plain language explanations

Reading Unfamiliar Code: The Outside-In Method

Experienced engineers do not read code top to bottom like prose. They read it outside-in, and an AI explainer is most useful when you drive it the same way. Start with the boundaries: what goes in, what comes out. Find the entry point (the exported function, the route handler, the main loop) and the exit points (return values, thrown errors, writes to a database or file). Before asking what any line does, ask the tool to identify inputs, outputs, and side effects — that single question maps the territory. Second pass: follow one concrete value through the code. Pick a realistic input (a user object with an empty orders array, say) and ask the explainer to trace it line by line. Tracing a specific value exposes branches, mutations, and early returns that a generic summary glosses over. Third pass: name the idioms. Most code is 80% pattern and 20% decision. A debounce wrapper, a retry loop with exponential backoff, a reducer — once the pattern is named, only the deviations from the pattern need real attention. Ask directly: which parts of this are standard patterns, and where does it deviate from them? Only then read line by line, and only the parts that survived the first three passes as genuinely unclear. On a 200-line file this usually leaves about 20 lines that matter.

What AI Code Explainers Get Wrong

AI explanations fail in patterns, and the patterns are predictable enough to check for. Hallucinated APIs are the classic failure: the explanation describes a parameter that does not exist, a method with a similar name from another library, or behavior from a different major version. Models blend the hundreds of libraries they saw in training, so an explanation of a lodash call may quietly describe the Underscore behavior, and an explanation of pandas code may cite an argument removed two versions ago. Missing side effects are subtler. An explainer summarizes what a function computes but omits that it also mutates its argument, writes to a cache, logs PII, or fires an analytics event. Summaries are biased toward the return value; real-world bugs live in the side effects. Always ask separately: what state does this code change outside its own scope? Stale idioms cut both ways. Models sometimes call perfectly modern code outdated, and sometimes present deprecated patterns (var, componentWillMount, Python 2 division semantics) as normal, because both eras exist in training data. Treat any claim about current best practice as dated by default. Finally, confident complexity claims: "this runs in O(n log n)" is exactly the kind of sentence a model generates fluently and incorrectly, especially with hidden loops inside library calls like sort, includes, or spread. If the complexity matters, derive it yourself or ask the model to count operations on a concrete input size rather than assert a formula.

Prompting for the Right Depth of Explanation

"Explain this code" produces a paragraph that restates the code in English — technically correct and nearly useless. The fix is to declare what you already know and what decision the explanation must support. If you are a beginner, ask for an analogy plus a trace: explain what this does using a real-world analogy, then walk one example input through it step by step. Analogies without traces produce vague comfort; traces without analogies produce a wall of state changes. If you can read the language but not this codebase, skip syntax entirely: assume I know JavaScript well; explain only the intent, the non-obvious decisions, and anything that would surprise a maintainer. This is the single highest-leverage prompt for code review preparation. If you are debugging, do not ask for an explanation at all — ask for a disagreement: the author believes this function returns a sorted copy; argue for and against that belief with reference to specific lines. Models are noticeably better at finding problems when framed as critics rather than narrators. The level selector in this tool writes these framings for you; edit the generated prompt to name your actual language experience rather than a generic level.
Instead of:
"Explain this code."

Try:
"I know Python well but have never used asyncio.
1. What is the intent of this function in one sentence?
2. Trace the input [3, 1, 2] through it line by line.
3. List every side effect (I/O, mutation, global state).
4. What would surprise a maintainer? What could break
   under concurrency?
Do not explain basic syntax."

Worked Example: A Restated Explanation vs a Useful One

Consider a five-line function that most explainers describe the same shallow way. The snippet below deduplicates users by email and keeps the last occurrence. A restated explanation says: this function iterates over the users array, builds a Map keyed by email, and returns the Map values as an array. Every word is true. Nothing is useful — it is the code, read aloud. A useful explanation answers the questions the code raises. Why a Map instead of a Set or an object? (Maps preserve insertion order and allow any key type; here order is the point.) Why does the last duplicate win? (Map.set overwrites, so later entries replace earlier ones — if the array is sorted oldest-first, this silently keeps the newest record, which may or may not be intended.) What are the sharp edges? (Emails differing only by case are treated as distinct; undefined emails collapse into one bucket; the function allocates a new array but the user objects inside are shared references, so mutating them affects the caller.) That last paragraph is what you should demand from any explainer: not what the code does, but what it decides, what it assumes, and where it can hurt you. If an explanation contains no sentence beginning with "note that" or "be careful", ask again.
function dedupeUsers(users) {
  const byEmail = new Map();
  for (const u of users) byEmail.set(u.email, u);
  return [...byEmail.values()];
}

Shallow: "Builds a Map keyed by email and returns its values."

Useful:  "Keeps the LAST user per email (Map.set overwrites).
          Case-sensitive: A@x.com and a@x.com stay separate.
          Returned array is new, but user objects are shared
          references — mutating them affects the original."

A Two-Minute Verification Routine

Treat every AI explanation as a draft from a confident intern: usually right, occasionally wrong in ways that matter, never aware of the difference. Check named APIs against real documentation — not against the model memory. If the explanation hinges on what a specific function does (parseInt without a radix, Array.sort mutating in place, a library default), the docs check takes thirty seconds and catches the most damaging errors. Run the trace. If the explainer walked an input through the code, actually execute that input in a REPL or a scratch test and compare. A divergence between the narrated trace and the real output is the strongest possible signal the explanation is wrong. Cross-examine the risky claims. For anything about concurrency, mutation, error handling, or performance, ask the same question again in a fresh session, phrased differently. Consistent answers are weak evidence of correctness; inconsistent answers are strong evidence that you are looking at a guess. None of this is much work — and the alternative, shipping code you understand only through an unverified paraphrase, is how subtle production bugs are born.