--- 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 ```markdown # 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 ```markdown # 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 ```javascript 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 ```javascript 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 ```javascript 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 ```javascript 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 ```javascript 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 ```javascript 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 ` 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!** ```javascript // ❌ 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()`** ```javascript // ❌ 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](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** ```javascript 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** ```javascript 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** ```javascript // Check if already processed if (order.status === 'paid') { return { alreadyProcessed: true, paymentId: order.paymentId }; } ``` **Implement Transaction/Rollback** ```javascript // 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** ```javascript 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 ```javascript 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: ```javascript 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: ```javascript 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: ```javascript 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** ```javascript // ❌ 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** ```javascript 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 ```markdown ## 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 ```javascript // ❌ 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 ```javascript // ❌ 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 ```javascript 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? ```javascript 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**: ```javascript 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: ```javascript 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** ```markdown # Quick Summary Handles user authentication with JWT tokens.
Technical Details ### 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
Security Considerations Never store sensitive data in JWT payload because it's only encoded, not encrypted...
``` ### 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**: ```javascript /** * 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} 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**: ```markdown # 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