A skill that helps users understand code, diffs, and the changes an agent made. It picks the output format that fits: plain prose, a diagram, or a self-contained HTML explainer.
To make complex code accessible and understandable by breaking down technical concepts into relatable analogies, visual representations, and clear explanations. This skill is triggered when users ask questions like “How does this work?”, “Explain this code”, “Explain this diff”, or “What did you change?”
Agents now write more of the code, so more of our work is reading and checking what they produced. The format ladder below follows Andrej Karpathy’s post on this: prose is often not the easiest format to understand, and an LLM can cheaply build a diagram or a one-off web page instead.
Use the lowest step that makes the explanation clear. Go up a step when the reader asks or is still lost.
| Step | Format | Use it for |
|---|---|---|
| 1 | Prose | One function or one small concept |
| 2 | Diagram (ASCII inline, Mermaid if it renders) | Flow, state, call stacks, module structure |
| 3 | Self-contained HTML file | Multi-part systems, large diffs, before/after comparisons, or when asked |
| 4 | Explainer video | Only when the user asks. It needs external tools or API keys |
Prose follows the ask-ste-writing rules (about 80% ASD-STE100): short sentences, active voice, one meaning per word. ask copy ask-explaining-code installs that skill as a dependency.
HTML explainers are disposable:
.html file with no build step. Use a CDN only when needed (for example, to render Mermaid).Lay an HTML explainer out as a reference sheet: labelled panels (A, B, C…) with one topic each, annotations on real lines from the code or diff, tables instead of paragraphs, ✓/✗ columns for before/after, and a title block that names the source files and the commit or branch.
When asked to explain code, follow this approach:
Begin with a high-level analogy that captures the essence:
"Think of this function like a restaurant kitchen:
- Orders come in (input parameters)
- Chefs process them (business logic)
- Dishes go out (return values)"
Use ASCII art to show structure, flow, or relationships:
┌─────────────┐
│ Request │
└──────┬──────┘
│
▼
┌─────────────┐
│ Validation │
└──────┬──────┘
│
▼
┌─────────────┐
│ Database │
└──────┬──────┘
│
▼
┌─────────────┐
│ Response │
└─────────────┘
Break down the execution flow with numbered steps and inline comments:
# Step 1: Receive the user's data
def process_user(user_data):
# Step 2: Check if the data is valid (like checking ID at a club entrance)
if not validate(user_data):
return "Invalid data"
# Step 3: Transform the data (like formatting a letter before mailing)
cleaned = sanitize(user_data)
# Step 4: Save to database (like filing a document)
save_to_db(cleaned)
# Step 5: Send confirmation (like a receipt)
return "Success!"
Connect technical concepts to everyday experiences:
| Technical Concept | Analogy |
|---|---|
| Variables | Labeled boxes that hold things |
| Functions | Vending machines (input → process → output) |
| Loops | Assembly line workers repeating tasks |
| Conditionals | Forks in the road with signs |
| Classes | Cookie cutters (template for objects) |
| APIs | Restaurant menu (you order, kitchen handles it) |
| Caching | Keeping frequently used items on your desk |
| Recursion | Russian nesting dolls |
| Async/Await | Ordering food delivery while doing other things |
| Database indexes | Book index (quick lookup) |
Proactively clarify confusing points:
⚠️ Common Misconception:
"Does this loop run forever?"
No! The loop stops when i reaches 10. Think of it like climbing stairs:
you start at step 0 and stop when you reach step 10.
When explaining code, use this format:
User: “How does this work?”
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
AI Response:
Quick Summary: This calculates Fibonacci numbers using recursion.
Analogy: Think of this like a family tree working backwards. To find out how many ancestors you have at generation N, you need to count ancestors from both your parents’ sides (N-1 and N-2), then add them together.
Visual Flow:
fibonacci(4) ├─ fibonacci(3) │ ├─ fibonacci(2) │ │ ├─ fibonacci(1) → 1 │ │ └─ fibonacci(0) → 0 │ └─ fibonacci(1) → 1 └─ fibonacci(2) ├─ fibonacci(1) → 1 └─ fibonacci(0) → 0Step-by-Step:
- Base case check: If n is 0 or 1, just return n (these are the “leaves” of our tree)
- Recursive split: Otherwise, split into two smaller problems
- Combine results: Add the results from both branches
Key Concept: Each call spawns two more calls, creating a tree of calculations that eventually hit the base cases and bubble back up.
⚠️ Common Pitfall: This is elegant but slow! It recalculates the same values many times. For fibonacci(4), it calculates fibonacci(2) twice. For larger numbers, this becomes exponentially wasteful.
User: “What’s happening in this decorator?”
def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
return None
return wrapper
return decorator
AI Response:
Quick Summary: This is a decorator that automatically retries a function if it fails.
Analogy: Think of this like a persistent salesperson:
- retry = The company policy (“try up to 3 times”)
- decorator = The training manual for the salesperson
- wrapper = The actual salesperson who follows the policy
Nesting Structure:
┌─────────────────────────────────────┐ │ retry(max_attempts=3) │ ← Policy setter │ ┌───────────────────────────────┐ │ │ │ decorator(func) │ │ ← Training manual │ │ ┌─────────────────────────┐ │ │ │ │ │ wrapper(*args, **kwargs)│ │ │ ← Actual worker │ │ │ • Try the task │ │ │ │ │ │ • If fail, retry │ │ │ │ │ │ • Give up after N tries│ │ │ │ │ └─────────────────────────┘ │ │ │ └───────────────────────────────┘ │ └─────────────────────────────────────┘Step-by-Step:
- Outer function (
retry): Sets the retry policy (how many attempts)- Middle function (
decorator): Receives the function to be wrapped- Inner function (
wrapper): Does the actual work:
- Loops up to
max_attemptstimes- Tries to run the original function
- If it succeeds, returns immediately
- If it fails, catches the error and tries again
- On the last attempt, lets the error bubble up
Usage:
@retry(max_attempts=3) def fetch_data(): # This will auto-retry up to 3 times if it fails return requests.get(url)⚠️ Common Misconception: “Why three nested functions?”
Each level serves a purpose:
- Level 1: Configuration (how many retries?)
- Level 2: Wrapping (which function to retry?)
- Level 3: Execution (actually doing the retries)
Start → Check → Process → Save → End
↓
Error? → Retry
[Idle] --request--> [Loading] --success--> [Done]
|
error
↓
[Error]
Array: [1, 2, 3, 4, 5]
↑ ↑
index 0 index 4
Tree: 10
/ \
5 15
/ \
2 7
main()
└─ processData()
└─ validate()
└─ checkEmail() ← Currently executing