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
python# ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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)
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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%)
bash# 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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
typescript// ❌ 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)
typescript// ❌ 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)
typescript// ❌ 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
bashpython scripts/validate_no_hardcoded_values.py --path src/ --report hardcoding-report.json
- •
scripts/check_error_handling.py — Verify try-catch coverage for risky operations
bashpython scripts/check_error_handling.py --path src/ --threshold 90
- •
scripts/verify_configuration.py — Ensure all config externalized
bashpython scripts/verify_configuration.py --path src/ --config-file .env.example
- •
scripts/check_logging_practices.py — Validate logging standards
bashpython scripts/check_logging_practices.py --path src/ --check-sensitive-data
- •
scripts/calculate_test_coverage.py — Check test coverage meets threshold
bashpython 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