Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:05:52 +08:00
commit db12a906d2
62 changed files with 27669 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,773 @@
---
name: debugging-methodology
description: Scientific debugging methodology including hypothesis-driven debugging, bug reproduction, binary search debugging, stack trace analysis, logging strategies, and root cause analysis. Use when debugging errors, analyzing stack traces, investigating bugs, or troubleshooting performance issues.
---
# Debugging Methodology
This skill provides comprehensive guidance for systematically debugging issues using scientific methods and proven techniques.
## Scientific Debugging Method
### The Scientific Approach
**1. Observe**: Gather information about the bug
**2. Hypothesize**: Form theories about the cause
**3. Test**: Design experiments to test hypotheses
**4. Analyze**: Evaluate results
**5. Conclude**: Fix the bug or refine hypothesis
### Example: Debugging a Login Issue
```typescript
// Bug: Users cannot log in
// 1. OBSERVE
// - Error message: "Invalid credentials"
// - Happens for all users
// - Started after last deployment
// - Logs show: "bcrypt compare failed"
// 2. HYPOTHESIZE
// Hypothesis 1: Password comparison logic is broken
// Hypothesis 2: Database passwords corrupted
// Hypothesis 3: Bcrypt library updated with breaking change
// 3. TEST
// Test 1: Check if bcrypt library version changed
const packageLock = await fs.readFile('package-lock.json');
// Result: bcrypt upgraded from 5.0.0 to 6.0.0
// Test 2: Check bcrypt changelog
// Result: v6.0.0 changed default salt rounds
// Test 3: Verify password hashing
const testPassword = 'password123';
const oldHash = '$2b$10$...'; // From database
const newHash = await bcrypt.hash(testPassword, 10);
console.log(await bcrypt.compare(testPassword, oldHash)); // false
console.log(await bcrypt.compare(testPassword, newHash)); // true
// 4. ANALYZE
// Old hashes use $2b$ format, new version uses $2a$ format
// Incompatible hash formats
// 5. CONCLUDE
// Rollback bcrypt to 5.x or migrate all password hashes
```
## Reproducing Bugs Consistently
### Creating Minimal Reproduction
```typescript
// Original bug report: "App crashes when clicking submit"
// Step 1: Remove unrelated code
// ❌ BAD - Too much noise
function handleSubmit() {
validateForm();
checkPermissions();
logAnalytics();
sendToServer();
updateUI();
showNotification();
// Which one causes the crash?
}
// ✅ GOOD - Minimal reproduction
function handleSubmit() {
// Removed: validateForm, checkPermissions, logAnalytics, updateUI, showNotification
// Bug still occurs with just:
sendToServer();
// Root cause: sendToServer crashes with undefined data
}
```
### Reproducing Race Conditions
```typescript
// Bug: Intermittent "Cannot read property of undefined"
// Make race condition reproducible with delays
async function fetchUserData() {
const user = await fetchUser();
// Add artificial delay to make race condition consistent
await new Promise(resolve => setTimeout(resolve, 100));
return user.profile; // Sometimes undefined
}
// Once reproducible, investigate:
// - Are multiple requests racing?
// - Is data being cleared too early?
// - Are promises resolving out of order?
```
### Creating Test Cases
```typescript
// Once bug is reproducible, create failing test
describe('Login', () => {
test('should authenticate user with valid credentials', async () => {
const user = await db.user.create({
email: 'test@example.com',
password: await bcrypt.hash('password123', 10),
});
const result = await login('test@example.com', 'password123');
expect(result.success).toBe(true);
expect(result.user.email).toBe('test@example.com');
});
});
```
## Binary Search Debugging
### Finding the Breaking Commit
```bash
# Use git bisect to find the commit that introduced the bug
# Start bisect
git bisect start
# Mark current commit as bad (has the bug)
git bisect bad
# Mark a known good commit (before bug appeared)
git bisect good v1.2.0
# Git will checkout a commit in the middle
# Test if bug exists, then mark:
git bisect bad # Bug exists in this commit
# or
git bisect good # Bug doesn't exist in this commit
# Repeat until git identifies the breaking commit
# Git will output: "abc123 is the first bad commit"
# End bisect session
git bisect reset
```
### Automated Bisect
```bash
# Create test script that exits 0 (pass) or 1 (fail)
# test.sh
#!/bin/bash
npm test 2>&1 | grep -q "Login test failed"
if [ $? -eq 0 ]; then
exit 1 # Bug found
else
exit 0 # Bug not found
fi
# Run automated bisect
git bisect start HEAD v1.2.0
git bisect run ./test.sh
# Git will automatically find the breaking commit
```
### Binary Search in Code
```typescript
// Bug: Function returns wrong result for large arrays
function processArray(arr: number[]): number {
// 100 lines of code
// Which line causes the bug?
}
// Binary search approach:
// 1. Comment out second half
function processArray(arr: number[]): number {
// Lines 1-50
// Lines 51-100 (commented out)
}
// If bug disappears: Bug is in lines 51-100
// If bug persists: Bug is in lines 1-50
// 2. Repeat on the problematic half
// Continue until you isolate the buggy line
```
## Stack Trace Analysis
### Reading Stack Traces
```
Error: Cannot read property 'name' of undefined
at getUserName (/app/src/user.ts:42:20)
at formatUserProfile (/app/src/profile.ts:15:25)
at handleRequest (/app/src/api.ts:89:30)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
```
**Analysis**:
1. **Error type**: TypeError - trying to access property on undefined
2. **Error message**: "Cannot read property 'name' of undefined"
3. **Origin**: `getUserName` function at line 42
4. **Call chain**: api.ts → profile.ts → user.ts
5. **Root cause location**: user.ts:42
### Investigating the Stack Trace
```typescript
// user.ts:42
function getUserName(userId: string): string {
const user = cache.get(userId);
return user.name; // ← Line 42: user is undefined
}
// Why is user undefined?
// 1. Check cache.get implementation
// 2. Check if userId is valid
// 3. Check if user exists in cache
// Add defensive check:
function getUserName(userId: string): string {
const user = cache.get(userId);
if (!user) {
throw new Error(`User not found in cache: ${userId}`);
}
return user.name;
}
```
### Source Maps for Production
```javascript
// Enable source maps in production
// webpack.config.js
module.exports = {
devtool: 'source-map',
// This generates .map files for production debugging
};
// View original TypeScript code in production errors
// Instead of:
// at r (/app/bundle.js:1:23456)
// You see:
// at getUserName (/app/src/user.ts:42:20)
```
## Logging Strategies
### Strategic Log Placement
```typescript
// ✅ GOOD - Log at key decision points
async function processOrder(order: Order) {
logger.info('Processing order', { orderId: order.id, items: order.items.length });
try {
// Log before critical operations
logger.debug('Validating order', { orderId: order.id });
await validateOrder(order);
logger.debug('Processing payment', { orderId: order.id, amount: order.total });
const payment = await processPayment(order);
logger.info('Order processed successfully', {
orderId: order.id,
paymentId: payment.id,
duration: Date.now() - startTime,
});
return payment;
} catch (error) {
// Log errors with context
logger.error('Order processing failed', {
orderId: order.id,
error: error.message,
stack: error.stack,
});
throw error;
}
}
```
### Structured Logging
```typescript
import winston from 'winston';
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'order-service',
version: process.env.APP_VERSION,
},
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
// Output:
// {
// "timestamp": "2025-10-16T10:30:00.000Z",
// "level": "error",
// "message": "Order processing failed",
// "orderId": "order_123",
// "error": "Payment declined",
// "service": "order-service",
// "version": "1.2.3"
// }
```
### Log Levels
```typescript
// Use appropriate log levels
logger.debug('Detailed debug information'); // Development only
logger.info('Normal operation'); // Informational
logger.warn('Warning but not an error'); // Potential issues
logger.error('Error occurred'); // Errors that need attention
logger.fatal('Critical failure'); // System-wide failures
// Set log level by environment
const logLevel = {
development: 'debug',
staging: 'info',
production: 'warn',
}[process.env.NODE_ENV];
```
## Debugging Tools
### Using Debuggers
```typescript
// Set breakpoints in VS Code
function calculateTotal(items: Item[]): number {
let total = 0;
for (const item of items) {
debugger; // Execution pauses here
total += item.price * item.quantity;
}
return total;
}
// Or use VS Code launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Tests",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand"],
"console": "integratedTerminal"
}
]
}
```
### Node.js Built-in Debugger
```bash
# Start Node with inspector
node --inspect index.js
# Open Chrome DevTools
# Navigate to: chrome://inspect
# Click "inspect" on your Node.js process
# Or use Node's built-in debugger
node inspect index.js
> cont # Continue
> next # Step over
> step # Step into
> out # Step out
> repl # Enter REPL to inspect variables
```
### Memory Profiling
```typescript
// Detect memory leaks
import v8 from 'v8';
import fs from 'fs';
// Take heap snapshot
function takeHeapSnapshot(filename: string) {
const snapshot = v8.writeHeapSnapshot(filename);
console.log(`Heap snapshot written to ${snapshot}`);
}
// Compare snapshots to find leaks
takeHeapSnapshot('before.heapsnapshot');
// ... run code that might leak
takeHeapSnapshot('after.heapsnapshot');
// Analyze in Chrome DevTools:
// 1. Open DevTools → Memory tab
// 2. Load snapshot
// 3. Compare snapshots
// 4. Look for objects that grew significantly
```
## Performance Profiling
### CPU Profiling
```typescript
// Profile function execution time
console.time('processLargeArray');
processLargeArray(data);
console.timeEnd('processLargeArray');
// Output: processLargeArray: 1234.567ms
// More detailed profiling
import { performance } from 'perf_hooks';
const start = performance.now();
processLargeArray(data);
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
```
### Node.js Profiler
```bash
# Generate CPU profile
node --prof index.js
# Process profile data
node --prof-process isolate-0x*.log > profile.txt
# Analyze profile.txt to find slow functions
```
### Chrome DevTools Performance
```typescript
// Add performance marks
performance.mark('start-data-processing');
processData(data);
performance.mark('end-data-processing');
performance.measure(
'data-processing',
'start-data-processing',
'end-data-processing'
);
const measure = performance.getEntriesByName('data-processing')[0];
console.log(`Data processing took ${measure.duration}ms`);
```
## Network Debugging
### HTTP Request Logging
```typescript
import axios from 'axios';
// Add request/response interceptors
axios.interceptors.request.use(
(config) => {
console.log('Request:', {
method: config.method,
url: config.url,
headers: config.headers,
data: config.data,
});
return config;
},
(error) => {
console.error('Request error:', error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log('Response:', {
status: response.status,
headers: response.headers,
data: response.data,
});
return response;
},
(error) => {
console.error('Response error:', {
status: error.response?.status,
data: error.response?.data,
message: error.message,
});
return Promise.reject(error);
}
);
```
### Debugging CORS Issues
```typescript
// Enable detailed CORS logging
import cors from 'cors';
const corsOptions = {
origin: (origin, callback) => {
console.log('CORS request from origin:', origin);
const allowedOrigins = ['https://app.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
console.log('CORS blocked:', origin);
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
};
app.use(cors(corsOptions));
```
## Race Condition Detection
### Using Promises Correctly
```typescript
// ❌ BAD - Race condition
let userData = null;
async function loadUser() {
userData = await fetchUser(); // Takes 100ms
}
async function displayUser() {
console.log(userData.name); // May be null if loadUser not finished
}
loadUser();
displayUser(); // Race condition!
// ✅ GOOD - Wait for promise
async function main() {
await loadUser();
await displayUser(); // userData guaranteed to be loaded
}
```
### Detecting Concurrent Modifications
```typescript
// Detect race conditions with version numbers
interface Document {
id: string;
content: string;
version: number;
}
async function updateDocument(doc: Document) {
// Read current version
const current = await db.document.findUnique({
where: { id: doc.id },
});
// Check if version matches
if (current.version !== doc.version) {
throw new Error('Document was modified by another user');
}
// Update with incremented version
await db.document.update({
where: { id: doc.id, version: doc.version },
data: {
content: doc.content,
version: doc.version + 1,
},
});
}
```
## Common Bug Patterns
### Off-by-One Errors
```typescript
// ❌ BAD - Off by one
const arr = [1, 2, 3, 4, 5];
for (let i = 0; i <= arr.length; i++) {
console.log(arr[i]); // Last iteration: undefined
}
// ✅ GOOD
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
```
### Null/Undefined Issues
```typescript
// ❌ BAD - No null check
function getUserName(user: User): string {
return user.profile.name; // Crashes if user or profile is null
}
// ✅ GOOD - Defensive checks
function getUserName(user: User | null): string {
if (!user) {
return 'Unknown';
}
if (!user.profile) {
return 'No profile';
}
return user.profile.name;
}
// ✅ BETTER - Optional chaining
function getUserName(user: User | null): string {
return user?.profile?.name ?? 'Unknown';
}
```
### Async/Await Pitfalls
```typescript
// ❌ BAD - Forgot await
async function getUser(id: string) {
const user = fetchUser(id); // Missing await!
return user.name; // user is a Promise, not the actual user
}
// ✅ GOOD
async function getUser(id: string) {
const user = await fetchUser(id);
return user.name;
}
// ❌ BAD - Sequential when parallel is possible
async function loadData() {
const users = await fetchUsers(); // 1 second
const posts = await fetchPosts(); // 1 second
const comments = await fetchComments(); // 1 second
// Total: 3 seconds
}
// ✅ GOOD - Parallel execution
async function loadData() {
const [users, posts, comments] = await Promise.all([
fetchUsers(),
fetchPosts(),
fetchComments(),
]);
// Total: 1 second
}
```
## Root Cause Analysis (5 Whys)
### The 5 Whys Technique
```
Problem: Application crashed in production
Why? Memory leak caused out-of-memory error
Why? Array of user sessions kept growing
Why? Sessions weren't being cleaned up
Why? Cleanup function wasn't being called
Why? Event listener for cleanup was never registered
Root Cause: Missing initialization code in new deployment script
```
### RCA Template
```markdown
## Root Cause Analysis
**Date**: 2025-10-16
**Incident**: API downtime (30 minutes)
### Timeline
- 10:00 - Deployment started
- 10:15 - First error reports
- 10:20 - Incident declared
- 10:25 - Rollback initiated
- 10:30 - Service restored
### Impact
- 500 users affected
- 10% of API requests failed
- $5,000 estimated revenue loss
### Root Cause
Database connection pool exhausted due to missing connection cleanup in new feature code.
### 5 Whys
1. Why did the API fail? → Database connections exhausted
2. Why were connections exhausted? → Connections not returned to pool
3. Why weren't connections returned? → Missing finally block in new code
4. Why was the finally block missing? → Code review missed it
5. Why did code review miss it? → No automated check for connection cleanup
### Immediate Actions Taken
- Rolled back deployment
- Manually closed leaked connections
- Service restored
### Preventive Measures
1. Add linter rule to detect missing finally blocks
2. Add integration test for connection cleanup
3. Update code review checklist
4. Add monitoring for connection pool usage
### Lessons Learned
- Need better monitoring of connection pool metrics
- Database connection patterns should be abstracted
- Code review process needs improvement
```
## Debugging Checklist
**Before Debugging**:
- [ ] Can you reproduce the bug consistently?
- [ ] Do you have a minimal reproduction case?
- [ ] Have you checked recent changes (git log)?
- [ ] Have you read error messages carefully?
- [ ] Have you checked logs?
**During Debugging**:
- [ ] Are you using scientific method (hypothesis-driven)?
- [ ] Have you added strategic logging?
- [ ] Are you using a debugger effectively?
- [ ] Have you isolated the problem area?
- [ ] Have you considered race conditions?
**After Fixing**:
- [ ] Does the fix address root cause (not just symptoms)?
- [ ] Have you added tests to prevent regression?
- [ ] Have you documented the fix?
- [ ] Have you conducted root cause analysis?
- [ ] Have you shared learnings with team?
## When to Use This Skill
Use this skill when:
- Debugging production issues
- Investigating bug reports
- Analyzing error logs
- Troubleshooting performance problems
- Finding memory leaks
- Resolving race conditions
- Conducting post-mortems
- Training team on debugging
- Improving debugging processes
- Setting up debugging tools
---
**Remember**: Debugging is detective work. Be systematic, stay curious, and always document what you learn. The bug you fix today will teach you how to prevent similar bugs tomorrow.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,922 @@
---
name: frontend-patterns
description: Modern frontend architecture patterns for React, Next.js, and TypeScript including component composition, state management, performance optimization, accessibility, and responsive design. Use when building UI components, implementing frontend features, optimizing performance, or working with React/Next.js applications.
---
# Frontend Development Patterns
This skill provides comprehensive guidance for modern frontend development using React, Next.js, TypeScript, and related technologies.
## Component Architecture
### Component Composition Patterns
**Container/Presentational Pattern**:
```typescript
// Presentational component (pure, reusable)
interface UserCardProps {
name: string;
email: string;
avatar: string;
onEdit: () => void;
}
function UserCard({ name, email, avatar, onEdit }: UserCardProps) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
<button onClick={onEdit}>Edit</button>
</div>
);
}
// Container component (handles logic, state, data fetching)
function UserCardContainer({ userId }: { userId: string }) {
const { data: user, isLoading } = useUser(userId);
const { mutate: updateUser } = useUpdateUser();
if (isLoading) return <Skeleton />;
if (!user) return <NotFound />;
return <UserCard {...user} onEdit={() => updateUser(user.id)} />;
}
```
**Compound Components Pattern**:
```typescript
// Flexible, composable API
<Tabs defaultValue="profile">
<TabsList>
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="profile">
<ProfileForm />
</TabsContent>
<TabsContent value="settings">
<SettingsForm />
</TabsContent>
</Tabs>
```
### Component Organization
```
components/
├── ui/ # Primitive components (buttons, inputs)
│ ├── button.tsx
│ ├── input.tsx
│ └── card.tsx
├── forms/ # Form components
│ ├── login-form.tsx
│ └── register-form.tsx
├── features/ # Feature-specific components
│ ├── user-profile/
│ │ ├── profile-header.tsx
│ │ ├── profile-stats.tsx
│ │ └── index.ts
│ └── dashboard/
│ ├── dashboard-grid.tsx
│ └── dashboard-card.tsx
└── layouts/ # Layout components
├── main-layout.tsx
└── auth-layout.tsx
```
## State Management
### Local State (useState)
Use for:
- Component-specific UI state
- Form inputs
- Toggles, modals
```typescript
function SearchBar() {
const [query, setQuery] = useState('');
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{isOpen && <SearchResults query={query} />}
</div>
);
}
```
### Global State (Zustand)
Use for:
- User authentication state
- Theme preferences
- Shopping cart
- Cross-component shared state
```typescript
import create from 'zustand';
interface UserStore {
user: User | null;
setUser: (user: User) => void;
logout: () => void;
}
export const useUserStore = create<UserStore>((set) => ({
user: null,
setUser: (user) => set({ user }),
logout: () => set({ user: null }),
}));
// Usage
function Header() {
const user = useUserStore((state) => state.user);
const logout = useUserStore((state) => state.logout);
return <div>{user ? user.name : 'Guest'}</div>;
}
```
### Server State (React Query / TanStack Query)
Use for:
- API data fetching
- Caching API responses
- Optimistic updates
- Background refetching
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Fetch data
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // 5 minutes
});
if (isLoading) return <Skeleton />;
if (error) return <Error error={error} />;
return <div>{data.name}</div>;
}
// Mutations with optimistic updates
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (user: User) => api.updateUser(user),
onMutate: async (newUser) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['user', newUser.id] });
// Snapshot previous value
const previous = queryClient.getQueryData(['user', newUser.id]);
// Optimistically update
queryClient.setQueryData(['user', newUser.id], newUser);
return { previous };
},
onError: (err, newUser, context) => {
// Rollback on error
queryClient.setQueryData(['user', newUser.id], context?.previous);
},
onSettled: (newUser) => {
// Refetch after mutation
queryClient.invalidateQueries({ queryKey: ['user', newUser.id] });
},
});
}
```
## Performance Optimization
### 1. Memoization
**useMemo** (expensive calculations):
```typescript
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = useMemo(
() => products.sort((a, b) => b.price - a.price),
[products]
);
return <div>{sortedProducts.map(...)}</div>;
}
```
**useCallback** (prevent re-renders):
```typescript
function Parent() {
const [count, setCount] = useState(0);
// ✅ Memoized - Child won't re-render unless count changes
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <Child onClick={handleClick} />;
}
const Child = memo(function Child({ onClick }: { onClick: () => void }) {
console.log('Child rendered');
return <button onClick={onClick}>Click</button>;
});
```
**React.memo** (prevent component re-renders):
```typescript
const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
// Only re-renders if data changes
return <div>{/* expensive rendering */}</div>;
});
```
### 2. Code Splitting
**Route-based splitting** (Next.js automatic):
```typescript
// app/dashboard/page.tsx - automatically code split
export default function DashboardPage() {
return <Dashboard />;
}
```
**Component-level splitting**:
```typescript
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('@/components/heavy-chart'), {
loading: () => <Skeleton />,
ssr: false, // Don't render on server
});
function Analytics() {
return <HeavyChart data={chartData} />;
}
```
### 3. Image Optimization
```typescript
import Image from 'next/image';
// ✅ Optimized - Next.js Image component
<Image
src="/hero.jpg"
alt="Hero image"
width={800}
height={600}
priority // Load immediately for LCP
placeholder="blur"
blurDataURL="data:image/..."
/>
// ❌ Not optimized
<img src="/hero.jpg" alt="Hero" />
```
### 4. Lazy Loading
```typescript
import { lazy, Suspense } from 'react';
const Comments = lazy(() => import('./comments'));
function Post() {
return (
<div>
<PostContent />
<Suspense fallback={<CommentsSkeleton />}>
<Comments postId={postId} />
</Suspense>
</div>
);
}
```
### 5. Virtual Scrolling
```typescript
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: `${virtualizer.getTotalSize()}px` }}>
{virtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`,
}}
>
{items[virtualRow.index].name}
</div>
))}
</div>
</div>
);
}
```
## Accessibility (a11y)
### Semantic HTML
```typescript
// ✅ Semantic
<nav>
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
// ❌ Non-semantic
<div>
<div>
<div onClick={goHome}>Home</div>
<div onClick={goAbout}>About</div>
</div>
</div>
```
### ARIA Attributes
```typescript
<button
aria-label="Close dialog"
aria-expanded={isOpen}
aria-controls="dialog-content"
onClick={toggle}
>
<X aria-hidden="true" />
</button>
<div
id="dialog-content"
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
>
<h2 id="dialog-title">Dialog Title</h2>
{content}
</div>
```
### Keyboard Navigation
```typescript
function Dropdown() {
const [isOpen, setIsOpen] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(0);
const handleKeyDown = (e: KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setFocusedIndex((i) => Math.min(i + 1, items.length - 1));
break;
case 'ArrowUp':
e.preventDefault();
setFocusedIndex((i) => Math.max(i - 1, 0));
break;
case 'Enter':
selectItem(items[focusedIndex]);
break;
case 'Escape':
setIsOpen(false);
break;
}
};
return (
<div onKeyDown={handleKeyDown} role="combobox">
{/* dropdown content */}
</div>
);
}
```
### Focus Management
```typescript
import { useRef, useEffect } from 'react';
function Modal({ isOpen, onClose }: ModalProps) {
const closeButtonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (isOpen) {
// Focus close button when modal opens
closeButtonRef.current?.focus();
// Trap focus within modal
const handleTab = (e: KeyboardEvent) => {
// Implement focus trap logic
};
document.addEventListener('keydown', handleTab);
return () => document.removeEventListener('keydown', handleTab);
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div role="dialog" aria-modal="true">
<button ref={closeButtonRef} onClick={onClose}>
Close
</button>
{content}
</div>
);
}
```
## Form Patterns
### Controlled Forms with Validation
```typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
const schema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
age: z.number().min(18, 'Must be 18 or older'),
});
type FormData = z.infer<typeof schema>;
function RegistrationForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = async (data: FormData) => {
await api.register(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input {...register('email')} type="email" />
{errors.email && <span>{errors.email.message}</span>}
</div>
<div>
<input {...register('password')} type="password" />
{errors.password && <span>{errors.password.message}</span>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
```
### Form State Management
```typescript
// Optimistic updates
const { mutate } = useMutation({
mutationFn: updateUser,
onMutate: async (newData) => {
// Cancel outgoing queries
await queryClient.cancelQueries({ queryKey: ['user', userId] });
// Snapshot previous
const previous = queryClient.getQueryData(['user', userId]);
// Optimistically update UI
queryClient.setQueryData(['user', userId], newData);
return { previous };
},
onError: (err, newData, context) => {
// Rollback on error
queryClient.setQueryData(['user', userId], context?.previous);
toast.error('Update failed');
},
onSuccess: () => {
toast.success('Updated successfully');
},
});
```
## Error Handling
### Error Boundaries
```typescript
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: any) {
console.error('Error boundary caught:', error, errorInfo);
// Log to error tracking service
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div>
<h2>Something went wrong</h2>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorFallback />}>
<App />
</ErrorBoundary>
```
### Async Error Handling
```typescript
function DataComponent() {
const { data, error, isError, isLoading } = useQuery({
queryKey: ['data'],
queryFn: fetchData,
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
if (isLoading) return <Skeleton />;
if (isError) return <ErrorDisplay error={error} />;
return <DisplayData data={data} />;
}
```
## Responsive Design
### Mobile-First Approach
```typescript
// Tailwind CSS (mobile-first)
<div className="
w-full /* Full width on mobile */
md:w-1/2 /* Half width on tablets */
lg:w-1/3 /* Third width on desktop */
p-4 /* Padding 16px */
md:p-6 /* Padding 24px on tablets+ */
">
Content
</div>
```
### Responsive Hooks
```typescript
import { useMediaQuery } from '@/hooks/use-media-query';
function ResponsiveLayout() {
const isMobile = useMediaQuery('(max-width: 768px)');
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
const isDesktop = useMediaQuery('(min-width: 1025px)');
if (isMobile) return <MobileLayout />;
if (isTablet) return <TabletLayout />;
return <DesktopLayout />;
}
```
## Data Fetching Strategies
### Server Components (Next.js 14+)
```typescript
// app/users/page.tsx - Server Component
async function UsersPage() {
// Fetched on server
const users = await db.user.findMany();
return <UserList users={users} />;
}
```
### Client Components with React Query
```typescript
'use client';
function UserList() {
const { data: users, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
if (isLoading) return <UsersLoading />;
return <div>{users.map(user => <UserCard key={user.id} {...user} />)}</div>;
}
```
### Parallel Data Fetching
```typescript
function Dashboard() {
const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
const { data: stats } = useQuery({ queryKey: ['stats'], queryFn: fetchStats });
const { data: posts } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
// All three queries run in parallel
return <div>...</div>;
}
```
### Dependent Queries
```typescript
function UserPosts({ userId }: { userId: string }) {
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
const { data: posts } = useQuery({
queryKey: ['posts', user?.id],
queryFn: () => fetchUserPosts(user!.id),
enabled: !!user, // Only fetch after user is loaded
});
return <div>...</div>;
}
```
## TypeScript Patterns
### Prop Types
```typescript
// Basic props
interface ButtonProps {
children: ReactNode;
onClick: () => void;
variant?: 'primary' | 'secondary';
disabled?: boolean;
}
// Props with generic
interface ListProps<T> {
items: T[];
renderItem: (item: T) => ReactNode;
keyExtractor: (item: T) => string;
}
// Props extending HTML attributes
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
}
```
### Type-Safe API Responses
```typescript
// API response types
interface ApiResponse<T> {
data: T;
error?: never;
}
interface ApiError {
data?: never;
error: {
code: string;
message: string;
};
}
type ApiResult<T> = ApiResponse<T> | ApiError;
// Usage
async function fetchUser(id: string): Promise<ApiResult<User>> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
```
## Testing Patterns
### Component Testing
```typescript
import { render, screen, fireEvent } from '@testing-library/react';
import { LoginForm } from './login-form';
test('submits form with email and password', async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'test@example.com' },
});
fireEvent.change(screen.getByLabelText('Password'), {
target: { value: 'password123' },
});
fireEvent.click(screen.getByRole('button', { name: 'Login' }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
});
```
### Mock API Calls
```typescript
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/users/:id', (req, res, ctx) => {
return res(ctx.json({
id: req.params.id,
name: 'Test User',
email: 'test@example.com',
}));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('displays user data', async () => {
const queryClient = new QueryClient();
render(
<QueryClientProvider client={queryClient}>
<UserProfile userId="123" />
</QueryClientProvider>
);
expect(await screen.findByText('Test User')).toBeInTheDocument();
});
```
## Build Optimization
### Bundle Analysis
```bash
# Next.js bundle analyzer
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// config
});
# Run analysis
ANALYZE=true npm run build
```
### Tree Shaking
```typescript
// ✅ Named imports (tree-shakeable)
import { Button } from '@/components/ui/button';
// ❌ Namespace import (includes everything)
import * as UI from '@/components/ui';
```
### Dynamic Imports
```typescript
// Import only when needed
async function handleExport() {
const { exportToPDF } = await import('@/lib/pdf-export');
await exportToPDF(data);
}
```
## Common Frontend Mistakes to Avoid
1. **Prop drilling**: Use Context or state management library instead
2. **Unnecessary re-renders**: Use memo, useMemo, useCallback appropriately
3. **Missing loading states**: Always show loading indicators
4. **No error boundaries**: Catch errors before they break the app
5. **Inline functions in JSX**: Causes re-renders, use useCallback
6. **Large bundle sizes**: Code split and lazy load
7. **Missing alt text**: All images need descriptive alt text
8. **Inaccessible forms**: Use proper labels and ARIA
9. **Console.log in production**: Remove or use proper logging
10. **Mixing server and client code**: Know Next.js boundaries
## Performance Metrics (Core Web Vitals)
### LCP (Largest Contentful Paint)
**Target**: < 2.5 seconds
**Optimize**:
- Preload critical images
- Use Next.js Image component
- Minimize render-blocking resources
- Use CDN for assets
### FID (First Input Delay)
**Target**: < 100 milliseconds
**Optimize**:
- Minimize JavaScript execution
- Code split large bundles
- Use web workers for heavy computation
- Defer non-critical JavaScript
### CLS (Cumulative Layout Shift)
**Target**: < 0.1
**Optimize**:
- Set explicit width/height on images
- Reserve space for ads/embeds
- Avoid inserting content above existing content
- Use CSS transforms instead of layout properties
## When to Use This Skill
Use this skill when:
- Building React or Next.js components
- Implementing frontend features
- Optimizing frontend performance
- Debugging rendering issues
- Setting up state management
- Implementing forms
- Ensuring accessibility
- Working with responsive design
- Fetching and caching data
- Testing frontend code
---
**Remember**: Modern frontend development is about creating fast, accessible, and delightful user experiences. Follow these patterns to build UIs that users love.

View File

@@ -0,0 +1,883 @@
---
name: project-planning
description: Project planning methodologies including work breakdown structure, task estimation, dependency management, risk assessment, sprint planning, and stakeholder communication. Use when breaking down projects, estimating work, planning sprints, or managing dependencies.
---
# Project Planning
This skill provides comprehensive guidance for planning and managing software development projects effectively.
## Work Breakdown Structure (WBS)
### Breaking Down Large Projects
```markdown
Project: E-commerce Platform
├── 1. User Management
│ ├── 1.1 Authentication
│ │ ├── 1.1.1 Email/Password Login
│ │ ├── 1.1.2 Social Login (Google, Facebook)
│ │ └── 1.1.3 Password Reset
│ ├── 1.2 User Profiles
│ │ ├── 1.2.1 Profile Creation
│ │ ├── 1.2.2 Profile Editing
│ │ └── 1.2.3 Avatar Upload
│ └── 1.3 Role Management
│ ├── 1.3.1 Admin Role
│ ├── 1.3.2 Customer Role
│ └── 1.3.3 Vendor Role
├── 2. Product Catalog
│ ├── 2.1 Product Listings
│ ├── 2.2 Product Details
│ ├── 2.3 Product Search
│ └── 2.4 Product Categories
├── 3. Shopping Cart
│ ├── 3.1 Add to Cart
│ ├── 3.2 Update Quantities
│ ├── 3.3 Remove Items
│ └── 3.4 Cart Persistence
├── 4. Checkout
│ ├── 4.1 Shipping Address
│ ├── 4.2 Payment Processing
│ ├── 4.3 Order Confirmation
│ └── 4.4 Email Notifications
└── 5. Order Management
├── 5.1 Order History
├── 5.2 Order Tracking
└── 5.3 Order Cancellation
```
### WBS Best Practices
**1. Start with deliverables, not activities**
```markdown
❌ Wrong (activities):
- Write code
- Test features
- Deploy
✅ Right (deliverables):
- User Authentication System
- Product Search Feature
- Payment Integration
```
**2. Use the 8/80 rule**
- No task should take less than 8 hours (too granular)
- No task should take more than 80 hours (too large)
- Sweet spot: 1-5 days per task
**3. Break down until you can estimate**
```markdown
❌ Too vague:
- Build API (? days)
✅ Specific:
- Design API endpoints (1 day)
- Implement authentication (2 days)
- Create CRUD operations (3 days)
- Write API documentation (1 day)
- Add rate limiting (1 day)
Total: 8 days
```
## Task Estimation
### Story Points
**Fibonacci Scale**: 1, 2, 3, 5, 8, 13, 21
```markdown
1 point - Trivial
- Update documentation
- Fix typo
- Change button color
2 points - Simple
- Add validation to form field
- Create simple API endpoint
- Write unit tests for existing function
3 points - Moderate
- Implement login form
- Add pagination to list
- Create database migration
5 points - Complex
- Build user profile page
- Implement search functionality
- Add email notifications
8 points - Very Complex
- Build payment integration
- Implement complex reporting
- Create admin dashboard
13 points - Epic
- Build entire authentication system
- Create complete checkout flow
(Should be broken down further)
```
### T-Shirt Sizing
**Quick estimation for early planning**:
```markdown
XS (1-2 days)
- Bug fixes
- Minor UI updates
- Documentation updates
S (3-5 days)
- Small features
- Simple integrations
- Basic CRUD operations
M (1-2 weeks)
- Medium features
- Standard integrations
- Multiple related stories
L (2-4 weeks)
- Large features
- Complex integrations
- Multiple epics
XL (1-2 months)
- Major features
- System redesigns
(Should be broken down into smaller pieces)
```
### Planning Poker
**Collaborative estimation process**:
```markdown
1. Product Owner presents user story
2. Team asks clarifying questions
3. Each member privately selects estimate
4. All reveal simultaneously
5. Discuss differences (especially highest/lowest)
6. Re-estimate if needed
7. Reach consensus
Example Session:
Story: "As a user, I want to reset my password"
Round 1 Estimates: 2, 3, 3, 8, 3
Discussion: Why 8?
- "What about email templates?"
- "What if SMTP fails?"
- "Need password strength validation"
Round 2 Estimates: 5, 5, 5, 5, 5
Consensus: 5 points
```
### Estimation Accuracy
**Cone of Uncertainty**:
```
Project Start: ±100% accuracy
Requirements: ±50% accuracy
Design: ±25% accuracy
Development: ±10% accuracy
Testing: ±5% accuracy
```
**Build in buffer**:
```markdown
Optimistic: 3 days
Realistic: 5 days
Pessimistic: 8 days
Formula: (Optimistic + 4×Realistic + Pessimistic) ÷ 6
Buffer: (3 + 4×5 + 8) ÷ 6 = 5.2 days
Use 6 days for planning
```
## Dependency Identification
### Types of Dependencies
```markdown
1. Finish-to-Start (FS) - Most common
Task B cannot start until Task A finishes
Example: Design → Development
2. Start-to-Start (SS)
Task B cannot start until Task A starts
Example: Development → Code Review (parallel)
3. Finish-to-Finish (FF)
Task B cannot finish until Task A finishes
Example: Development → Testing (testing continues)
4. Start-to-Finish (SF) - Rare
Task B cannot finish until Task A starts
Example: Old system → New system (overlap)
```
### Dependency Mapping
```markdown
Task Dependencies:
1. Database Schema Design
└─► 2. API Development (FS)
├─► 3. Frontend Development (FS)
└─► 4. Integration Tests (SS)
└─► 5. End-to-End Tests (FS)
2. API Development
└─► 6. API Documentation (SS)
3. Frontend Development
└─► 7. UI/UX Review (FF)
Critical Path: 1 → 2 → 3 → 5
(Longest path through dependencies)
```
### Managing Dependencies
```typescript
// Dependency tracking in code
interface Task {
id: string;
name: string;
dependencies: string[]; // IDs of tasks that must complete first
status: 'pending' | 'in-progress' | 'completed' | 'blocked';
}
const tasks: Task[] = [
{
id: 'db-schema',
name: 'Design database schema',
dependencies: [],
status: 'completed',
},
{
id: 'api-dev',
name: 'Develop API',
dependencies: ['db-schema'],
status: 'in-progress',
},
{
id: 'frontend-dev',
name: 'Develop frontend',
dependencies: ['api-dev'],
status: 'blocked', // Waiting for API
},
];
function canStartTask(taskId: string): boolean {
const task = tasks.find(t => t.id === taskId);
if (!task) return false;
// Check if all dependencies are completed
return task.dependencies.every(depId => {
const dep = tasks.find(t => t.id === depId);
return dep?.status === 'completed';
});
}
```
## Critical Path Analysis
### Finding the Critical Path
```markdown
Project: Launch Marketing Website
Tasks:
A. Design mockups (3 days)
B. Develop frontend (5 days) - depends on A
C. Write content (4 days) - independent
D. Set up hosting (1 day) - independent
E. Deploy website (1 day) - depends on B, C, D
F. Test website (2 days) - depends on E
Path 1: A → B → E → F = 3 + 5 + 1 + 2 = 11 days
Path 2: C → E → F = 4 + 1 + 2 = 7 days
Path 3: D → E → F = 1 + 1 + 2 = 4 days
Critical Path: A → B → E → F (11 days)
(Any delay in these tasks delays the entire project)
```
### Managing Critical Path
```markdown
Strategies:
1. Fast-track critical tasks
- Assign best developers
- Remove blockers immediately
- Daily status checks
2. Crash critical tasks (add resources)
- Pair programming
- Additional team members
- Overtime (carefully)
3. Parallelize where possible
- Content writing during development
- Documentation during testing
4. Monitor closely
- Daily updates on critical path
- Early warning of delays
- Quick decision-making
```
## Risk Assessment and Mitigation
### Risk Matrix
```markdown
Impact vs Probability:
Low Medium High
High Monitor Mitigate Immediate Action
Medium Accept Monitor Mitigate
Low Accept Accept Monitor
Example Risks:
1. API Integration Delays (High Impact, Medium Probability)
→ Mitigate: Start integration early, have backup plan
2. Key Developer Leaves (High Impact, Low Probability)
→ Monitor: Document knowledge, cross-train team
3. Library Deprecated (Medium Impact, Low Probability)
→ Accept: Will address if it happens
```
### Risk Register
```markdown
| ID | Risk | Impact | Prob | Status | Mitigation |
|----|------|--------|------|--------|------------|
| R1 | Third-party API unreliable | High | Medium | Active | Build fallback, cache responses |
| R2 | Database performance issues | High | Low | Monitor | Load testing, optimization plan |
| R3 | Requirements change | Medium | High | Active | Weekly stakeholder sync, flexible architecture |
| R4 | Security vulnerability | High | Low | Monitor | Security audits, dependency scanning |
| R5 | Team member unavailable | Medium | Medium | Active | Documentation, knowledge sharing |
```
### Risk Mitigation Strategies
```markdown
1. Avoidance - Eliminate risk
Risk: Untested technology
Action: Use proven technology stack
2. Reduction - Decrease likelihood/impact
Risk: Integration failures
Action: Early integration testing, CI/CD
3. Transfer - Share risk
Risk: Infrastructure failure
Action: Use cloud provider with SLA
4. Acceptance - Accept risk
Risk: Minor UI inconsistencies
Action: Document and fix in future release
```
## Milestone Planning
### Setting Milestones
```markdown
Project Timeline: 12 weeks
Week 2: M1 - Requirements Complete
- All user stories defined
- Mockups approved
- Technical design ready
✓ Milestone met when: PRD signed off
Week 4: M2 - Foundation Complete
- Database schema implemented
- Authentication working
- Basic API endpoints created
✓ Milestone met when: Users can log in
Week 7: M3 - Core Features Complete
- All CRUD operations working
- Main user flows implemented
- Integration tests passing
✓ Milestone met when: Alpha testing can begin
Week 10: M4 - Feature Complete
- All features implemented
- Bug fixes complete
- Documentation written
✓ Milestone met when: Beta testing ready
Week 12: M5 - Launch
- Production deployment
- Monitoring in place
- Support processes ready
✓ Milestone met when: Live to users
```
## Sprint Planning
### Sprint Structure (2-week sprint)
```markdown
Day 1 - Monday: Sprint Planning
- Review backlog
- Estimate stories
- Commit to sprint goal
Days 2-9: Development
- Daily standups
- Development work
- Code reviews
- Testing
Day 10 - Friday Week 2: Sprint Review & Retrospective
- Demo completed work
- Discuss what went well/poorly
- Plan improvements
```
### Sprint Planning Meeting
```markdown
Agenda (2 hours):
Part 1: Sprint Goal (30 min)
- Review product roadmap
- Define sprint goal
- Identify high-priority items
Example Sprint Goal:
"Enable users to browse and search products"
Part 2: Story Selection (60 min)
- Review top backlog items
- Estimate stories
- Check capacity
- Commit to stories
Team Capacity:
- 5 developers × 8 days × 6 hours = 240 hours
- Velocity: 40 story points per sprint
- Buffer: 20% for bugs/meetings = 32 points
Selected Stories:
- Product list page (5 pts)
- Product search (8 pts)
- Product filters (8 pts)
- Product pagination (3 pts)
- Product sort (3 pts)
- Bug fixes (5 pts)
Total: 32 points
Part 3: Task Breakdown (30 min)
- Break stories into tasks
- Identify blockers
- Assign initial tasks
```
## Capacity Planning
### Calculating Team Capacity
```markdown
Team: 5 Developers
Sprint: 2 weeks (10 working days)
Available Hours:
5 developers × 10 days × 8 hours = 400 hours
Subtract Non-Dev Time:
- Meetings: 2 hours/day × 10 days × 5 people = 100 hours
- Code reviews: 1 hour/day × 10 days × 5 people = 50 hours
- Planning/retro: 4 hours × 5 people = 20 hours
Actual Development Time:
400 - 100 - 50 - 20 = 230 hours
Story Points:
If 1 point ≈ 6 hours
Capacity: 230 ÷ 6 ≈ 38 points
Add 20% buffer: 30 points safe commitment
```
### Handling Vacation and Absences
```markdown
Team Capacity with Absences:
Regular Capacity: 40 points
Developer A: Out entire sprint (-8 points)
Developer B: Out 3 days (-5 points)
Holiday: 1 day for everyone (-8 points)
Adjusted Capacity:
40 - 8 - 5 - 8 = 19 points
Plan accordingly:
- Smaller sprint goal
- Fewer stories
- Focus on high priority
- Avoid risky work
```
## Burndown Charts
### Creating Burndown Charts
```markdown
Sprint Burndown:
Day | Remaining Points | Ideal Burn
----|------------------|------------
0 | 40 | 40
1 | 38 | 36
2 | 35 | 32
3 | 32 | 28
4 | 28 | 24
5 | 28 | 20 ← Weekend
6 | 28 | 16 ← Weekend
7 | 25 | 12
8 | 20 | 8
9 | 12 | 4
10 | 0 | 0
Ideal line: Straight from start to finish
Actual line: Based on completed work
Analysis:
- Days 3-6: Slow progress (blocker?)
- Day 7: Back on track
- Day 9: Ahead of schedule
```
### Interpreting Burndown Trends
```markdown
Scenarios:
1. Line below ideal
→ Ahead of schedule
→ May have underestimated
→ Consider pulling in more work
2. Line above ideal
→ Behind schedule
→ May have overcommitted
→ Identify blockers
→ Consider removing stories
3. Flat line
→ No progress
→ Blocker or team unavailable
→ Immediate intervention needed
4. Increasing line
→ Scope creep
→ Stories added mid-sprint
→ Review sprint boundaries
```
## Velocity Tracking
### Measuring Velocity
```markdown
Historical Velocity:
Sprint 1: 28 points completed
Sprint 2: 32 points completed
Sprint 3: 30 points completed
Sprint 4: 35 points completed
Sprint 5: 33 points completed
Average Velocity: (28+32+30+35+33) ÷ 5 = 31.6 points
Use for Planning:
- Conservative: 28 points (lowest recent)
- Realistic: 32 points (average)
- Optimistic: 35 points (highest recent)
Recommend: Use 32 points for next sprint
```
### Velocity Trends
```markdown
Improving Velocity:
Sprint 1: 20 → Sprint 2: 25 → Sprint 3: 30
- Team learning
- Process improvements
- Good trend
Declining Velocity:
Sprint 1: 35 → Sprint 2: 30 → Sprint 3: 25
- Technical debt accumulating
- Team burnout
- Need intervention
Stable Velocity:
Sprint 1: 30 → Sprint 2: 31 → Sprint 3: 29
- Sustainable pace
- Predictable
- Ideal state
```
## Agile Ceremonies
### Daily Standup (15 minutes)
```markdown
Format: Each person answers:
1. What did I complete yesterday?
2. What will I work on today?
3. What blockers do I have?
Example:
"Yesterday I completed the login form.
Today I'll start on the password reset flow.
I'm blocked on the email template approval."
Anti-patterns:
❌ Status reports to manager
❌ Problem-solving discussions
❌ More than 15 minutes
Best practices:
✓ Same time, same place
✓ Everyone participates
✓ Park detailed discussions
✓ Update task board
```
### Sprint Review (1 hour)
```markdown
Agenda:
1. Demo completed work (40 min)
- Show working software
- Get stakeholder feedback
- Note requested changes
2. Review sprint metrics (10 min)
- Velocity
- Completed vs planned
- Quality metrics
3. Update product backlog (10 min)
- Adjust priorities
- Add new items
- Remove obsolete items
Tips:
- Focus on working software
- No PowerPoint presentations
- Encourage feedback
- Keep it informal
```
### Sprint Retrospective (1 hour)
```markdown
Format: What went well / What to improve / Action items
Example:
What Went Well:
✓ Completed all planned stories
✓ Good collaboration on complex feature
✓ Improved code review process
What to Improve:
⚠ Too many meetings interrupted flow
⚠ Test environment was unstable
⚠ Requirements unclear on story X
Action Items:
1. Block "focus time" 2-4pm daily (Owner: Scrum Master)
2. Fix test environment stability (Owner: DevOps)
3. Refine stories with PO before sprint (Owner: Team Lead)
Follow-up:
- Review action items at next retro
- Track completion
- Celebrate improvements
```
## Stakeholder Communication
### Status Reports
```markdown
Weekly Status Report - Week of Oct 16, 2025
Sprint Progress:
- Completed: 18/32 points (56%)
- On Track: Yes
- Sprint Goal: Enable product browsing
Completed This Week:
✓ Product list page with pagination
✓ Basic search functionality
✓ Product filters (category, price)
In Progress:
• Advanced search with autocomplete (90% done)
• Product sort options (started today)
Upcoming Next Week:
○ Complete remaining search features
○ Begin product detail page
○ Integration testing
Blockers/Risks:
⚠ Designer out sick - UI reviews delayed 1 day
⚠ Third-party API slow - investigating alternatives
Metrics:
- Velocity: 32 points/sprint (stable)
- Bug count: 3 (all low priority)
- Test coverage: 85%
Next Milestone:
M3 - Core Features (Week 7) - On track
```
### Stakeholder Matrix
```markdown
| Stakeholder | Role | Interest | Influence | Communication |
|-------------|------|----------|-----------|---------------|
| CEO | Sponsor | High | High | Monthly exec summary |
| Product Manager | Owner | High | High | Daily collaboration |
| Engineering Manager | Lead | High | High | Daily standup |
| Marketing Director | User | Medium | Medium | Weekly demo |
| Customer Support | User | Medium | Low | Sprint review |
| End Users | Consumer | High | Low | Beta feedback |
```
## Project Tracking Tools
### Issue/Task Management
```markdown
GitHub Issues / Jira / Linear:
Epic: User Authentication
├── Story: Email/Password Login (8 pts)
│ ├── Task: Design login form
│ ├── Task: Implement API endpoint
│ ├── Task: Add validation
│ └── Task: Write tests
├── Story: Social Login (5 pts)
└── Story: Password Reset (5 pts)
Labels:
- Priority: P0 (Critical), P1 (High), P2 (Normal), P3 (Low)
- Type: Feature, Bug, Tech Debt, Documentation
- Status: Todo, In Progress, In Review, Done
- Component: Frontend, Backend, Database, DevOps
```
### Documentation
```markdown
Essential Project Documents:
1. Product Requirements Document (PRD)
- Features and requirements
- User stories
- Acceptance criteria
2. Technical Design Document
- Architecture
- Technology choices
- API design
3. Project Charter
- Goals and objectives
- Scope
- Timeline
- Resources
4. Risk Register
- Identified risks
- Mitigation plans
- Status
5. Sprint Plans
- Sprint goals
- Committed stories
- Capacity
```
## Planning Checklist
**Project Initiation**:
- [ ] Define project goals and objectives
- [ ] Identify stakeholders
- [ ] Create project charter
- [ ] Define scope and requirements
- [ ] Estimate timeline and budget
**Planning Phase**:
- [ ] Create work breakdown structure
- [ ] Estimate tasks
- [ ] Identify dependencies
- [ ] Assess risks
- [ ] Define milestones
- [ ] Allocate resources
**Sprint Planning**:
- [ ] Review and refine backlog
- [ ] Define sprint goal
- [ ] Estimate stories
- [ ] Check team capacity
- [ ] Commit to sprint backlog
- [ ] Break down into tasks
**During Execution**:
- [ ] Track progress daily
- [ ] Update burndown chart
- [ ] Address blockers immediately
- [ ] Communicate with stakeholders
- [ ] Adjust plan as needed
**Sprint Close**:
- [ ] Demo completed work
- [ ] Conduct retrospective
- [ ] Update velocity metrics
- [ ] Plan next sprint
## When to Use This Skill
Use this skill when:
- Starting new projects
- Breaking down large initiatives
- Estimating work effort
- Planning sprints
- Managing dependencies
- Assessing risks
- Tracking progress
- Communicating with stakeholders
- Running agile ceremonies
- Improving team processes
---
**Remember**: Plans are useless, but planning is essential. Stay flexible, communicate often, and adjust course based on reality. The goal is not perfect adherence to the plan, but successfully delivering value to users.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,912 @@
---
name: technical-writing
description: Technical writing best practices including documentation structure, clear writing principles, API documentation, tutorials, changelogs, and markdown formatting. Use when writing documentation, creating READMEs, documenting APIs, or writing tutorials.
---
# Technical Writing
This skill provides comprehensive guidance for creating clear, effective technical documentation that helps users and developers.
## Documentation Structure
### The Four Types of Documentation
**1. Tutorials** (Learning-oriented)
- Goal: Help beginners learn
- Format: Step-by-step lessons
- Example: "Build your first API"
**2. How-to Guides** (Problem-oriented)
- Goal: Solve specific problems
- Format: Numbered steps
- Example: "How to deploy to production"
**3. Reference** (Information-oriented)
- Goal: Provide detailed information
- Format: Systematic descriptions
- Example: API reference, configuration options
**4. Explanation** (Understanding-oriented)
- Goal: Clarify concepts
- Format: Discursive explanations
- Example: Architecture decisions, design patterns
### README Structure
```markdown
# Project Name
Brief description of what the project does (1-2 sentences).
[![Build Status](badge)](link)
[![Coverage](badge)](link)
[![License](badge)](link)
## Features
- Feature 1
- Feature 2
- Feature 3
## Quick Start
```bash
# Installation
npm install project-name
# Usage
npx project-name init
```
## Prerequisites
- Node.js 18+
- PostgreSQL 14+
- Redis 7+
## Installation
### Using npm
```bash
npm install project-name
```
### Using yarn
```bash
yarn add project-name
```
### From source
```bash
git clone https://github.com/user/project.git
cd project
npm install
npm run build
```
## Configuration
Create a `.env` file:
```env
DATABASE_URL=postgresql://user:password@localhost:5432/db
API_KEY=your_api_key
```
## Usage
### Basic Example
```typescript
import { createClient } from 'project-name';
const client = createClient({
apiKey: process.env.API_KEY,
});
const result = await client.doSomething();
console.log(result);
```
### Advanced Example
[More complex example with explanations]
## API Reference
See [API.md](./API.md) for complete API documentation.
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
## License
MIT © [Author Name]
## Support
- Documentation: https://docs.example.com
- Issues: https://github.com/user/project/issues
- Discussions: https://github.com/user/project/discussions
```
## Clear Writing Principles
### Use Active Voice
```markdown
❌ Passive: The data is validated by the function.
✅ Active: The function validates the data.
❌ Passive: Errors should be handled by your application.
✅ Active: Your application should handle errors.
```
### Use Simple Language
```markdown
❌ Complex: Utilize the aforementioned methodology to instantiate a novel instance.
✅ Simple: Use this method to create a new instance.
❌ Jargon: Leverage our SDK to synergize with the API ecosystem.
✅ Clear: Use our SDK to connect to the API.
```
### Be Concise
```markdown
❌ Wordy: In order to be able to successfully complete the installation process,
you will need to make sure that you have Node.js version 18 or higher installed
on your system.
✅ Concise: Install Node.js 18 or higher.
❌ Redundant: The function returns back a response.
✅ Concise: The function returns a response.
```
### Use Consistent Terminology
```markdown
❌ Inconsistent:
- Create a user
- Add an account
- Register a member
(All referring to the same action)
✅ Consistent:
- Create a user
- Update a user
- Delete a user
```
## Code Example Best Practices
### Complete, Runnable Examples
```typescript
// ❌ BAD - Incomplete example
user.save();
// ✅ GOOD - Complete example
import { User } from './models';
async function createUser() {
const user = new User({
email: 'user@example.com',
name: 'John Doe',
});
await user.save();
console.log('User created:', user.id);
}
createUser();
```
### Show Expected Output
```typescript
// Calculate fibonacci number
function fibonacci(n: number): number {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(10));
// Output: 55
```
### Highlight Important Parts
```typescript
// Authenticate user with JWT
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 👇 Important: Always use bcrypt for password comparison
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = generateToken(user);
res.json({ token });
});
```
### Provide Context
```typescript
// ❌ BAD - No context
await client.query('SELECT * FROM users');
// ✅ GOOD - With context
// Fetch all active users who logged in within the last 30 days
const activeUsers = await client.query(`
SELECT id, email, name, last_login
FROM users
WHERE status = 'active'
AND last_login > NOW() - INTERVAL '30 days'
ORDER BY last_login DESC
`);
```
## Tutorial Structure
### Learning Progression
**1. Introduction** (2-3 sentences)
- What will users learn?
- Why is it useful?
**2. Prerequisites**
- Required knowledge
- Required tools
- Time estimate
**3. Step-by-Step Instructions**
- Number each step
- One concept per step
- Show results after each step
**4. Next Steps**
- Links to related tutorials
- Advanced topics
- Additional resources
### Tutorial Example
```markdown
# Building a REST API with Express
In this tutorial, you'll build a REST API for managing a todo list.
You'll learn how to create routes, handle requests, and connect to a database.
**Time**: 30 minutes
**Level**: Beginner
## Prerequisites
- Node.js 18+ installed
- Basic JavaScript knowledge
- Code editor (VS Code recommended)
## Step 1: Set Up Project
Create a new project directory and initialize npm:
```bash
mkdir todo-api
cd todo-api
npm init -y
```
Install Express:
```bash
npm install express
```
You should see `express` added to your `package.json`.
## Step 2: Create Basic Server
Create `index.js`:
```javascript
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({ message: 'Hello, World!' });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
```
Run the server:
```bash
node index.js
```
Visit http://localhost:3000 in your browser. You should see:
```json
{ "message": "Hello, World!" }
```
## Step 3: Add Todo Routes
[Continue with more steps...]
## What You Learned
- How to set up an Express server
- How to create REST API routes
- How to connect to a database
## Next Steps
- [Authentication with JWT](./auth-tutorial.md)
- [Deploy to Production](./deploy-guide.md)
- [API Best Practices](./api-best-practices.md)
```
## API Documentation Patterns
### Endpoint Documentation
```markdown
## Create User
Creates a new user account.
**Endpoint**: `POST /api/v1/users`
**Authentication**: Not required
**Request Body**:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| email | string | Yes | User's email address (must be valid) |
| password | string | Yes | Password (min 8 characters) |
| name | string | Yes | User's full name (max 100 characters) |
**Example Request**:
```bash
curl -X POST https://api.example.com/v1/users \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecurePass123",
"name": "John Doe"
}'
```
**Success Response** (201 Created):
```json
{
"id": "user_abc123",
"email": "user@example.com",
"name": "John Doe",
"createdAt": "2025-10-16T10:30:00Z"
}
```
**Error Responses**:
**400 Bad Request** - Invalid input:
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email address",
"field": "email"
}
}
```
**409 Conflict** - Email already exists:
```json
{
"error": {
"code": "EMAIL_EXISTS",
"message": "Email address already registered"
}
}
```
**Rate Limit**: 5 requests per minute
```
### Function/Method Documentation
```typescript
/**
* Calculates the total price of items including tax.
*
* @param items - Array of items to calculate total for
* @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%)
* @returns Total price including tax
*
* @throws {Error} If items array is empty
* @throws {Error} If taxRate is negative
*
* @example
* ```typescript
* const items = [
* { price: 10, quantity: 2 },
* { price: 15, quantity: 1 }
* ];
* const total = calculateTotal(items, 0.08);
* console.log(total); // 37.80
* ```
*/
function calculateTotal(
items: Array<{ price: number; quantity: number }>,
taxRate: number
): number {
if (items.length === 0) {
throw new Error('Items array cannot be empty');
}
if (taxRate < 0) {
throw new Error('Tax rate cannot be negative');
}
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return subtotal * (1 + taxRate);
}
```
## Changelog Best Practices
### Keep a Changelog Format
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- New feature X for Y use case
### Changed
- Improved performance of Z operation
### Fixed
- Fixed bug where A caused B
## [2.1.0] - 2025-10-16
### Added
- User profile avatars (#123)
- Email notification settings (#125)
- Two-factor authentication support (#130)
### Changed
- Updated UI for settings page (#124)
- Improved API response times by 40% (#128)
### Deprecated
- `oldFunction()` will be removed in v3.0 - use `newFunction()` instead
### Fixed
- Fixed memory leak in session management (#126)
- Corrected timezone handling in reports (#129)
### Security
- Updated dependencies to patch security vulnerabilities (#127)
## [2.0.0] - 2025-09-01
### Added
- Complete redesign of dashboard
- GraphQL API support
### Changed
- **BREAKING**: Renamed `create_user` to `createUser` for consistency
- **BREAKING**: Changed date format from `DD/MM/YYYY` to ISO 8601
### Removed
- **BREAKING**: Removed deprecated v1 API endpoints
[Unreleased]: https://github.com/user/project/compare/v2.1.0...HEAD
[2.1.0]: https://github.com/user/project/compare/v2.0.0...v2.1.0
[2.0.0]: https://github.com/user/project/releases/tag/v2.0.0
```
### Version Numbering
**Semantic Versioning (MAJOR.MINOR.PATCH)**:
- **MAJOR**: Breaking changes (2.0.0 → 3.0.0)
- **MINOR**: New features, backwards compatible (2.0.0 → 2.1.0)
- **PATCH**: Bug fixes, backwards compatible (2.0.0 → 2.0.1)
## Markdown Formatting
### Headers
```markdown
# H1 - Main title
## H2 - Section
### H3 - Subsection
#### H4 - Sub-subsection
```
### Emphasis
```markdown
**Bold text** or __bold__
*Italic text* or _italic_
***Bold and italic***
~~Strikethrough~~
`Inline code`
```
### Lists
```markdown
Unordered list:
- Item 1
- Item 2
- Nested item
- Another nested item
- Item 3
Ordered list:
1. First item
2. Second item
1. Nested item
2. Another nested item
3. Third item
Task list:
- [x] Completed task
- [ ] Incomplete task
```
### Links and Images
```markdown
[Link text](https://example.com)
[Link with title](https://example.com "Title text")
![Alt text](image.jpg)
![Alt text](image.jpg "Image title")
```
### Code Blocks
````markdown
Inline code: `const x = 5;`
Code block:
```javascript
function greet(name) {
console.log(`Hello, ${name}!`);
}
```
With line highlighting:
```javascript {2}
function greet(name) {
console.log(`Hello, ${name}!`); // This line is highlighted
}
```
````
### Tables
```markdown
| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Row 1 | Data | More |
| Row 2 | Data | More |
Alignment:
| Left | Center | Right |
|:-----|:------:|------:|
| L | C | R |
```
### Blockquotes
```markdown
> Single line quote
> Multi-line
> quote with
> several lines
> **Note**: Important information
```
### Admonitions
```markdown
> **⚠️ Warning**: This action cannot be undone.
> **💡 Tip**: Use keyboard shortcuts to speed up your workflow.
> **🚨 Danger**: Never commit secrets to version control.
> ** Info**: This feature requires Node.js 18+.
```
## Diagrams and Visuals
### When to Use Diagrams
**Use diagrams for**:
- System architecture
- Data flow
- Process flows
- Component relationships
- Complex concepts
**Don't use diagrams for**:
- Simple concepts (text is better)
- Things that change frequently
- Content that can be code
### Mermaid Diagrams
````markdown
```mermaid
graph TD
A[User Request] --> B{Authenticated?}
B -->|Yes| C[Process Request]
B -->|No| D[Return 401]
C --> E[Return Response]
```
```mermaid
sequenceDiagram
Client->>API: POST /users
API->>Database: INSERT user
Database-->>API: User created
API->>Email: Send welcome email
API-->>Client: 201 Created
```
````
### ASCII Diagrams
```markdown
┌─────────────┐ ┌──────────────┐ ┌──────────┐
│ Client │─────▶│ API Server │─────▶│ Database │
│ (Browser) │◀─────│ (Express) │◀─────│ (Postgres)│
└─────────────┘ └──────────────┘ └──────────┘
```
## Progressive Disclosure
### Start Simple, Add Details
```markdown
## Installation
Install via npm:
```bash
npm install package-name
```
<details>
<summary>Advanced installation options</summary>
### Install from source
```bash
git clone https://github.com/user/package.git
cd package
npm install
npm run build
npm link
```
### Install specific version
```bash
npm install package-name@2.1.0
```
### Install with peer dependencies
```bash
npm install package-name react react-dom
```
</details>
```
### Organize by Skill Level
```markdown
## Quick Start (Beginner)
Get up and running in 5 minutes:
[Simple example]
## Advanced Usage
For experienced users:
[Complex example]
## Expert Topics
Deep dive into internals:
[Very advanced example]
```
## User-Focused Language
### Address the Reader
```markdown
❌ Impersonal: The configuration file should be updated.
✅ Personal: Update your configuration file.
❌ Distant: One must install the dependencies.
✅ Direct: Install the dependencies.
```
### Use "You" Not "We"
```markdown
❌ We: Now we'll create a new user.
✅ You: Now you'll create a new user.
❌ We: We recommend using TypeScript.
✅ You: We recommend you use TypeScript.
```
### Be Helpful
```markdown
❌ Vague: An error occurred.
✅ Helpful: Connection failed. Check your network and try again.
❌ Blaming: You entered invalid data.
✅ Helpful: The email field requires a valid email address (e.g., user@example.com).
```
## Avoiding Jargon
### Define Technical Terms
```markdown
❌ Assumes knowledge:
"Use the ORM to query the RDBMS."
✅ Explains terms:
"Use the ORM (Object-Relational Mapping tool) to query the database.
An ORM lets you interact with your database using code instead of SQL."
```
### Use Common Words
```markdown
❌ Technical jargon:
"Leverage the API to facilitate data ingestion."
✅ Plain English:
"Use the API to import data."
```
## Version Documentation
### Document Version Changes
```markdown
## Version Compatibility
| Version | Node.js | Features |
|---------|---------|----------|
| 3.x | 18+ | Full feature set |
| 2.x | 16+ | Legacy API (deprecated) |
| 1.x | 14+ | No longer supported |
## Upgrading from 2.x to 3.x
### Breaking Changes
**1. Renamed functions**
```typescript
// v2.x
import { create_user } from 'package';
// v3.x
import { createUser } from 'package';
```
**2. Changed date format**
Dates now use ISO 8601 format:
- Old: `01/15/2025`
- New: `2025-01-15T00:00:00Z`
### Migration Guide
1. Update imports:
```bash
# Run this command to update your code
npx package-migrate-v3
```
2. Update date handling:
```typescript
// Before
const date = '01/15/2025';
// After
const date = '2025-01-15T00:00:00Z';
```
3. Test thoroughly before deploying.
```
## Documentation Checklist
**Before Writing**:
- [ ] Who is the audience (beginner/intermediate/expert)?
- [ ] What do they need to accomplish?
- [ ] What do they already know?
**While Writing**:
- [ ] Use active voice
- [ ] Use simple language
- [ ] Be concise
- [ ] Provide examples
- [ ] Show expected output
**After Writing**:
- [ ] Read it aloud
- [ ] Have someone else review it
- [ ] Test all code examples
- [ ] Check all links
- [ ] Spell check
## When to Use This Skill
Use this skill when:
- Writing project READMEs
- Creating API documentation
- Writing tutorials
- Documenting code
- Creating user guides
- Writing changelogs
- Contributing to open source
- Creating internal documentation
- Writing blog posts about technical topics
- Training others on technical writing
---
**Remember**: Good documentation is empathetic. Always write for the person reading your docs at 2 AM who just wants to get their code working. Be clear, be helpful, and be kind.

