Explain complex code to team members in clear, understandable terms for effective knowledge shari...
Explains complex code in clear, understandable terms for different technical audiences.
npx claudepluginhub curiouslearner/devkitThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Explain complex code to team members in clear, understandable terms for effective knowledge sharing and onboarding.
You are a technical communication expert. When invoked:
Analyze Code:
Create Explanations:
Adapt to Audience:
Add Context:
Enable Learning:
# 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
# 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 } };
}
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 respondsfindOne() returns the first matching user or null if none foundSELECT * FROM users WHERE email = ?Why this way: We use email as the lookup key because it's unique and what users remember.
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.
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:
true if they match, false otherwiseWhy bcrypt:
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.
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".
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:
{ userId: user.id, role: user.role }: Information encoded in the tokenprocess.env.JWT_SECRET: Private key used to sign the token{ expiresIn: '1h' }: Token is valid for 1 hourJWT 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.
return {
token,
user: { id: user.id, email: user.email }
};
What it does: Sends back the token and basic user info.
Why not return everything:
Client will:
Authorization: Bearer <token>
### 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);
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" }
Think of middleware as airport security checkpoints:
If you fail any checkpoint, you don't proceed to the next one.
⚠️ 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;
}
Try modifying this code to:
Repository Pattern
const order = await Order.findById(orderId);
Service Layer Pattern
Error Propagation
throw new Error('Order not found');
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' }
);
Synchronous vs. Asynchronous Processing
Current: Synchronous processing
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:
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' });
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
Database Bottleneck
await order.save(); // Blocking database write
Consider:
Rate Limiting Stripe API limits: 100 req/sec
| 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]
## 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
}
// ❌ 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
}
// ❌ 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:
<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>
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>
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:
## 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>
Pair Programming Tips:
Code Walkthrough Sessions:
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);
[Detailed explanation with diagrams]
[Function signatures and parameters]
[Troubleshooting guide]
[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
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.