Prompting for Code Explanation

La Lakshmi Priya Updated 06 Nov 2026
10 min read ·Lesson 92 of 100

Why Code Explanation Needs Careful Prompting Developers read far more code than they write: when joining a project, reviewing a pull request, maintaining old features, or learning a new library. Language models can explain code quickly, but an explanation is only useful if it is at the right level, connected to the reader's purpose, and correct.

The last point is critical. A model explaining code describes what the code appears to do. It can describe the intended behavior instead of the actual behavior, which hides bugs instead of revealing them. This lesson shows how to prompt for useful explanations and how to check them against what the code really does.

Explanation Type Answers

  1. Overview What is this code for?
  2. Line-by-line walkthrough What does each part do?
  3. Execution trace What happens with this specific input?
  4. Data flow Where does this value come from, and where does it go?
  5. Design reasoning Why might it be written this way?
  6. Explanation for a specific purpose What do I need to know to change, review, or use it? The Context an Explanation Needs A function on its own is often ambiguous. Provide:

Context Why Language and framework versions Behavior can differ between versions Where the code is called from Shows what inputs it actually receives Related definitions Models, helper functions, constants used inside it The reader's level Determines depth and vocabulary The reader's purpose Learning, reviewing, debugging, or modifying Number the Lines Line numbers let both the model and the reader refer to exact locations. Add them in code before building the prompt (Lesson 7):

def number_lines(code: str) -> str: return "\n".join(f"{i:>3}: {line}" for i, line in enumerate(code.splitlines(), start=1)) {i:>3} right-aligns the line number in three characters, so the code stays aligned.

Type 1: Overview Use it when: you need to know what code is for before reading it in detail.

Prompt Give an overview of the {language} code in for {reader}.

  1. Purpose: what this code does, in one or two sentences.
  2. Inputs and outputs: what it receives and what it returns or changes.
  3. Main steps: 3 to 6 bullet points.
  4. Dependencies: other functions, models, or services it relies on.

Describe only what the code actually does. If you are unsure what an external function does, say so instead of guessing.

{numbered_code} Why It Works A fixed structure produces overviews that are quick to scan. "Only what the code actually does" discourages describing the intended purpose based on function names alone. Admitting uncertainty about external functions prevents invented descriptions of code the model cannot see. Type 2: Line-by-Line Walkthrough Use it when: learning, or when a small piece of code is hard to understand.

Prompt Explain the code in line by line for {reader}.

For each line or small group of lines:

  • Line numbers
  • What it does
  • Any language feature used, explained briefly the first time it appears

Then point out any line whose behavior might surprise a reader, and why.

{numbered_code} Why It Works Language features explained once teach concepts like list comprehensions or decorators in context. "Lines that might surprise a reader" directs attention to subtle behavior, which is where bugs and misunderstandings hide. Type 3: Execution Trace With a Concrete Input Use it when: you need to know exactly what happens for a specific input, which is the most reliable way to understand and debug code.

Prompt Trace the execution of the function in for this call: {call}

For each step, show the line number and the value of every variable that changes. Then state the final return value or the exception raised.

Base the trace only on the code. Do not assume what the code is "supposed" to do.

{numbered_code} Why Traces Are Powerful An overview says what the code is meant to do. A trace with real values shows what it actually does. If the traced result differs from what the function's name or docstring suggests, you have found a bug.

Verifying Explanations by Running the Code A model's trace or prediction is itself a claim that can be wrong. When the code is safe to run, such as your own pure functions, compare the model's predictions with real results.

Example The function below is supposed to return the average progress of active students:

SOURCE = '''def average_progress(students): total = 0 for s in students: if s["active"]: total += s["progress"] return total / len(students)''' Suppose a model, asked to explain and predict outputs, says: "It returns the average progress of active students", and predicts 80.0 for one active student at 80% and one inactive student at 20%.

namespace = {} exec(SOURCE, namespace) # our own, known code, used here only for testing average_progress = namespace["average_progress"]

def check_predictions(func, predictions): """Compare predicted results with actual results for each set of arguments.""" results = [] for args, predicted in predictions: try: actual = func(*args) except Exception as error: actual = f"raises {type(error).name}" results.append((predicted == actual, predicted, actual)) return results

predictions = [ (([{"active": True, "progress": 80}, {"active": True, "progress": 60}],), 70.0), (([{"active": True, "progress": 80}, {"active": False, "progress": 20}],), 80.0), (([],), 0), ]

for matches, predicted, actual in check_predictions(average_progress, predictions): print("OK " if matches else "DIFF", "predicted:", predicted, "actual:", actual) Output:

