Claude Code Plugins

Community-maintained marketplace

Feedback
9
0

Explain complex code to team members in clear, understandable terms for effective knowledge shari...

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name code-explainer
description Explain complex code to team members in clear, understandable terms for effective knowledge shari...

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
```javascript
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

```markdown
# Understanding the Middleware Pipeline

## Code Overview
```javascript
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

```markdown
# 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
```javascript
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
  • 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**:
```markdown
# 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
```javascript
// ❌ 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

```markdown
## Practice Exercises

### Exercise 1: Modify the Code
Add validation to check if the amount is positive before processing:
```javascript
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:

Click to reveal
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
}

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:

Click to reveal

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;
}

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**
```markdown
# 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
```javascript
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