Make AI Explain Unfamiliar Code Before You Change It
Type "refactor this" on code you don't fully understand and the AI — which understands it no better — rewrites a load-bearing line. Here's how to run an explanation pass first: what the code does, what it assumes, and which lines are risky to touch, before you change anything.
Build a Code Explanation PromptWhen "just refactor this" edits code nobody understands
You inherit a function — old code, or someone else's, or your own from two years ago — and the ask is "make this cleaner," "add this feature," "fix this." You don't fully understand what it does, so you hand it to the AI and let it decide. But the model is in the same position you are: it can read the lines, not the history. It doesn't know which empty return is a deliberate signal, which odd special case is guarding a real edge, or which caller depends on the exact behavior. So it produces a confident, cleaner-looking version — and quietly changes something that was there on purpose. The refactor looks like progress until the thing it protected breaks in production.
That's the risk this guide heads off: make AI explain code before it changes it. Before you ask for any change, run an explanation pass — get the AI to tell you what the code does, what it assumes about the world around it, which paths are edge cases, and which lines are risky to touch, and, crucially, to mark what it can't determine from the code alone. NewPrompt builds that explanation prompt for you; it doesn't read, run, test, or analyze your code, and it doesn't connect to your repo. The model produces the explanation when you run the prompt in your own assistant, working only from the snippet you paste — and what comes back is a candidate understanding to verify against the callers, the tests, and the running system, not a verified account of how the code behaves. Understanding first doesn't make the change safe; it stops you from editing in the dark.
Why asking for a change first is the dangerous order
"Fix this" and "refactor this" put the model straight into editing mode, where its job is to produce different code — not to understand the code it's changing. A model optimizing for a cleaner result will smooth over exactly the things that make code fragile, because they look like mess. Here's what a change-first request walks past:
- The intentional oddity. A strange special case or an early empty return often guards a real requirement no comment records; "cleaning it up" removes the guard.
- The load-bearing assumption. The code may assume a directory layout, an input shape, or an ordering that holds today — a rewrite can break the assumption without ever naming it.
- The invisible dependency. A caller, a test, or another system may rely on the exact output or side effect; the model can't see them from one function.
- The narration trap. Asked to explain in passing, a model tends to restate what the code visibly does and invent a plausible reason for the parts it can't actually see.
- The missing context. The one function you pasted rarely contains the answer; without being told to flag that, the model fills the gap with a confident guess instead of "can't tell from this."
Step 1: Ask for an explanation, and say "don't change it yet"
Make the explanation its own pass, explicitly separated from the fix. The instruction is short: "Explain this code. Do not rewrite or suggest changes yet." That one boundary keeps the model in understanding mode instead of jumping to a rewrite, so you get the reasoning before the edit rather than buried under it. It also changes what you're evaluating — an explanation you can check against reality, instead of a diff you have to reverse-engineer.
The quality of an explanation depends less on the model than on what you ask it to produce. "Explain this code" gets you line-by-line narration; a real explanation contract gets you understanding. The Code Explanation Prompt builds that contract: you pick the mode (a function walkthrough, an architecture overview, or legacy-code analysis), the audience, and the depth, and it assembles a prompt that asks for purpose, execution flow, design decisions, and — the part that matters most — an assumptions section that labels intent as inference. Be exact about what it is: it builds the prompt and nothing else. It doesn't read, run, or inspect your code; the explanation is the model's, produced when you run the prompt in your own assistant on the snippet you provide.
Step 2: Name the fields the explanation must cover
A useful explanation isn't a paragraph; it's a set of named answers you can act on. Ask the pass to cover: the purpose, the inputs and outputs, the main flow, the external calls and dependencies, the state changes and side effects, the assumptions the code relies on, the edge cases and error handling, the lines that would be risky to change, what can't be determined from the snippet, and the questions to answer before editing. Each field maps to a way the change could go wrong — side effects catch the hidden dependency, assumptions catch the fragile foundation, risky lines catch the intentional oddity.
Two fields do most of the protective work. The **risky lines** field forces the model to point at what it would be nervous to touch and say why, which is the closest thing you get to a pre-change warning. The **questions before editing** field turns the explanation into a to-do list: the things you need to confirm — from a caller, a test, a teammate — before a change is anything other than a guess. An explanation that ends in good questions is more useful than one that ends in false confidence.
Step 3: Make it separate fact from inference, especially on old code
The single most important instruction in a code explanation is the honesty rule: separate what the code demonstrably does from why it might do it. What a line does is verifiable from the source; why it was written that way is a guess unless a comment, a test, or a telling name evidences it. Tell the model to label every claim about intent, history, or rationale as an inference, and to write "cannot be determined from this code" rather than fill a gap with a smooth story. An explanation that admits what it can't see is far more trustworthy than one that narrates a complete, confident account of code it only half-understands.
This matters most on unfamiliar and legacy code, where the names lie and the comments describe what the code used to do. The Legacy Code Analysis resource is built for exactly that: it walks the actual behavior rather than the apparent one, treats a strange special case as evidence of a forgotten requirement to infer and label, and ends with a maintenance brief — what's safe to touch, what's load-bearing, and what to find out before changing anything. When the goal is to understand rules rather than mechanics — pricing, eligibility, policy code — the Understand Business Logic resource explains the implemented rules in plain terms and keeps intent labeled as inference. Both are ready-made explanation prompts you run yourself; they produce a careful reading of the code, not a verified one.
Step 4: If the code is too big for one message, keep it intact
When the thing you need explained spans more than a single function — several files, a whole module — pasting it in blind causes its own failure: a function severed across messages, a code fence broken so structure collapses, files blended into anonymous soup the model can't reference. Send it in order, at boundaries that keep code whole. The Split Large Codebase Context resource does this: it chunks at file boundaries, keeps fenced blocks atomic, lists the files in each chunk, and tells the model to hold off until the whole thing has arrived — so the explanation runs against the complete picture, not half of it.
Whatever fits, give the model its edges. Say which file each snippet comes from, what calls into it, and what it calls out to, and tell it plainly to explain only what's visible and mark everything else as missing context. The failure mode with a partial paste isn't that the model refuses — it's that it invents the parts it can't see. A boundary instruction ("only explain what's in front of you; flag what you'd need to see the rest") is what keeps a partial explanation honest about being partial.
Step 5: Verify the explanation before you trust it
An explanation is a reading of the code, and a reading can be wrong. The model can misread a misleading name exactly as a human can, state an inference as if it were fact, or miss the one edge case that matters. So treat the explanation as a hypothesis and check it against things the model never saw: the callers that actually use the function, the tests that pin its behavior, the logs or a quick run that show what it really does, and your own knowledge of the domain. Where the explanation made an inference, that's precisely where to look first.
Read it for what it doesn't guarantee, too. The list of risky lines and edge cases is a starting set, not an exhaustive audit — the absence of a warning isn't proof a change is safe. And understanding the code, however well, doesn't make it safe to change: it lowers the odds of a blind break, but the change is its own separate pass with its own verification. The explanation's real output is a better-informed you and a short list of things to confirm — not a green light.
Common mistakes
The ways an explanation pass stops protecting you:
- Skipping straight to "refactor this." The change-first order puts the model in editing mode before either of you understands what the code guards.
- Accepting narration as understanding. "This loop iterates over items" restates the visible code; ask for purpose, assumptions, and risks, not a play-by-play.
- Letting inference pass as fact. If the model doesn't label guesses about intent, a plausible story reads exactly like a verified one — require the split.
- Pasting a fragment with no boundaries. Give it one function out of ten and it invents the other nine; tell it to flag missing context instead.
- Trusting the risk list as complete. The edge cases it names are a starting set; no warning is not a guarantee of safety.
- Treating understanding as permission to change. A good explanation is a precondition for a careful change; it isn't the change, and it isn't its verification.
A worked example: an upload-path function
Take a small function nobody wants to touch and run the explanation pass before the refactor.
"Refactor this" on unfamiliar code vs. an explanation pass that surfaces the intentional empty return, the layout assumption, and the questions to answer firstTHE REQUEST (on code you don't fully understand):
"Refactor this upload-path function to make it cleaner."
WHAT THE CODE ROUGHLY DOES:
find the app base directory
walk up to its parent (assumed to be the web root)
if there's no web.config there -> return "" (empty)
otherwise -> return <web root>/Upload
WHAT A BLIND "CLEAN IT UP" WALKS PAST:
- the empty return may be intentional -- a signal that the layout is invalid
- it assumes the base dir's parent IS the web root (a bin-like deployment)
- a caller may depend on empty-means-invalid, not empty-means-error
- changing how the path resolves can break upload/download in some deployments
BETTER: ASK FOR EXPLANATION FIRST (do not change yet):
"Explain this code before suggesting any change. Do not rewrite it yet.
For each part, tell me:
- what it does
- what it assumes about the runtime directory layout
- what can return empty, and whether that looks intentional
- what a caller might depend on
- what could change if we added a fallback path
- what you cannot tell from this snippet alone
- the questions you'd need answered before editing"
THE EXPLANATION SHAPE YOU WANT BACK:
Purpose: resolve the web root from the app base dir; return <root>/Upload.
Returns empty: when the base dir is empty, the parent is missing, or
web.config is absent.
Assumption (INFERENCE): the base dir's parent is the web root (bin-like
layout) -- not evidenced in this snippet.
Risky to change: the empty return may be a deliberate "invalid layout" signal;
adding a fallback could hide that and change behavior somewhere.
Cannot tell here: which layouts must be supported; who calls this and why.
Questions first: is empty-return a signal callers rely on? which hosting
layouts must stay supported? should a fallback run only when
the web.config check fails?
YOUR NEXT MOVE: check the callers and one real deployment before you touch a line.
Where this fits in NewPrompt
Explaining code is the first pass in a small family of coding jobs, and its whole value is staying separate from the others. The Code Explanation Prompt builds the understanding contract; the Legacy Code Analysis and Understand Business Logic resources are ready-made versions of it for old code and business rules; and the Split Large Codebase Context resource keeps a multi-file paste intact. Every one of them builds a prompt you run yourself — none reads, runs, tests, or reviews your code, and none connects to your repository or editor.
Once you actually understand the code, the change is a different pass with a different tool. If the goal is a behavior-preserving cleanup, the Refactor Prompt Builder writes that as a contract that names what must not change — and the explanation you just did is exactly what tells you what to protect. Judging the code for problems is a review pass, diagnosing a failure is a debugging pass, and understanding a whole codebase for onboarding is a broader job than one function before an edit — each is its own move, deliberately not folded into this one. Explanation asks "how does this work and why?"; the others ask "what's wrong," "why did it fail," or "how should it change."
The reason to run it first is simple: the model reads the same lines you do, and can be fooled by the same misleading name. An explanation is a map drawn from the code alone — useful for planning the route, no substitute for walking the territory. Before you change the code, check the map against the callers, the tests, and the running system, because the confident version the model hands you is still a guess until you've confirmed it. Understanding the code is what lets you change it on purpose instead of on faith — and the checking that earns that confidence is yours to do.