Install
$ agentstack add skill-curiouslearner-devkit-code-explainer ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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:
- 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) │ │ │ `
- 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:
Click to reveal
async function processPayment(orderId, paymentMethod, amount) {
if (amount
### 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:
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
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [CuriousLearner](https://github.com/CuriousLearner)
- **Source:** [CuriousLearner/devkit](https://github.com/CuriousLearner/devkit)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.