development-best-practices
Development Best Practices
Universal coding standards and anti-patterns that apply to ALL code regardless of feature or technology.
Quick Start
Validate feature design:
"Check feature-5.1 design against development-best-practices"
Validate implementation:
"Verify src/services/ follows development-best-practices"
Check code before commit:
"Review my changes against development-best-practices before committing"
Core Workflow
Step 1: Anti-Hallucination Validation
Purpose: Prevent AI agents from inventing code, APIs, or file structures that don't exist
Validation Checks:
-
File Existence Before Reading
# ❌ BAD: Assume file exists with open('config.yaml') as f: config = yaml.load(f) # ✅ GOOD: Check existence first if os.path.exists('config.yaml'): with open('config.yaml') as f: config = yaml.load(f) else: raise FileNotFoundError("config.yaml not found") -
API Response Validation
// ❌ BAD: Assume response structure const user = await api.getUser(userId); console.log(user.email); // ✅ GOOD: Validate response structure const user = await api.getUser(userId); if (!user || !user.email) { throw new Error("Invalid user response"); } console.log(user.email); -
Test Assumptions with Actual Data
// ❌ BAD: Assume API returns array const items = await api.getItems(); items.forEach(item => console.log(item)); // ✅ GOOD: Verify it's an array const items = await api.getItems(); if (!Array.isArray(items)) { throw new Error("Expected array, got " + typeof items); } items.forEach(item => console.log(item)); -
Read Actual Code, Don't Invent Signatures
// ❌ BAD: Guess function signature // Assume: calculateTotal(items, tax, discount) const total = calculateTotal(cartItems, 0.08, 10); // ✅ GOOD: Read actual function signature first // After reading code: calculateTotal(items: Item[]): number // Tax/discount handled internally via config const total = calculateTotal(cartItems); -
Validate Configuration Values Exist
// ❌ BAD: Assume env var exists const apiUrl = process.env.API_URL; fetch(apiUrl + '/users'); // ✅ GOOD: Validate env var exists const apiUrl = process.env.API_URL; if (!apiUrl) { throw new Error("API_URL environment variable not set"); } fetch(apiUrl + '/users');
Checklist:
- Verified file existence before reading/writing
- Validated API response structure before accessing
- Tested assumptions with console.log or debugger
- Read actual function signatures from source code
- Checked configuration values exist before using
- No invented APIs, methods, or properties
Step 2: Anti-Hardcoding Validation
Purpose: Ensure all environment-specific values are externalized
Validation Checks:
-
No Hardcoded URLs
// ❌ BAD: Hardcoded URL const API_URL = 'https://api.prod.company.com'; // ✅ GOOD: Environment variable const API_URL = process.env.API_URL || 'http://localhost:3000'; -
No Hardcoded Credentials
// ❌ BAD: Hardcoded API key const apiKey = 'sk_live_abc123xyz789'; // ✅ GOOD: Environment variable const apiKey = process.env.API_KEY; if (!apiKey) throw new Error("API_KEY not configured"); -
Extract Magic Numbers to Constants
// ❌ BAD: Magic numbers if (user.age >= 18 && cartTotal > 100) { applyDiscount(cartTotal * 0.1); } // ✅ GOOD: Named constants const MINIMUM_AGE = 18; const DISCOUNT_THRESHOLD = 100; const DISCOUNT_RATE = 0.1; if (user.age >= MINIMUM_AGE && cartTotal > DISCOUNT_THRESHOLD) { applyDiscount(cartTotal * DISCOUNT_RATE); } -
Use Feature Flags for Toggles
// ❌ BAD: Hardcoded feature toggle const enableNewCheckout = true; // ✅ GOOD: Configuration-based feature flag const enableNewCheckout = config.get('features.newCheckout', false); -
No Environment-Specific Logic in Code
// ❌ BAD: Environment checks in code if (process.env.NODE_ENV === 'production') { // Use production logic } else { // Use dev logic } // ✅ GOOD: Configuration-driven const useCache = config.get('cache.enabled'); if (useCache) { // Use cache }
Checklist:
- No hardcoded URLs, endpoints, or service addresses
- No hardcoded API keys, tokens, or credentials
- No magic numbers (extracted to named constants)
- No hardcoded database connection strings
- No environment-specific logic in code (use config)
- Feature flags in configuration, not code
Step 3: Error Handling Validation
Purpose: Ensure all risky operations have proper error handling
Validation Checks:
-
Wrap Risky Operations in Try-Catch
// ❌ BAD: No error handling const data = JSON.parse(jsonString); const result = await api.fetchData(); // ✅ GOOD: Try-catch around risky operations let data; try { data = JSON.parse(jsonString); } catch (error) { console.error("Failed to parse JSON", error); throw new Error("Invalid JSON format"); } let result; try { result = await api.fetchData(); } catch (error) { console.error("API call failed", error); throw new Error("Failed to fetch data from API"); } -
Propagate Errors Correctly
// ❌ BAD: Swallow errors async function loadUser(userId: string) { try { return await api.getUser(userId); } catch (error) { console.error(error); return null; // Error swallowed } } // ✅ GOOD: Re-throw or return error async function loadUser(userId: string): Promise<User> { try { return await api.getUser(userId); } catch (error) { console.error("Failed to load user", userId, error); throw new Error(`User ${userId} not found`); } } -
User-Friendly Error Messages
// ❌ BAD: Technical error to user catch (error) { alert(error.stack); } // ✅ GOOD: User-friendly message catch (error) { console.error("Technical error:", error); showNotification("Unable to load data. Please try again later.", "error"); } -
Log Errors Appropriately
// ❌ BAD: No logging catch (error) { throw error; } // ✅ GOOD: Log before re-throwing catch (error) { logger.error("Failed to process payment", { userId: user.id, amount: amount, error: error.message, stack: error.stack }); throw new Error("Payment processing failed"); } -
Cleanup with Finally Blocks
// ❌ BAD: Resources not cleaned up const connection = await db.connect(); await connection.query(sql); await connection.close(); // ✅ GOOD: Finally block ensures cleanup const connection = await db.connect(); try { await connection.query(sql); } finally { await connection.close(); }
Checklist:
- All risky operations wrapped in try-catch
- Errors logged with context (user ID, action, etc.)
- Errors propagated or handled appropriately (not swallowed)
- User-friendly error messages (no stack traces to users)
- Cleanup code in finally blocks (close connections, files)
- Specific error types thrown (not generic Error)
Step 4: Logging Validation
Purpose: Ensure consistent, structured logging throughout application
Validation Checks:
-
Use Structured Logging (JSON Format)
// ❌ BAD: String concatenation console.log("User " + userId + " logged in at " + new Date()); // ✅ GOOD: Structured logging logger.info("User logged in", { userId: userId, timestamp: new Date().toISOString(), ipAddress: req.ip }); -
No Sensitive Data in Logs
// ❌ BAD: Logging sensitive data logger.info("User authenticated", { userId: user.id, password: user.password, // 🚨 Never log passwords ssn: user.ssn // 🚨 Never log PII }); // ✅ GOOD: Redact sensitive data logger.info("User authenticated", { userId: user.id, email: maskEmail(user.email) // user@example.com → u***@example.com }); -
Appropriate Log Levels
// ❌ BAD: Everything at INFO level logger.info("Starting application"); logger.info("User not found"); // Should be WARN logger.info("Database connection failed"); // Should be ERROR logger.info("Query took 5ms"); // Should be DEBUG // ✅ GOOD: Proper log levels logger.info("Starting application"); logger.warn("User not found", { userId }); logger.error("Database connection failed", { error }); logger.debug("Query executed", { duration: 5, query }); -
Correlation IDs for Tracing
// ❌ BAD: No request correlation logger.info("Processing payment"); logger.info("Payment successful"); // ✅ GOOD: Include correlation ID const correlationId = req.headers['x-correlation-id'] || uuidv4(); logger.info("Processing payment", { correlationId, userId, amount }); logger.info("Payment successful", { correlationId, transactionId }); -
Performance Logging for Slow Operations
// ❌ BAD: No performance tracking const result = await expensiveOperation(); // ✅ GOOD: Log slow operations const start = Date.now(); const result = await expensiveOperation(); const duration = Date.now() - start; if (duration > 1000) { logger.warn("Slow operation detected", { operation: "expensiveOperation", duration, threshold: 1000 }); }
Checklist:
- Structured logging (JSON format, not string concat)
- No sensitive data in logs (passwords, tokens, PII)
- Appropriate log levels (ERROR, WARN, INFO, DEBUG)
- Correlation IDs included for request tracing
- Performance logging for operations >1s
- Contextual information (user ID, action, resource)
Step 5: Testing Validation
Purpose: Ensure adequate test coverage and quality
Validation Checks:
-
Unit Tests for Business Logic
// ❌ BAD: No tests export function calculateDiscount(price: number, discountPercent: number) { return price * (discountPercent / 100); } // ✅ GOOD: Unit tests cover logic describe('calculateDiscount', () => { it('should calculate 10% discount correctly', () => { expect(calculateDiscount(100, 10)).toBe(10); }); it('should handle 0% discount', () => { expect(calculateDiscount(100, 0)).toBe(0); }); it('should handle 100% discount', () => { expect(calculateDiscount(100, 100)).toBe(100); }); }); -
Integration Tests for Service Interactions
// ❌ BAD: Only unit tests, no integration tests // ✅ GOOD: Integration test for API interaction describe('UserService', () => { it('should fetch user from API', async () => { const userService = new UserService(mockApiClient); const user = await userService.getUser('user123'); expect(user).toBeDefined(); expect(user.id).toBe('user123'); expect(mockApiClient.get).toHaveBeenCalledWith('/users/user123'); }); }); -
Test Coverage Meets Threshold (>80%)
# Run test coverage npm run test:coverage # Ensure meets threshold # Lines: 85% (target: 80%) # Branches: 82% (target: 80%) # Functions: 90% (target: 80%) -
Edge Cases Covered
// ❌ BAD: Only happy path tested it('should process valid input', () => { expect(processInput('valid')).toBe('processed'); }); // ✅ GOOD: Edge cases tested describe('processInput', () => { it('should process valid input', () => { expect(processInput('valid')).toBe('processed'); }); it('should handle empty string', () => { expect(() => processInput('')).toThrow('Input cannot be empty'); }); it('should handle null', () => { expect(() => processInput(null)).toThrow('Input cannot be null'); }); it('should handle very long input', () => { const longInput = 'a'.repeat(10000); expect(() => processInput(longInput)).toThrow('Input too long'); }); }); -
Mocking Used Appropriately
// ❌ BAD: Real API calls in tests it('should create user', async () => { const result = await realApi.createUser(userData); // 🚨 Real API call expect(result.success).toBe(true); }); // ✅ GOOD: Mock API calls it('should create user', async () => { const mockApi = { createUser: jest.fn().mockResolvedValue({ success: true, id: '123' }) }; const service = new UserService(mockApi); const result = await service.createUser(userData); expect(result.success).toBe(true); expect(mockApi.createUser).toHaveBeenCalledWith(userData); });
Checklist:
- Unit tests for all business logic functions
- Integration tests for service interactions
- Test coverage >80% (lines, branches, functions)
- Edge cases covered (null, empty, invalid, boundary)
- Mocking used for external dependencies
- Tests run in CI/CD pipeline
Step 6: Security Validation
Purpose: Ensure security best practices followed
Validation Checks:
-
Input Validation on All User Inputs
// ❌ BAD: No input validation app.post('/users', async (req, res) => { const user = await db.createUser(req.body); res.json(user); }); // ✅ GOOD: Validate input app.post('/users', async (req, res) => { const schema = z.object({ email: z.string().email(), age: z.number().min(0).max(150), name: z.string().min(1).max(100) }); const validated = schema.parse(req.body); const user = await db.createUser(validated); res.json(user); }); -
Authentication Checks on Protected Operations
// ❌ BAD: No auth check app.get('/admin/users', async (req, res) => { const users = await db.getAllUsers(); res.json(users); }); // ✅ GOOD: Require authentication app.get('/admin/users', requireAuth, async (req, res) => { const users = await db.getAllUsers(); res.json(users); }); -
Authorization/RBAC Enforced
// ❌ BAD: No permission check app.delete('/users/:id', requireAuth, async (req, res) => { await db.deleteUser(req.params.id); res.json({ success: true }); }); // ✅ GOOD: Check permissions app.delete('/users/:id', requireAuth, requireRole('admin'), async (req, res) => { await db.deleteUser(req.params.id); res.json({ success: true }); }); -
SQL Injection Prevention (Parameterized Queries)
// ❌ BAD: String concatenation (SQL injection) const query = `SELECT * FROM users WHERE email = '${email}'`; const users = await db.query(query); // ✅ GOOD: Parameterized query const query = `SELECT * FROM users WHERE email = ?`; const users = await db.query(query, [email]); -
XSS Prevention (Output Encoding)
// ❌ BAD: Unescaped user input res.send(`<h1>Welcome ${req.query.name}</h1>`); // ✅ GOOD: Escape user input const escape = require('escape-html'); res.send(`<h1>Welcome ${escape(req.query.name)}</h1>`);
Checklist:
- Input validation on all user inputs (schema validation)
- Authentication required for protected endpoints
- Authorization/RBAC checks enforced
- Parameterized queries (no SQL injection)
- Output encoding (no XSS vulnerabilities)
- HTTPS enforced (no sensitive data over HTTP)
- CSRF protection enabled
- Rate limiting on public endpoints
Available Resources
Scripts
-
scripts/validate_no_hardcoded_values.py — Scan for hardcoded URLs, keys, magic numbers
python scripts/validate_no_hardcoded_values.py --path src/ --report hardcoding-report.json -
scripts/check_error_handling.py — Verify try-catch coverage for risky operations
python scripts/check_error_handling.py --path src/ --threshold 90 -
scripts/verify_configuration.py — Ensure all config externalized
python scripts/verify_configuration.py --path src/ --config-file .env.example -
scripts/check_logging_practices.py — Validate logging standards
python scripts/check_logging_practices.py --path src/ --check-sensitive-data -
scripts/calculate_test_coverage.py — Check test coverage meets threshold
python scripts/calculate_test_coverage.py --threshold 80 --report coverage-report.json
References
- references/anti-hallucination-checklist.md — Specific checks to prevent AI hallucination
- references/configuration-patterns.md — How to externalize configuration properly
- references/error-handling-patterns.md — Standard error handling approaches
- references/logging-standards.md — Structured logging formats and standards
- references/testing-requirements.md — Unit/integration test requirements
- references/security-checklist.md — Security validation checklist
Integration with Workflow
During Feature Design (/design-features)
Before designing features:
- Review anti-hallucination checklist
- Plan configuration externalization
- Design error handling strategy
- Plan logging approach
- Define test strategy
- Identify security requirements
During Wave Design (/design-waves)
Before designing waves:
- Validate wave follows best practices
- Check for hardcoding opportunities
- Plan error handling for wave
- Define tests needed for wave
During Implementation (/implement-waves)
Before implementing:
- Invoke
development-best-practicesskill - Review relevant checklist sections
- Run validation scripts during implementation
- Test against best practices before commit
During Code Review
Before merging:
- Run all validation scripts
- Review against best practices checklist
- Verify test coverage meets threshold
- Check security checklist
Success Criteria
- ✅ No hallucinated code (all APIs/files verified to exist)
- ✅ No hardcoded values (URLs, credentials, magic numbers)
- ✅ All risky operations have error handling
- ✅ Structured logging with appropriate levels
- ✅ Test coverage >80%
- ✅ Security checklist items addressed
Tips for Effective Practice
- Invoke early and often - Use skill before design, during implementation, before commit
- Run validation scripts - Catch violations automatically
- Review checklists - Systematic approach beats ad-hoc checks
- Learn from violations - Understand why rules exist
- Automate enforcement - Use pre-commit hooks, CI/CD checks
- Update skill - Add patterns as you discover them
- Share violations - Help team learn from mistakes
Last Updated: 2025-01-30