Site

App: Code Explainer — File Analysis

Tutorial A2.0  •  AI / Learn / Applications

A2.0 What This Teaches

This tutorial builds a command-line tool that reads a source file and asks Claude to explain it. It covers:

A2.1 Application Design

The tool follows five steps in sequence:
  1. Read the filename from sys.argv[1]
  2. Read the file contents into a string
  3. Build a prompt that embeds the code in a fenced code block
  4. Call the API with a system prompt that enforces a structured response format
  5. Print the result to the terminal
Each step is small enough to understand and test independently. Keeping them separate also makes it easy to extend the tool later - for example, by writing the explanation to a file instead of printing it.

A2.2 Reading the File

Read the file with open() inside a try/except block so a bad path produces a readable error instead of a traceback. Also warn early if the file is large enough to generate a significant token bill.
# read_file.py - isolated file reading with error handling and size check.
import sys

MAX_BYTES = 50_000   # roughly 10,000-15,000 tokens depending on content

def read_source_file(path: str) -> str:
    try:
        with open(path, "r", encoding="utf-8") as f:
            source = f.read()
    except FileNotFoundError:
        print(f"Error: file not found: {path}")
        sys.exit(1)

    if len(source) > MAX_BYTES:
        print(
            f"Warning: {path} is {len(source):,} bytes. "
            "Token cost will be high. Consider explaining a smaller section."
        )

    return source
sys.exit(1) stops the program immediately with a non-zero exit code, which signals failure to any shell script or CI system that called the tool.

A2.3 Building the Prompt

Wrap the source code in a fenced code block inside the prompt. This gives the model clear boundaries around the code and lets it apply syntax-aware reasoning. Detect the language from the file extension so the fence label is accurate.
# detect_language.py - map file extension to language name.
from pathlib import Path

EXT_TO_LANG = {
    ".py":   "Python",
    ".js":   "JavaScript",
    ".cpp":  "C++",
    ".rs":   "Rust",
    ".cs":   "C#",
    ".java": "Java",
    ".go":   "Go",
}

def detect_language(filename: str) -> str:
    ext = Path(filename).suffix.lower()
    return EXT_TO_LANG.get(ext, "code")

def build_prompt(filename: str, code: str) -> str:
    language = detect_language(filename)
    return f"Explain this {language} code:\n\n```{language}\n{code}\n```"
If the extension is not in the table, the language falls back to "code", which is a valid fence label and still gives the model enough context to work with.

A2.4 The System Prompt

A system prompt sets the model's role and output format before the user message arrives. Specifying the exact structure here means every explanation follows the same four-part layout, regardless of which file you pass.
# system_prompt.py - the explanation format.
SYSTEM = (
    "You are a code explainer. Structure your response as:\n"
    "(1) Purpose - one sentence.\n"
    "(2) Key Components - bulleted list.\n"
    "(3) How It Works - step-by-step.\n"
    "(4) Potential Issues - any risks or gotchas."
)
Numbered labels like (1) are more reliable than prose section headers when you want consistent output - the model treats them as required fields to fill in.

A2.5 Complete Implementation

# explain.py - explain a source file using Claude.
# Usage: python explain.py <filename>

import sys
from pathlib import Path
import anthropic

MAX_BYTES = 50_000

EXT_TO_LANG = {
    ".py": "Python", ".js": "JavaScript", ".cpp": "C++",
    ".rs": "Rust",   ".cs": "C#",         ".java": "Java", ".go": "Go",
}

SYSTEM = (
    "You are a code explainer. Structure your response as:\n"
    "(1) Purpose - one sentence.\n"
    "(2) Key Components - bulleted list.\n"
    "(3) How It Works - step-by-step.\n"
    "(4) Potential Issues - any risks or gotchas."
)

def detect_language(filename: str) -> str:
    return EXT_TO_LANG.get(Path(filename).suffix.lower(), "code")

def main():
    if len(sys.argv) < 2:
        print("Usage: python explain.py <filename>")
        sys.exit(1)

    filename = sys.argv[1]

    try:
        with open(filename, "r", encoding="utf-8") as f:
            code = f.read()
    except FileNotFoundError:
        print(f"Error: file not found: {filename}")
        sys.exit(1)

    if len(code) > MAX_BYTES:
        print(f"Warning: {filename} is {len(code):,} bytes - token cost will be high.")

    language = detect_language(filename)
    prompt = f"Explain this {language} code:\n\n```{language}\n{code}\n```"

    client = anthropic.Anthropic()
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=SYSTEM,
        messages=[{"role": "user", "content": prompt}],
    )

    print(response.content[0].text)

if __name__ == "__main__":
    main()

A2.6 Sample Output

Running the tool on a small Python file that defines a clamp() function produces output like this:
$ python explain.py clamp.py

(1) Purpose - Constrains a numeric value to lie within a specified minimum and maximum range.

(2) Key Components
- clamp(value, lo, hi): the single public function
- Parameters: value (the number to constrain), lo (lower bound), hi (upper bound)
- Return: the nearest boundary value if out of range, otherwise value unchanged

(3) How It Works
1. Compare value against lo - if below, return lo immediately.
2. Compare value against hi - if above, return hi.
3. If neither condition triggers, return value as-is.

(4) Potential Issues
- No type checking: passing a string raises TypeError at the comparison.
- lo > hi is not validated; results are undefined in that case.
- No docstring or type hints, which makes the expected types ambiguous.

A2.7 Detecting Language from Extension

The mapping used in this tool covers the most common compiled and scripted languages. Add entries as needed for your own projects.
ExtensionLanguage label
.pyPython
.jsJavaScript
.cppC++
.rsRust
.csC#
.javaJava
.goGo
(anything else)code

A2.8 Exercise

Exercise Extend explain.py to accept an optional second argument: a question about the code. If provided, append it to the prompt after the code block:
if len(sys.argv) >= 3:
    question = sys.argv[2]
    prompt += f"\n\nAfter your explanation, answer this specific question: {question}"
Usage example:
python explain.py myfile.py "What does the main function do?"
Test with a file you wrote yourself - ask a question whose answer you already know, then check whether the model's answer is accurate.

A2.9 Common Mistakes

Not handling FileNotFoundError

If you call open(filename) without a try/except and the path is wrong, Python raises an unformatted traceback. Users of a CLI tool expect a clean error message, not a stack trace. Always catch FileNotFoundError and print something actionable before calling sys.exit(1).

Sending an entire large codebase file

Files over 1,000 lines often produce poor explanations - the model has to skim rather than reason carefully. If a file is that large, either explain individual functions by slicing the text, or ask the model to focus on one specific section per call.

Not telling the model the language

Sending raw code without a language label forces the model to guess. For a .cs file that uses LINQ, the model might misread it as Java and produce a subtly wrong explanation. The fenced code block label and the phrase "Explain this C# code" together eliminate the ambiguity.

Key Terms

TermMeaning
sys.argvList of command-line arguments; argv[0] is the script name, argv[1] is the first user argument
pathlib.PathObject-oriented path manipulation; Path(f).suffix returns the file extension including the dot
system promptAn instruction passed to the model before the user message; controls tone and output format
fenced code blockMarkdown syntax wrapping code in triple backticks with an optional language label
FileNotFoundErrorPython exception raised when open() cannot locate the specified path
sys.exit(1)Terminates the program immediately with exit code 1, signaling failure
token costThe number of tokens consumed by a request; large files increase cost and may degrade quality