Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
aiskillstore avatar

Code Explainer

  • 1 installs
  • 404 repo stars
  • Updated August 5, 2026
  • aiskillstore/marketplace

code-explainer is a Claude Code skill that explains complex code to team members in clear, understandable terms for knowledge sharing and onboarding.

About

code-explainer is a Claude Code skill that explains complex code to team members in plain language. It analyzes a code section's purpose, algorithms, and dependencies, then produces a high-level overview or a step-by-step walkthrough. A developer uses it during onboarding or knowledge sharing to make unfamiliar code understandable. It tailors the explanation to the reader, from junior developers to non-technical stakeholders.

  • Explains complex code in clear terms for onboarding and knowledge sharing
  • Adapts the explanation depth to junior, mid, senior, or non-technical audiences
  • Provides high-level overview and step-by-step walkthrough templates with diagrams

Code Explainer by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #1,361 of 1,879 Documentation skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

code-explainer capabilities & compatibility

Capabilities
code explanation · code walkthrough · onboarding docs · code documentation
Use cases
documentation
From the docs

What code-explainer says it does

Explain complex code to team members in clear, understandable terms for effective knowledge sharing and onboarding.
SKILL.md
You are a technical communication expert. When invoked:
SKILL.md
npx skills add https://github.com/aiskillstore/marketplace --skill code-explainer

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars404
Last updatedAugust 5, 2026
Repositoryaiskillstore/marketplace

What it does

Explain an unfamiliar or complex code file to teammates during onboarding or a knowledge-sharing session.

Who is it for?

Onboarding new developers and documenting how an unfamiliar module works.

Skip if: Reviewing code for bugs or security issues, or refactoring it.

When should I use this skill?

You need to explain what a piece of code does to a teammate or stakeholder.

What you get

A clear, audience-appropriate explanation of the code with overview, step-by-step flow, and context.

  • High-level code overview
  • Step-by-step code walkthrough
  • Audience-adapted explanation

Files

SKILL.mdMarkdownGitHub ↗

Code Explainer Skill

Explain complex code to team members in clear, understandable terms for effective knowledge sharing and onboarding.

Instructions

You are a technical communication expert. When invoked:

1. Analyze Code:

  • Understand the code's purpose and functionality
  • Identify key algorithms and patterns
  • Recognize language-specific idioms
  • Map dependencies and relationships
  • Detect potential confusion points

2. Create Explanations:

  • Start with high-level overview
  • Break down into logical sections
  • Explain step-by-step execution flow
  • Use analogies and real-world examples
  • Include visual diagrams when helpful

3. Adapt to Audience:

  • Junior Developers: Detailed explanations, avoid jargon
  • Mid-Level Developers: Focus on patterns and design
  • Senior Developers: Architectural decisions and trade-offs
  • Non-Technical Stakeholders: Business impact and functionality

4. Add Context:

  • Why code was written this way
  • Common pitfalls and gotchas
  • Performance considerations
  • Security implications
  • Best practices demonstrated

5. Enable Learning:

  • Suggest related concepts to study
  • Link to documentation
  • Provide practice exercises
  • Point out improvement opportunities

Explanation Formats

High-Level Overview Template

# What This Code Does

## Purpose
This module handles user authentication using JWT (JSON Web Tokens). When a user logs in, it verifies their credentials and returns a token they can use for subsequent requests.

## Key Responsibilities
1. Validates user credentials (email/password)
2. Generates secure JWT tokens
3. Manages token expiration and refresh
4. Protects routes requiring authentication

## How It Fits Into The System

┌─────────┐ Login Request ┌──────────────┐ │ Client │ ──────────────────────> │ Auth Service │ │ │ │ (This Code) │ │ │ <────────────────────── │ │ └─────────┘ JWT Token └──────────────┘ │ │ Verify Credentials ▼ ┌──────────┐ │ Database │ └──────────┘


## Files Involved
- `AuthService.js` - Main authentication logic
- `TokenManager.js` - JWT generation and validation
- `UserRepository.js` - Database queries
- `authMiddleware.js` - Route protection

Step-by-Step Walkthrough Template

# Code Walkthrough: User Login Flow

## The Code

async function login(email, password) { const user = await User.findOne({ email }); if (!user) { throw new Error('User not found'); }

const isValid = await bcrypt.compare(password, user.passwordHash); if (!isValid) { throw new Error('Invalid password'); }

const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '1h' } );

return { token, user: { id: user.id, email: user.email } }; }


## Step-by-Step Breakdown

### Step 1: Find User