View File

@@ -0,0 +1,909 @@
---
name: testing-strategy
description: Comprehensive testing strategies including test pyramid, TDD methodology, testing patterns, coverage goals, and CI/CD integration. Use when writing tests, implementing TDD, reviewing test coverage, debugging test failures, or setting up testing infrastructure.
---
# Testing Strategy
This skill provides comprehensive guidance for implementing effective testing strategies across your entire application stack.
## Test Pyramid
### The Testing Hierarchy
```
/\
/ \
/E2E \ 10% - End-to-End Tests (slowest, most expensive)
/______\
/ \
/Integration\ 20% - Integration Tests (medium speed/cost)
/____________\
/ \
/ Unit Tests \ 70% - Unit Tests (fast, cheap, focused)
/__________________\
```
**Rationale**:
- **70% Unit Tests**: Fast, isolated, catch bugs early
- **20% Integration Tests**: Test component interactions
- **10% E2E Tests**: Test critical user journeys
### Why This Distribution?
**Unit tests are cheap**:
- Run in milliseconds
- No external dependencies
- Easy to debug
- High code coverage per test
**Integration tests are moderate**:
- Test real interactions
- Catch integration bugs
- Slower than unit tests
- More complex setup
**E2E tests are expensive**:
- Test entire system
- Catch UX issues
- Very slow (seconds/minutes)
- Brittle and hard to maintain
## TDD (Test-Driven Development)
### Red-Green-Refactor Cycle
**1. Red - Write a failing test**:
```typescript
describe('Calculator', () => {
test('adds two numbers', () => {
const calculator = new Calculator();
expect(calculator.add(2, 3)).toBe(5); // FAILS - method doesn't exist
});
});
```
**2. Green - Write minimal code to pass**:
```typescript
class Calculator {
add(a: number, b: number): number {
return a + b; // Simplest implementation
}
}
// Test now PASSES
```
**3. Refactor - Improve the code**:
```typescript
class Calculator {
add(a: number, b: number): number {
// Add validation
if (!Number.isFinite(a) || !Number.isFinite(b)) {
throw new Error('Arguments must be finite numbers');
}
return a + b;
}
}
```
### TDD Benefits
**Design benefits**:
- Forces you to think about API before implementation
- Leads to more testable, modular code
- Encourages SOLID principles
**Quality benefits**:
- 100% test coverage by design
- Catches bugs immediately
- Provides living documentation
**Workflow benefits**:
- Clear next step (make test pass)
- Confidence when refactoring
- Prevents over-engineering
## Arrange-Act-Assert Pattern
### The AAA Pattern
Every test should follow this structure:
```typescript
test('user registration creates account and sends welcome email', async () => {
// ARRANGE - Set up test conditions
const userData = {
email: 'test@example.com',
password: 'SecurePass123',
name: 'Test User',
};
const mockEmailService = jest.fn();
const userService = new UserService(mockEmailService);
// ACT - Execute the behavior being tested
const result = await userService.register(userData);
// ASSERT - Verify the outcome
expect(result.id).toBeDefined();
expect(result.email).toBe(userData.email);
expect(mockEmailService).toHaveBeenCalledWith({
to: userData.email,
subject: 'Welcome!',
template: 'welcome',
});
});
```
### Why AAA?
- **Clear structure**: Easy to understand what's being tested
- **Consistent**: All tests follow same pattern
- **Maintainable**: Easy to modify and debug
## Mocking Strategies
### When to Mock
**✅ DO mock**:
- External APIs
- Databases
- File system operations
- Time/dates
- Random number generators
- Network requests
- Third-party services
```typescript
// Mock external API
jest.mock('axios');
test('fetches user data from API', async () => {
const mockData = { id: 1, name: 'John' };
(axios.get as jest.Mock).mockResolvedValue({ data: mockData });
const user = await fetchUser(1);
expect(user).toEqual(mockData);
});
```
### When NOT to Mock
**❌ DON'T mock**:
- Pure functions (test them directly)
- Simple utility functions
- Domain logic
- Value objects
- Internal implementation details
```typescript
// ❌ BAD - Over-mocking
test('validates email', () => {
const validator = new EmailValidator();
jest.spyOn(validator, 'isValid').mockReturnValue(true);
expect(validator.isValid('test@example.com')).toBe(true);
// This test is useless - you're testing the mock, not the code
});
// ✅ GOOD - Test real implementation
test('validates email', () => {
const validator = new EmailValidator();
expect(validator.isValid('test@example.com')).toBe(true);
expect(validator.isValid('invalid')).toBe(false);
});
```
### Mocking Patterns
**Stub** (return predetermined values):
```typescript
const mockDatabase = {
findUser: jest.fn().mockResolvedValue({ id: 1, name: 'John' }),
saveUser: jest.fn().mockResolvedValue(true),
};
```
**Spy** (track calls, use real implementation):
```typescript
const emailService = new EmailService();
const sendSpy = jest.spyOn(emailService, 'send');
await emailService.send('test@example.com', 'Hello');
expect(sendSpy).toHaveBeenCalledTimes(1);
expect(sendSpy).toHaveBeenCalledWith('test@example.com', 'Hello');
```
**Fake** (lightweight implementation):
```typescript
class FakeDatabase {
private data = new Map();
async save(key: string, value: any) {
this.data.set(key, value);
}
async get(key: string) {
return this.data.get(key);
}
}
```
## Test Coverage Goals
### Coverage Metrics
**Line Coverage**: Percentage of code lines executed
- **Target**: 80-90% for critical paths
**Branch Coverage**: Percentage of if/else branches tested
- **Target**: 80%+ (more important than line coverage)
**Function Coverage**: Percentage of functions called
- **Target**: 90%+
**Statement Coverage**: Percentage of statements executed
- **Target**: 80%+
### Coverage Configuration
```json
// package.json
{
"jest": {
"collectCoverage": true,
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 90,
"lines": 80,
"statements": 80
},
"./src/critical/": {
"branches": 95,
"functions": 95,
"lines": 95,
"statements": 95
}
},
"coveragePathIgnorePatterns": [
"/node_modules/",
"/tests/",
"/migrations/",
"/.config.ts$/"
]
}
}
```
### What to Prioritize
**High priority** (aim for 95%+ coverage):
- Business logic
- Security-critical code
- Payment/billing code
- Data validation
- Authentication/authorization
**Medium priority** (aim for 80%+ coverage):
- API endpoints
- Database queries
- Utility functions
- Error handling
**Low priority** (optional coverage):
- UI components (use integration tests instead)
- Configuration files
- Type definitions
- Third-party library wrappers
## Integration Testing
### Database Integration Tests
```typescript
import { PrismaClient } from '@prisma/client';
describe('UserRepository', () => {
let prisma: PrismaClient;
let repository: UserRepository;
beforeAll(async () => {
// Use test database
prisma = new PrismaClient({
datasources: { db: { url: process.env.TEST_DATABASE_URL } },
});
repository = new UserRepository(prisma);
});
beforeEach(async () => {
// Clean database before each test
await prisma.user.deleteMany();
});
afterAll(async () => {
await prisma.$disconnect();
});
test('creates user and retrieves by email', async () => {
// ARRANGE
const userData = {
email: 'test@example.com',
name: 'Test User',
password: 'hashed_password',
};
// ACT
const created = await repository.create(userData);
const retrieved = await repository.findByEmail(userData.email);
// ASSERT
expect(retrieved).toBeDefined();
expect(retrieved?.id).toBe(created.id);
expect(retrieved?.email).toBe(userData.email);
});
});
```
### API Integration Tests
```typescript
import request from 'supertest';
import { app } from '../src/app';
describe('User API', () => {
test('POST /api/users creates user and returns 201', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: 'test@example.com',
password: 'SecurePass123',
name: 'Test User',
})
.expect(201);
expect(response.body).toMatchObject({
email: 'test@example.com',
name: 'Test User',
});
expect(response.body.password).toBeUndefined(); // Never return password
});
test('POST /api/users returns 400 for invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: 'invalid-email',
password: 'SecurePass123',
name: 'Test User',
})
.expect(400);
expect(response.body.error.code).toBe('VALIDATION_ERROR');
});
});
```
### Service Integration Tests
```typescript
describe('OrderService Integration', () => {
test('complete order flow', async () => {
// Create order
const order = await orderService.create({
userId: 'user_123',
items: [{ productId: 'prod_1', quantity: 2 }],
});
// Process payment
const payment = await paymentService.process({
orderId: order.id,
amount: order.total,
});
// Verify inventory updated
const product = await inventoryService.getProduct('prod_1');
expect(product.stock).toBe(originalStock - 2);
// Verify order status updated
const updatedOrder = await orderService.getById(order.id);
expect(updatedOrder.status).toBe('paid');
});
});
```
## E2E Testing
### Playwright Setup
```typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } },
],
});
```
### E2E Test Example
```typescript
import { test, expect } from '@playwright/test';
test.describe('User Registration Flow', () => {
test('user can register and login', async ({ page }) => {
// Navigate to registration page
await page.goto('/register');
// Fill registration form
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'SecurePass123');
await page.fill('[name="confirmPassword"]', 'SecurePass123');
await page.fill('[name="name"]', 'Test User');
// Submit form
await page.click('button[type="submit"]');
// Wait for redirect to dashboard
await page.waitForURL('/dashboard');
// Verify welcome message
await expect(page.locator('h1')).toContainText('Welcome, Test User');
});
test('shows validation errors for invalid input', async ({ page }) => {
await page.goto('/register');
await page.fill('[name="email"]', 'invalid-email');
await page.fill('[name="password"]', '123'); // Too short
await page.click('button[type="submit"]');
// Verify error messages displayed
await expect(page.locator('[data-testid="email-error"]'))
.toContainText('Invalid email');
await expect(page.locator('[data-testid="password-error"]'))
.toContainText('at least 8 characters');
});
});
```
### Critical E2E Scenarios
Test these critical user journeys:
- User registration and login
- Checkout and payment flow
- Password reset
- Profile updates
- Critical business workflows
## Performance Testing
### Load Testing with k6
```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 }, // Ramp up to 20 users
{ duration: '1m', target: 20 }, // Stay at 20 users
{ duration: '30s', target: 100 }, // Ramp up to 100 users
{ duration: '1m', target: 100 }, // Stay at 100 users
{ duration: '30s', target: 0 }, // Ramp down to 0 users
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% error rate
},
};
export default function() {
const response = http.get('https://api.example.com/users');
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
```
### Benchmark Testing
```typescript
import { performance } from 'perf_hooks';
describe('Performance Benchmarks', () => {
test('database query completes in under 100ms', async () => {
const start = performance.now();
await database.query('SELECT * FROM users WHERE email = ?', ['test@example.com']);
const duration = performance.now() - start;
expect(duration).toBeLessThan(100);
});
test('API endpoint responds in under 200ms', async () => {
const start = performance.now();
await request(app).get('/api/users/123');
const duration = performance.now() - start;
expect(duration).toBeLessThan(200);
});
});
```
## Flaky Test Prevention
### Common Causes of Flaky Tests
**1. Race Conditions**:
```typescript
// ❌ BAD - Race condition
test('displays data', async () => {
fetchData();
expect(screen.getByText('Data loaded')).toBeInTheDocument();
// Fails intermittently if fetchData takes longer than expected
});
// ✅ GOOD - Wait for async operation
test('displays data', async () => {
fetchData();
await screen.findByText('Data loaded'); // Waits up to 1 second
});
```
**2. Time Dependencies**:
```typescript
// ❌ BAD - Depends on current time
test('shows message for new users', () => {
const user = { createdAt: new Date() };
expect(isNewUser(user)).toBe(true);
// Fails if test runs slowly
});
// ✅ GOOD - Mock time
test('shows message for new users', () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2025-10-16'));
const user = { createdAt: new Date('2025-10-15') };
expect(isNewUser(user)).toBe(true);
jest.useRealTimers();
});
```
**3. Shared State**:
```typescript
// ❌ BAD - Tests share state
let counter = 0;
test('increments counter', () => {
counter++;
expect(counter).toBe(1);
});
test('increments counter again', () => {
counter++;
expect(counter).toBe(1); // Fails if first test ran
});
// ✅ GOOD - Isolated state
test('increments counter', () => {
const counter = new Counter();
counter.increment();
expect(counter.value).toBe(1);
});
```
### Flaky Test Best Practices
1. **Always clean up after tests**:
```typescript
afterEach(async () => {
await database.truncate();
jest.clearAllMocks();
jest.useRealTimers();
});
```
2. **Use explicit waits, not delays**:
```typescript
// ❌ BAD
await sleep(1000);
// ✅ GOOD
await waitFor(() => expect(element).toBeInTheDocument());
```
3. **Isolate test data**:
```typescript
test('creates user', async () => {
const uniqueEmail = `test-${Date.now()}@example.com`;
const user = await createUser({ email: uniqueEmail });
expect(user.email).toBe(uniqueEmail);
});
```
## Test Data Management
### Test Fixtures
```typescript
// fixtures/users.ts
export const testUsers = {
admin: {
email: 'admin@example.com',
password: 'AdminPass123',
role: 'admin',
},
regular: {
email: 'user@example.com',
password: 'UserPass123',
role: 'user',
},
};
// Usage in tests
import { testUsers } from './fixtures/users';
test('admin can delete users', async () => {
const admin = await createUser(testUsers.admin);
// Test admin functionality
});
```
### Factory Pattern
```typescript
class UserFactory {
static create(overrides = {}) {
return {
id: faker.datatype.uuid(),
email: faker.internet.email(),
name: faker.name.fullName(),
createdAt: new Date(),
...overrides,
};
}
static createMany(count: number, overrides = {}) {
return Array.from({ length: count }, () => this.create(overrides));
}
}
// Usage
test('displays user list', () => {
const users = UserFactory.createMany(5);
render(<UserList users={users} />);
expect(screen.getAllByRole('listitem')).toHaveLength(5);
});
```
### Database Seeding
```typescript
// seeds/test-seed.ts
export async function seedTestDatabase() {
// Create admin user
const admin = await prisma.user.create({
data: { email: 'admin@test.com', role: 'admin' },
});
// Create test products
const products = await Promise.all([
prisma.product.create({ data: { name: 'Product 1', price: 10 } }),
prisma.product.create({ data: { name: 'Product 2', price: 20 } }),
]);
return { admin, products };
}
// Usage
beforeEach(async () => {
await prisma.$executeRaw`TRUNCATE TABLE users CASCADE`;
const { admin, products } = await seedTestDatabase();
});
```
## CI/CD Integration
### GitHub Actions Configuration
```yaml
# .github/workflows/test.yml
name: Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run type check
run: npm run type-check
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
- name: Run E2E tests
run: npm run test:e2e
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
fail_ci_if_error: true
```
### Test Scripts Organization
```json
// package.json
{
"scripts": {
"test": "npm run test:unit && npm run test:integration && npm run test:e2e",
"test:unit": "jest --testPathPattern=\\.test\\.ts$",
"test:integration": "jest --testPathPattern=\\.integration\\.ts$",
"test:e2e": "playwright test",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --maxWorkers=2"
}
}
```
### Test Performance in CI
**Parallel execution**:
```yaml
jobs:
test:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npm test -- --shard=${{ matrix.shard }}/4
```
**Cache dependencies**:
```yaml
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
```
## Test Organization
### File Structure
```
tests/
├── unit/ # Fast, isolated tests
│ ├── services/
│ │ ├── user-service.test.ts
│ │ └── order-service.test.ts
│ └── utils/
│ ├── validator.test.ts
│ └── formatter.test.ts
├── integration/ # Database, API tests
│ ├── api/
│ │ ├── users.integration.ts
│ │ └── orders.integration.ts
│ └── database/
│ └── repository.integration.ts
├── e2e/ # End-to-end tests
│ ├── auth.spec.ts
│ ├── checkout.spec.ts
│ └── profile.spec.ts
├── fixtures/ # Test data
│ ├── users.ts
│ └── products.ts
└── helpers/ # Test utilities
├── setup.ts
└── factories.ts
```
### Test Naming Conventions
```typescript
// Pattern: describe('Component/Function', () => test('should...when...'))
describe('UserService', () => {
describe('register', () => {
test('should create user when valid data provided', async () => {
// Test implementation
});
test('should throw error when email already exists', async () => {
// Test implementation
});
test('should hash password before saving', async () => {
// Test implementation
});
});
describe('login', () => {
test('should return token when credentials are valid', async () => {
// Test implementation
});
test('should throw error when password is incorrect', async () => {
// Test implementation
});
});
});
```
## When to Use This Skill
Use this skill when:
- Setting up testing infrastructure
- Writing unit, integration, or E2E tests
- Implementing TDD methodology
- Reviewing test coverage
- Debugging flaky tests
- Optimizing test performance
- Configuring CI/CD pipelines
- Establishing testing standards
- Training team on testing practices
- Improving code quality through testing
---
**Remember**: Good tests give you confidence to refactor, catch bugs early, and serve as living documentation. Invest in your test suite and it will pay dividends throughout the project lifecycle.