OK predicted: 70.0 actual: 70.0 DIFF predicted: 80.0 actual: 40.0 DIFF predicted: 0 actual: raises ZeroDivisionError Code Explanation exec(SOURCE, namespace) defines the function from a string, so this example is self-contained. In your project, import the function normally. Never use exec on untrusted or model-generated code (Lesson 58). check_predictions() runs the function for each set of arguments, captures exceptions as text, and compares the result with the prediction. What the Mismatches Reveal Second case: the code adds only active students' progress, but divides by all students: (80) ÷ 2 = 40. The model explained the intended behavior, an average of active students, which would be 80. The code has a bug. Third case: an empty list causes division by zero. The model predicted 0, a reasonable intention, but the code crashes. The explanation sounded correct, but running the code exposed two bugs. This is why explanations of important code should be checked against reality.

Prompting to Reduce This Problem Asking for a trace (Type 3) instead of a summary makes the model follow the actual code more closely. Adding "Base the trace only on the code" and asking explicitly "Does the code's behavior match its name and docstring?" further encourages it to notice gaps between intention and implementation.

Type 4: Data Flow Use it when: you need to know where a value comes from and where it goes, for example when changing a field or investigating wrong data.

Prompt In the code in , trace the value {variable_or_field}:

  1. Where it comes from (input, database, calculation, or external call), with line numbers
  2. Every place it is changed, with line numbers
  3. Where it is used or sent (returned, saved, displayed, logged)
  4. Any place where it could be missing, empty, or of an unexpected type

{numbered_code} Why It Works Data flow questions are common in maintenance work: "If I change how discount is calculated, what else is affected?" A structured trace answers this directly, and the "could be missing" step finds fragile points.

Type 5: Design Reasoning Use it when: you want to understand why code might be written a certain way, for example before changing it.

Prompt For the code in , explain the likely reasons for these design choices: {choices}

For each choice:

  • The most likely reason
  • One alternative design and its trade-off

Mark these as likely reasons, not facts, since the original author's intent is not known. If the project's documentation or comments state a reason, prefer that. Why It Works The model cannot know the author's intent. Asking it to label reasons as likely, and to prefer documented reasons, keeps the explanation honest. Alternatives with trade-offs help you decide whether a change is safe.

Type 6: Explanation for a Specific Purpose Different tasks need different explanations of the same code.

Purpose What to Ask For Using the code Inputs, outputs, errors, and a usage example Reviewing the code Behavior, risks, edge cases, and assumptions Modifying the code Where to make the change, what else depends on it, and what could break Learning from the code Concepts and patterns used, with explanations Example: Explaining for Modification I need to change the code in so that {change}.

Explain:

  1. Which lines need to change
  2. Which other parts of this code depend on those lines
  3. Which callers might be affected, based on
  4. Which tests should be added or updated

Do not write the change yet. "Do not write the change yet" separates understanding from implementation, so you can check the plan before any code is generated (Lesson 91).

Explaining Larger Codebases For more code than fits comfortably in a prompt:

Start with structure: provide the folder tree and file names, and ask for a map of what each part likely does. Go file by file: explain the most important files individually. Follow one flow: trace one user action, such as enrolling in a course, through views, services, and models. Collect the results into an onboarding document (Lesson 97). Lessons 101–106 cover retrieval, which allows question answering over large codebases and documentation.

Common Mistakes Mistake 1: Explaining Without Context What it is: Pasting a function without its callers, related models, or versions.

Why it happens: The function seems self-contained.

What it causes: Guesses about inputs and dependencies presented as facts.

How to fix it: Provide versions, related definitions, and callers.

Mistake 2: Accepting Intended Behavior as Actual Behavior What it is: Trusting an explanation that matches the function's name.

Why it happens: The explanation sounds reasonable.

What it causes: Bugs remain hidden, as in the average progress example.

How to fix it: Ask for execution traces, and run the code with test inputs where possible.

Mistake 3: Wrong Level of Detail What it is: A line-by-line explanation when you needed an overview, or the reverse.

Why it happens: The purpose is not stated.

What it causes: Time wasted reading the wrong kind of explanation.

How to fix it: State the reader and purpose, and choose the explanation type.

Mistake 4: Treating Design Reasons as Facts What it is: Believing the model's explanation of why the author made a choice.

Why it happens: The explanation is confident.

What it causes: Wrong assumptions when changing code.

How to fix it: Ask for likely reasons, labeled as such, and check documentation, comments, or commit history.

Best Practices Provide context. Versions, related code, callers, reader, and purpose.

Number the lines. Make explanations precise.

Choose the explanation type for the task. Overview, walkthrough, trace, data flow, design, or purpose-specific.

Prefer traces with concrete inputs for important code. They reveal actual behavior.

Verify by running safe code. Compare predictions with real results.