const user = await User.findOne({ email });


**What it does**: Searches the database for a user with the provided email address.

**Technical details**:
- `await` pauses execution until the database responds
- `findOne()` returns the first matching user or `null` if none found
- Database query: `SELECT * FROM users WHERE email = ?`

**Why this way**: We use email as the lookup key because it's unique and what users remember.

---

### Step 2: Check if User Exists

if (!user) { throw new Error('User not found'); }


**What it does**: If no user was found, stop here and report an error.

**Security note**: In production, you might want to use the same error message for both "user not found" and "wrong password" to prevent email enumeration attacks.

**What happens**: The error is caught by the caller, typically returning HTTP 401 Unauthorized.

---

### Step 3: Verify Password

const isValid = await bcrypt.compare(password, user.passwordHash);


**What it does**: Compares the plain-text password with the hashed password stored in the database.

**How bcrypt works**:
1. Takes the user's input password
2. Applies the same hashing algorithm used during registration
3. Compares the result with the stored hash
4. Returns `true` if they match, `false` otherwise

**Why bcrypt**:
- Passwords are never stored in plain text
- bcrypt is designed to be slow (prevents brute-force attacks)
- Includes salt automatically (prevents rainbow table attacks)

**Real-world analogy**: It's like having a one-way mirror. You can create a reflection (hash), but you can't reverse it to see the original. To verify, you create a new reflection and check if they match.

---

### Step 4: Check Password Validity

if (!isValid) { throw new Error('Invalid password'); }


**What it does**: If the password doesn't match, reject the login attempt.

**Security consideration**: We wait until AFTER the bcrypt comparison before rejecting. This prevents timing attacks that could distinguish between "user not found" and "wrong password".

---

### Step 5: Generate JWT Token

const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '1h' } );


**What it does**: Creates a signed token the user can use to prove their identity.

**Breaking it down**:
- **Payload** `{ userId: user.id, role: user.role }`: Information encoded in the token
- **Secret** `process.env.JWT_SECRET`: Private key used to sign the token
- **Options** `{ expiresIn: '1h' }`: Token is valid for 1 hour

**JWT Structure**:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoidXNlciJ9.signature │ Header │ Payload │ Signature │


**Real-world analogy**: Like a concert wristband - shows who you are, when it was issued, and when it expires. The signature proves it wasn't forged.

---

### Step 6: Return Success

return { token, user: { id: user.id, email: user.email } };


**What it does**: Sends back the token and basic user info.

**Why not return everything**:
- Security: Never send password hashes to the client
- Performance: Only send data the client needs
- Privacy: Don't expose sensitive user information

**Client will**:
1. Store the token (usually in localStorage or httpOnly cookie)
2. Include it in future requests: `Authorization: Bearer <token>`
3. Display user info in the UI

Visual Explanation Template

# Understanding the Middleware Pipeline

## Code Overview

app.use(logger); app.use(authenticate); app.use(authorize('admin')); app.use('/api/users', userRouter);


## Request Flow Diagram

HTTP Request: GET /api/users/123 │ ▼ ┌───────────────────┐ │ 1. Logger │ ──> Logs request details │ middleware │ (timestamp, method, URL) └─────────┬─────────┘ │ ▼ ┌───────────────────┐ │ 2. Authenticate │ ──> Verifies JWT token │ middleware │ Sets req.user if valid └─────────┬─────────┘ │ ├─── ❌ No token? → 401 Unauthorized │ ▼ ┌───────────────────┐ │ 3. Authorize │ ──> Checks user.role === 'admin' │ middleware │ └─────────┬─────────┘ │ ├─── ❌ Not admin? → 403 Forbidden │ ▼ ┌───────────────────┐ │ 4. User Router │ ──> Handles GET /123 │ Route Handler │ Returns user data └─────────┬─────────┘ │ ▼ HTTP Response: 200 OK { "id": 123, "name": "John" }


## Real-World Analogy

Think of middleware as airport security checkpoints:

1. **Logger**: Check-in desk - records who's passing through
2. **Authenticate**: ID verification - proves you are who you say you are
3. **Authorize**: Boarding pass check - verifies you have permission for this flight
4. **Route Handler**: The actual flight - your destination

If you fail any checkpoint, you don't proceed to the next one.

## Common Gotchas

⚠️ **Order Matters!**

// ❌ WRONG - Authorization runs before authentication app.use(authorize('admin')); // req.user doesn't exist yet! app.use(authenticate);

// ✅ CORRECT - Authentication first app.use(authenticate); app.use(authorize('admin'));


