Marketplace code-explainer
Explain complex code to team members in clear, understandable terms for effective knowledge shari...
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/curiouslearner/code-explainer" ~/.claude/skills/aiskillstore-marketplace-code-explainer && rm -rf "$T"
skills/curiouslearner/code-explainer/SKILL.mdCode 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:
-
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
-
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
-
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
-
Add Context:
- Why code was written this way
- Common pitfalls and gotchas
- Performance considerations
- Security implications
- Best practices demonstrated
-
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:
pauses execution until the database respondsawait
returns the first matching user orfindOne()
if none foundnull- 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:
- Takes the user's input password
- Applies the same hashing algorithm used during registration
- Compares the result with the stored hash
- Returns
if they match,true
otherwisefalse
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
: Information encoded in the token{ userId: user.id, role: user.role } - Secret
: Private key used to sign the tokenprocess.env.JWT_SECRET - Options
: Token is valid for 1 hour{ expiresIn: '1h' }
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:
- Store the token (usually in localStorage or httpOnly cookie)
- Include it in future requests:
Authorization: Bearer <token> - 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:
- Logger: Check-in desk - records who's passing through
- Authenticate: ID verification - proves you are who you say you are
- Authorize: Boarding pass check - verifies you have permission for this flight
- 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
- Learn more: MDN Async/Await Guide
- Error handling: Using try/catch to handle failures
- External APIs: Integrating with third-party services (Stripe)
Practice Exercise
Try modifying this code to:
- Add a console.log after each step to see the flow
- Add error handling with try/catch
- 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:
<details> <summary>Click to reveal</summary></details>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:
<details> <summary>Click to reveal</summary>The function returns before the async callbacks complete. forEach doesn't wait for async functions.
Fixed version:
</details>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:
- What security vulnerabilities do you see?
- Are there any performance issues?
- 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:
- Think Aloud: Verbalize your thought process
- Ask Questions: "Does this make sense?" "What would you expect here?"
- Pause for Understanding: Give time to absorb information
- Encourage Questions: "Any questions before we move on?"
- Live Debugging: Show how you would debug issues
Code Walkthrough Sessions:
- Start with architecture diagram
- Explain data flow end-to-end
- Dive into key files
- Show tests demonstrating behavior
- 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