Label uncertainty. External functions and design intent should not be presented as facts.

Try It Yourself Exercise 1: Overview and Trace Choose a function from one of your projects. Ask for an overview (Type 1) and a trace with a concrete input (Type 3). Run the function with the same input and compare the result with the trace.

Exercise 2: Find the Gap Write a function with a small bug, such as an off-by-one error in a loop. Ask a model to explain it and to predict outputs for three inputs. Use check_predictions() to compare. Then ask "Does the code's behavior match its name?" and check whether the model now finds the bug.

Exercise 3: Explain for Modification Take a view or function that you want to change. Use the "explanation for modification" prompt with the code and at least one caller. Compare the model's list of affected lines and callers with your own analysis.

0 Comments

Reviewed before they appear

No comments yet.

Prompt Engineering
Prompt Engineering Introduction How Large Language Models Respond to Prompts Prompting Basics Elements of an Effective Prompt Writing Clear Instructions for AI Models Adding Context to a Prompt Providing Input Data in Prompts Using Output Indicators and Response Formats Prompt Formatting: Questions, Instructions, and Templates General Tips for Designing Better Prompts Common Prompt Engineering Mistakes Iterative Prompt Design and Refinement Understanding Instruction, Context, Input, and Output How Specificity and Conciseness Affect Prompt Quality Breaking Large Tasks into Smaller Prompting Tasks Zero-Shot Prompting Zero-Shot Prompting Examples for Everyday Tasks Zero-Shot Text Classification Zero-Shot Question Answering Zero-Shot Summarization Zero-Shot Translation Zero-Shot Prompting for Information Extraction When to Use Zero-Shot Prompting Limitations of Zero-Shot Prompting Improving Weak Zero-Shot Prompts Few-Shot Prompting One-Shot Prompting vs Few-Shot Prompting Choosing Good Examples for Few-Shot Prompts Formatting Examples in Few-Shot Prompts Few-Shot Classification with Examples Few-Shot Prompting for Structured Output Few-Shot Prompting Zero-Shot vs Few-Shot Prompting Role Prompting and Persona Instructions Instruction-Based Prompting Task-Oriented Prompt Templates Using Delimiters to Separate Prompt Sections Prompt Templates with Variables Chain-of-Thought Prompting Zero-Shot Chain-of-Thought Prompting Few-Shot Chain-of-Thought Prompting Automatic Chain-of-Thought (Auto-CoT) When Chain-of-Thought Prompting Helps Limitations of Chain-of-Thought Prompting Self-Consistency Prompting Comparing Chain-of-Thought and Self-Consistency Tree of Thoughts (ToT) Prompting Exploring Multiple Solutions with Tree of Thoughts Least-to-Most Prompting Complexity-Based Prompting Maieutic Prompting Self-Consistency: Sampling Multiple Reasoning Paths Self-Consistency for Arithmetic and Logic Tasks Tree of Thoughts: Search, Evaluation, and Backtracking Meta Prompting Generate Knowledge Prompting Directional Stimulus Prompting Program-Aided Language Models (PAL) React: Reasoning and Acting with Language Models Reflexion: Improving Agents Through Feedback Prompt Chaining Designing Multi-Step Prompt Chains Decomposing Complex Tasks into Prompt Chains Automatic Prompt Engineer (APE) Active-Prompt: Selecting Useful Reasoning Examples Optimizing Prompts Systematically Prompt Iteration and Versioning Building Reusable Prompt Libraries Testing Prompt Variants Prompt Chaining Fundamentals Passing Outputs Between Prompt Steps Prompt Chains for Summarization and Extraction Validating Intermediate Results in a Prompt Chain Debugging a Multi-Step Prompt Workflow Graph Prompting Multimodal Chain-of-Thought Prompting Automatic Reasoning and Tool-Use (ART) Prompting for Tool Selection and Use Combining Prompting Techniques for Complex Tasks Prompts for Text Summarization Prompts for Information Extraction Prompts for Text Classification Prompts for Question Answering Prompts for Sentiment Analysis Prompts for Rewriting and Editing Prompts for Translation and Localization Prompts for Brainstorming and Creative Writing Prompts for Generating Structured Data Prompts for Explaining Technical Concepts Prompts for Generating and Reviewing Code Prompting for Code Generation Prompting for Code Explanation Prompting for Debugging and Error Analysis Prompting for Unit Test Generation Prompting for SQL Query Generation Prompting for API Documentation Prompting for Technical Documentation Using Prompting to Extract Requirements Creating JSON-Style Structured Responses with Prompts Designing Prompts for Repeatable Developer Workflows
Ask about this post
AI Ask about this post

Ask questions about Prompting for Code Explanation and get answers drawn from it.

Signed-in readers only.