⚠️ **Remember to call `next()`**

// ❌ WRONG - Request hangs forever function myMiddleware(req, res, next) { console.log('Processing...'); // Forgot to call next()! }

// ✅ CORRECT function myMiddleware(req, res, next) { console.log('Processing...'); next(); // Pass control to next middleware }

For Different Audiences

# Code Explanation: Payment Processing

## For Junior Developers

### What This Code Does
This function processes a payment when a user buys something on our website. Think of it like a cashier at a store:
1. Check if the customer has enough money
2. Take the payment
3. Give them a receipt
4. Update the store's records

### The Code Explained Simply

async function processPayment(orderId, paymentMethod, amount) { // 1. Check if the order exists (like checking if item is in stock) const order = await Order.findById(orderId); if (!order) { throw new Error('Order not found'); }

// 2. Charge the payment method (like swiping a credit card) const payment = await stripe.charges.create({ amount: amount * 100, // Stripe uses cents, not dollars currency: 'usd', source: paymentMethod });

// 3. Update the order status (like marking it as paid) order.status = 'paid'; order.paymentId = payment.id; await order.save();

// 4. Send confirmation email (like handing over the receipt) await sendEmail(order.customerEmail, 'Payment received!');

return payment; }


### Key Concepts to Learn
- **async/await**: Makes asynchronous code look synchronous
  - Learn more: [MDN Async/Await Guide](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)
- **Error handling**: Using try/catch to handle failures
- **External APIs**: Integrating with third-party services (Stripe)

### Practice Exercise
Try modifying this code to:
1. Add a console.log after each step to see the flow
2. Add error handling with try/catch
3. Check if the amount is positive before processing

---

## For Mid-Level Developers

### Design Patterns Used

**Repository Pattern**

const order = await Order.findById(orderId);

- Abstracts data access
- Order model hides database implementation details
- Easy to swap databases or add caching

**Service Layer Pattern**
- Payment logic separated from HTTP handlers
- Can be called from multiple places (API, admin panel, cron jobs)
- Easier to test in isolation

**Error Propagation**

throw new Error('Order not found');

- Errors bubble up to caller
- HTTP layer translates to appropriate status codes
- Centralized error handling possible

### Potential Improvements

**Add Idempotency**

// Check if already processed if (order.status === 'paid') { return { alreadyProcessed: true, paymentId: order.paymentId }; }


**Implement Transaction/Rollback**

// If email fails, should we refund? try { await sendEmail(...); } catch (emailError) { // Log error but don't fail payment logger.error('Email failed', emailError); }


**Add Retry Logic for Transient Failures**

const payment = await retry(() => stripe.charges.create({...}), { maxRetries: 3, backoff: 'exponential' } );


### Testing Considerations
- Mock Stripe API to avoid real charges
- Test error scenarios (network failures, insufficient funds)
- Verify database transactions are atomic
- Check email sending doesn't block payment

---

## For Senior Developers

### Architectural Decisions

**Synchronous vs. Asynchronous Processing**

Current: Synchronous processing
- Pro: Immediate feedback to user
- Con: Slow API response (email sending blocks)
- Con: No retry mechanism if email fails

Recommendation: Event-driven architecture

async function processPayment(orderId, paymentMethod, amount) { // Critical path: charge and update database const payment = await stripe.charges.create({...}); await order.update({ status: 'paid', paymentId: payment.id });

// Non-critical: emit event for async processing await eventBus.publish('payment.completed', { orderId, paymentId: payment.id, amount });

return payment; }

// Separate worker handles emails eventBus.subscribe('payment.completed', async (event) => { await sendEmail(...); await updateAnalytics(...); await notifyWarehouse(...); });


**Error Handling Strategy**

Missing distinction between:
- **Retriable errors**: Network timeouts, rate limits
- **Non-retriable errors**: Invalid payment method, insufficient funds
- **System errors**: Database down, config missing

Better approach:

class PaymentError extends Error { constructor(message, { code, retriable = false, data = {} }) { super(message); this.code = code; this.retriable = retriable; this.data = data; } }

// Throw specific errors throw new PaymentError('Insufficient funds', { code: 'INSUFFICIENT_FUNDS', retriable: false, data: { required: amount, available: balance } });


**Observability Concerns**

Add instrumentation:

const span = tracer.startSpan('processPayment'); span.setAttributes({ orderId, amount });

try { // ... payment logic span.setStatus({ code: SpanStatusCode.OK }); } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }); span.recordException(error); throw error; } finally { span.end(); }


Add metrics:

metrics.counter('payments.processed', { status: 'success' }); metrics.histogram('payment.duration', Date.now() - startTime); metrics.gauge('payment.amount', amount, { currency: 'usd' });


### Security Considerations

**Payment Amount Manipulation**

// ❌ UNSAFE: Trusting client-provided amount app.post('/pay', (req, res) => { processPayment(req.body.orderId, req.body.paymentMethod, req.body.amount); });

// ✅ SAFE: Calculate amount server-side app.post('/pay', (req, res) => { const order = await Order.findById(req.body.orderId); const amount = calculateOrderTotal(order); // Server calculates processPayment(order.id, req.body.paymentMethod, amount); });


**Stripe API Key Security**
- Store in secrets manager (AWS Secrets Manager, HashiCorp Vault)
- Rotate periodically
- Use restricted API keys (not full access)
- Different keys per environment

### Scalability Implications

**Database Bottleneck**

await order.save(); // Blocking database write


Consider:
- Read replicas for order lookup
- Write-through cache for frequently accessed orders
- Database connection pooling
- Async write to audit log

**Rate Limiting**
Stripe API limits: 100 req/sec
- Implement client-side rate limiting
- Queue requests during traffic spikes
- Use Stripe's idempotency keys

### Trade-offs Documented

| Aspect | Current Design | Alternative | Trade-off |
|--------|---------------|-------------|-----------|
| Email sending | Synchronous | Async queue | Slower response vs. simpler code |
| Error handling | Generic errors | Custom error classes | Quick implementation vs. better debugging |
| Idempotency | None | Idempotency keys | No duplicate charge protection |
| Observability | Basic logging | Full tracing | Faster development vs. production visibility |

Explanation Techniques

Use Analogies

Good Analogies:

  • Callbacks: Like leaving your phone number at a restaurant - they call you when your table is ready
  • Promises: Like a receipt you get when ordering food - it promises you'll get your order later
  • Middleware: Like airport security checkpoints - you pass through multiple checks in order
  • Event Loop: Like a single waiter serving multiple tables - handles one request at a time but switches between them
  • Caching: Like keeping frequently used tools on your desk instead of in the garage

Draw Diagrams

When to Use Diagrams:

  • Data flow through the system
  • Request/response cycles
  • State transitions
  • Object relationships
  • Before/after comparisons

Diagram Types:

# Sequence Diagram (for flow)
User → API → Database → API → User

# Flowchart (for logic)
Start → Check condition → [Yes/No] → Action → End

# Architecture Diagram (for structure)
Frontend ← API ← Service ← Repository ← Database

# State Machine (for states)
Pending → Processing → [Success/Failed]

Highlight Common Pitfalls

## Common Mistakes to Avoid

### 1. Forgetting to await

// ❌ WRONG: Not awaiting async function async function saveUser(user) { database.save(user); // Returns immediately, save not complete! console.log('User saved'); // Logs before save completes }

// ✅ CORRECT: Await the promise async function saveUser(user) { await database.save(user); // Wait for save to complete console.log('User saved'); // Now it's actually saved }


### 2. Mutating shared state

// ❌ WRONG: Modifying shared object const config = { apiUrl: 'https://api.example.com' };

function updateConfig(newUrl) { config.apiUrl = newUrl; // Affects all code using config! }

// ✅ CORRECT: Return new object function updateConfig(config, newUrl) { return { ...config, apiUrl: newUrl }; // New object, no mutation }


### 3. Not handling errors

// ❌ WRONG: Errors crash the app async function fetchUser(id) { const user = await api.get(/users/${id}); return user; }

// ✅ CORRECT: Handle potential errors async function fetchUser(id) { try { const user = await api.get(/users/${id}); return user; } catch (error) { if (error.status === 404) { return null; // User not found } throw error; // Re-throw unexpected errors } }

Interactive Learning

Provide Exercises

## Practice Exercises

### Exercise 1: Modify the Code
Add validation to check if the amount is positive before processing:

async function processPayment(orderId, paymentMethod, amount) { // TODO: Add validation here

const order = await Order.findById(orderId); // ... rest of code }


**Hint**: Use an if statement to check `amount > 0`

**Solution**:
<details>
<summary>Click to reveal</summary>

async function processPayment(orderId, paymentMethod, amount) { if (amount <= 0) { throw new Error('Amount must be positive'); }

const order = await Order.findById(orderId); // ... rest of code }

</details>

### Exercise 2: Debug the Bug
This code has a bug. Can you spot it?

async function getUsers() { const users = []; const userIds = [1, 2, 3, 4, 5];

userIds.forEach(async (id) => { const user = await fetchUser(id); users.push(user); });

return users; // Will be empty! Why? }


**Hint**: Think about when the function returns vs. when the forEach completes.

**Solution**:
<details>
<summary>Click to reveal</summary>

The function returns before the async callbacks complete. forEach doesn't wait for async functions.

**Fixed version**:

async function getUsers() { const userIds = [1, 2, 3, 4, 5];

const users = await Promise.all( userIds.map(id => fetchUser(id)) );

return users; }

</details>

### Exercise 3: Code Review
Review this code and suggest improvements:

function login(email, password) { let user = db.query('SELECT * FROM users WHERE email = "' + email + '"'); if (user && user.password == password) { return { success: true, token: email + Date.now() }; } return { success: false }; }


**Questions to consider**:
1. What security vulnerabilities do you see?
2. Are there any performance issues?
3. How would you improve error handling?

Usage Examples

@code-explainer
@code-explainer src/services/PaymentService.js
@code-explainer --audience junior
@code-explainer --audience senior
@code-explainer --with-diagrams
@code-explainer --step-by-step
@code-explainer --include-exercises

Communication Best Practices

For Written Explanations

Start Simple, Add Depth

# What it does (simple)
This function checks if a user is logged in.

# How it works (detailed)
It reads the JWT token from the Authorization header, verifies the signature using the secret key, and checks if the token hasn't expired.

# Why this approach (architectural)
We use JWTs instead of session cookies because they're stateless, which makes horizontal scaling easier and reduces database load.

Use Progressive Disclosure

# Quick Summary
Handles user authentication with JWT tokens.

<details>
<summary>Technical Details</summary>

### Token Structure
JWT consists of three parts: header, payload, and signature...

### Verification Process
1. Extract token from header
2. Decode base64
3. Verify signature
4. Check expiration
</details>

<details>
<summary>Security Considerations</summary>

Never store sensitive data in JWT payload because it's only encoded, not encrypted...
</details>

For Live Explanations

Pair Programming Tips: 1. Think Aloud: Verbalize your thought process 2. Ask Questions: "Does this make sense?" "What would you expect here?" 3. Pause for Understanding: Give time to absorb information 4. Encourage Questions: "Any questions before we move on?" 5. Live Debugging: Show how you would debug issues

Code Walkthrough Sessions: 1. Start with architecture diagram 2. Explain data flow end-to-end 3. Dive into key files 4. Show tests demonstrating behavior 5. Open for Q&A

For Documentation

Code Comments:

/**
 * Processes a payment for an order.
 *
 * This function handles the complete payment flow:
 * 1. Validates the order exists and is pending
 * 2. Charges the payment method via Stripe
 * 3. Updates order status to 'paid'
 * 4. Sends confirmation email to customer
 *
 * @param {string} orderId - The ID of the order to process
 * @param {string} paymentMethod - Stripe payment method ID
 * @param {number} amount - Amount in dollars (not cents)
 * @returns {Promise<PaymentResult>} The Stripe payment object
 * @throws {Error} If order not found or payment fails
 *
 * @example
 * const payment = await processPayment('order_123', 'pm_card_visa', 49.99);
 * console.log(payment.id); // 'ch_3MtwBwLkdIwHu7ix0fYv3yZ'
 */

README Sections:

# Payment Service

## Overview
Handles all payment processing using Stripe API.

## Quick Start

const payment = await processPayment(orderId, paymentMethodId, amount);


## How It Works
[Detailed explanation with diagrams]

## API Reference
[Function signatures and parameters]

## Common Issues
[Troubleshooting guide]

## Advanced Usage
[Complex scenarios and edge cases]

Notes

  • Adapt explanation depth to audience technical level
  • Use concrete examples instead of abstract concepts
  • Visual aids significantly improve understanding
  • Encourage questions and interactive learning
  • Break complex code into digestible chunks
  • Relate code behavior to real-world analogies
  • Highlight gotchas and common mistakes
  • Provide hands-on exercises when possible
  • Link to additional learning resources
  • Keep explanations up-to-date with code changes
  • Document the "why" not just the "what"
  • Use consistent terminology throughout

Related skills

FAQ

Who is code-explainer for?

It adapts explanations for junior developers, mid-level and senior developers, and non-technical stakeholders.

What output does it produce?

A high-level overview and a step-by-step walkthrough of the code, with diagrams and context when helpful.

Documentationdocsbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.