Skip to content
Professor Synapse creating a magic shield to protect against gremlin attacks on a gate.
Education Code agents

Vibe Coding Principles: Error Handling & Defensive Programming

Professor Synapse
Professor Synapse |

Greetings, guardians of digital stability! Professor Synapse here with the essential protective magic that keeps your systems running smoothly when the unexpected occurs. Today we explore the defensive enchantments that separate robust applications from fragile toys.

When your AI familiar generates code at lightning speed, it's crucial to weave in protective spells that handle errors gracefully, validate inputs rigorously, and fail safely when things go wrong. Think of these principles as the magical shields and safety nets that protect your users and your system's integrity.

Even the most powerful wizard carries protective amulets - your code should be equally prepared for the chaotic forces of real-world usage.

Fail Fast Principle - Immediate Magical Alerts

"Detect and report errors as early as possible"

Fail Fast is like having magical alarm bells that sound the moment something goes wrong, rather than letting problems fester and cause mysterious failures later. Quick failure with clear messages is far better than silent corruption.

❌ Failing Slowly - Hidden Magical Corruption

// BAD: Problems allowed to propagate and cause mysterious failures
function processPayment(paymentData) {
    // No immediate validation - problems hidden
    let amount = paymentData.amount
    let cardNumber = paymentData.cardNumber
    let customerEmail = paymentData.customerEmail
    
    // Negative amounts accepted silently
    if (amount < 0) {
        amount = 0  // Silent correction - hides the real problem
    }
    
    // Invalid card numbers processed
    if (!cardNumber) {
        cardNumber = "0000000000000000"  // Default value masks error
    }
    
    // Malformed emails allowed through
    if (!customerEmail) {
        customerEmail = "no-email@default.com"  // Hiding missing data
    }
    
    try {
        // Payment will fail mysteriously later
        const payment = paymentGateway.charge(amount, cardNumber)
        
        // Email will fail to send, but we'll never know why
        emailService.send(customerEmail, "Payment processed")
        
        return { success: true, paymentId: payment.id }
    } catch (error) {
        // Generic error hiding root cause
        return { success: false, error: "Payment failed" }
    }
}

// Problems:
// - Invalid data silently "corrected" instead of rejected
// - Root cause of failures hidden by default values
// - Debugging becomes nearly impossible
// - Users never learn about their mistakes

The Problem: By silently "fixing" invalid input or providing defaults, the system hides real problems. Users don't learn about their mistakes, and debugging becomes a nightmare when mysterious failures occur later.

✅ Failing Fast - Clear Immediate Feedback

// GOOD: Immediate validation with clear error messages
function processPayment(paymentData) {
    // Validate immediately and fail fast with specific messages
    validatePaymentData(paymentData)  // Throws on first invalid field
    
    try {
        const payment = paymentGateway.charge(
            paymentData.amount, 
            paymentData.cardNumber
        )
        
        emailService.send(
            paymentData.customerEmail, 
            "Payment processed successfully"
        )
        
        return { 
            success: true, 
            paymentId: payment.id,
            message: "Payment processed successfully"
        }
    } catch (error) {
        // Preserve specific error information
        throw new PaymentProcessingError(
            `Payment failed: ${error.message}`,
            error.code,
            paymentData.amount
        )
    }
}

function validatePaymentData(paymentData) {
    const errors = []
    
    // Check each field immediately and collect all errors
    if (!paymentData.amount || paymentData.amount <= 0) {
        errors.push("Amount must be a positive number")
    }
    
    if (!paymentData.cardNumber || !isValidCardNumber(paymentData.cardNumber)) {
        errors.push("Invalid credit card number format")
    }
    
    if (!paymentData.customerEmail || !isValidEmail(paymentData.customerEmail)) {
        errors.push("Valid email address is required")
    }
    
    if (paymentData.amount > 10000) {
        errors.push("Amount exceeds maximum limit of $10,000")
    }
    
    // Fail fast with all validation errors at once
    if (errors.length > 0) {
        throw new ValidationError("Invalid payment data", errors)
    }
}

function isValidCardNumber(cardNumber) {
    // Proper validation logic
    return /^\d{13,19}$/.test(cardNumber.replace(/\s/g, ''))
}

function isValidEmail(email) {
    // Proper email validation
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}

// Custom error types provide specific information
class ValidationError extends Error {
    constructor(message, validationErrors) {
        super(message)
        this.name = 'ValidationError'
        this.validationErrors = validationErrors
    }
}

class PaymentProcessingError extends Error {
    constructor(message, code, amount) {
        super(message)
        this.name = 'PaymentProcessingError'
        this.code = code
        this.amount = amount
    }
}

AI Prompt Example:

"Add fail-fast validation to this function. Check all inputs immediately and throw specific errors with clear messages. Don't silently correct invalid data - make problems visible immediately."

Graceful Degradation - Elegant Magical Backup Plans

"When parts of the system fail, the rest should continue functioning"

Graceful degradation is like having backup magical systems - when the main enchantment fails, secondary spells keep the essential functions running while you repair the primary magic.

❌ Cascading Failures - Total Magical Collapse

// BAD: Single failure brings down entire system
class ECommerceApplication {
    function displayProductPage(productId) {
        // If ANY service fails, entire page fails
        const product = productService.getProduct(productId)  // Might fail
        const recommendations = recommendationService.getRecommendations(productId)  // Might fail
        const reviews = reviewService.getReviews(productId)  // Might fail
        const inventory = inventoryService.getStock(productId)  // Might fail
        const pricing = pricingService.getCurrentPrice(productId)  // Might fail
        
        // All or nothing - if any service is down, page crashes
        return {
            product: product,
            recommendations: recommendations,
            reviews: reviews,
            inventory: inventory,
            pricing: pricing
        }
    }
    
    function processOrder(orderData) {
        // Critical and non-critical operations mixed together
        const payment = paymentService.processPayment(orderData.payment)
        const shipping = shippingService.calculateShipping(orderData)
        const confirmation = emailService.sendConfirmation(orderData.customerEmail)
        const analytics = analyticsService.trackPurchase(orderData)
        const newsletter = newsletterService.subscribe(orderData.customerEmail)
        
        // If newsletter signup fails, entire order fails!
        return {
            success: payment.success && shipping.success && 
                    confirmation.success && analytics.success && newsletter.success
        }
    }
}

// Problems:
// - Non-critical failures (newsletter, analytics) prevent core functionality
// - User sees broken page when recommendations service is down
// - No fallback when individual services are unavailable

The Problem: The system treats all failures equally, allowing non-critical service failures to break core functionality and user experience.

✅ Graceful Degradation - Resilient Magical Systems

// GOOD: Core functionality protected, non-critical features degrade gracefully
class ECommerceApplication {
    function displayProductPage(productId) {
        const pageData = {
            product: null,
            recommendations: [],
            reviews: [],
            inventory: { available: false, message: "Stock information unavailable" },
            pricing: { price: "Price unavailable", currency: "USD" },
            errors: []
        }
        
        try {
            // Core product data is essential - fail if not available
            pageData.product = productService.getProduct(productId)
            if (!pageData.product) {
                throw new Error("Product not found")
            }
        } catch (error) {
            // Core failure - can't display page
            throw new ProductNotFoundError(`Product ${productId} not available`)
        }
        
        // Non-critical features degrade gracefully
        try {
            pageData.recommendations = recommendationService.getRecommendations(productId)
        } catch (error) {
            // Show page without recommendations
            pageData.errors.push("Recommendations temporarily unavailable")
            logger.warn(`Recommendations failed for product ${productId}: ${error.message}`)
        }
        
        try {
            pageData.reviews = reviewService.getReviews(productId)
        } catch (error) {
            // Show page without reviews
            pageData.errors.push("Reviews temporarily unavailable")
            logger.warn(`Reviews failed for product ${productId}: ${error.message}`)
        }
        
        try {
            pageData.inventory = inventoryService.getStock(productId)
        } catch (error) {
            // Show page with default inventory message
            pageData.inventory = { 
                available: false, 
                message: "Stock information temporarily unavailable" 
            }
            logger.warn(`Inventory check failed for product ${productId}: ${error.message}`)
        }
        
        try {
            pageData.pricing = pricingService.getCurrentPrice(productId)
        } catch (error) {
            // Use cached price or default
            pageData.pricing = this.getCachedPriceOrDefault(productId)
            logger.warn(`Pricing failed for product ${productId}: ${error.message}`)
        }
        
        return pageData
    }
    
    function processOrder(orderData) {
        const orderResult = {
            orderId: generateOrderId(),
            success: false,
            criticalErrors: [],
            nonCriticalErrors: []
        }
        
        try {
            // CRITICAL: Payment must succeed
            const payment = paymentService.processPayment(orderData.payment)
            if (!payment.success) {
                orderResult.criticalErrors.push("Payment processing failed")
                return orderResult
            }
            orderResult.paymentId = payment.id
            
            // CRITICAL: Order must be saved
            const savedOrder = orderService.saveOrder(orderData, payment.id)
            orderResult.orderId = savedOrder.id
            orderResult.success = true
            
        } catch (error) {
            orderResult.criticalErrors.push(`Order processing failed: ${error.message}`)
            return orderResult
        }
        
        // NON-CRITICAL: These can fail without affecting order success
        this.performNonCriticalOrderTasks(orderData, orderResult)
        
        return orderResult
    }
    
    function performNonCriticalOrderTasks(orderData, orderResult) {
        // Email confirmation - important but not critical
        try {
            emailService.sendConfirmation(orderData.customerEmail, orderResult.orderId)
        } catch (error) {
            orderResult.nonCriticalErrors.push("Email confirmation failed - check your email later")
            // Queue for retry later
            emailQueue.scheduleRetry(orderData.customerEmail, orderResult.orderId)
        }
        
        // Analytics tracking - nice to have
        try {
            analyticsService.trackPurchase(orderData)
        } catch (error) {
            // Silent failure for analytics - user doesn't need to know
            logger.info(`Analytics tracking failed for order ${orderResult.orderId}`)
        }
        
        // Newsletter signup - completely optional
        try {
            if (orderData.subscribeToNewsletter) {
                newsletterService.subscribe(orderData.customerEmail)
            }
        } catch (error) {
            orderResult.nonCriticalErrors.push("Newsletter signup failed - you can subscribe later")
        }
    }
    
    function getCachedPriceOrDefault(productId) {
        // Try cache first, then reasonable default
        const cachedPrice = priceCache.get(productId)
        if (cachedPrice) {
            return { ...cachedPrice, note: "Price from cache" }
        }
        
        return { 
            price: "Contact for pricing", 
            currency: "USD",
            note: "Current pricing unavailable"
        }
    }
}

AI Prompt Example:

"Refactor this to use graceful degradation. Separate critical operations that must succeed from non-critical features that can fail without breaking core functionality. Add proper error handling and fallback mechanisms."

Synaptic Labs AI education attribution required

Defensive Programming - Paranoid Magical Protection

"Assume the worst and protect against it"

Defensive programming is like being a cautious wizard who double-checks every spell component and prepares for unexpected magical interference. Always assume inputs are malicious, external services will fail, and Murphy's Law is actively working against you.

❌ Trusting Magical Inputs - Naive Assumptions

// BAD: Trusting all inputs and external systems
function calculateShippingCost(order) {
    // Dangerous assumptions about input structure
    const totalWeight = order.items.reduce((sum, item) => {
        return sum + (item.weight * item.quantity)  // What if item is null?
    }, 0)
    
    // No validation of critical data
    const distance = calculateDistance(
        order.fromAddress.coordinates,  // What if fromAddress is missing?
        order.toAddress.coordinates     // What if toAddress is null?
    )
    
    // Trusting external service without checks
    const shippingRates = shippingAPI.getRates(totalWeight, distance)
    
    // No bounds checking
    return shippingRates.standardRate  // What if this is undefined or negative?
}

function calculateDistance(from, to) {
    // Assuming coordinates are always valid
    const lat1 = from.latitude   // Crash if from is null
    const lon1 = from.longitude  // Crash if longitude missing
    const lat2 = to.latitude     // Crash if to is null
    const lon2 = to.longitude    // Crash if longitude missing
    
    // Mathematical calculation without safety checks
    return Math.sqrt(Math.pow(lat2 - lat1, 2) + Math.pow(lon2 - lon1, 2))
}

// Problems:
// - No null/undefined checks
// - No validation of data types
// - No bounds checking on calculations
// - Blind trust in external APIs
// - No fallback for missing data

✅ Defensive Programming - Paranoid Protection

// GOOD: Defensive checks and safe fallbacks
function calculateShippingCost(order) {
    // Defensive validation of input structure
    if (!order || typeof order !== 'object') {
        throw new ValidationError("Order must be a valid object")
    }
    
    if (!Array.isArray(order.items) || order.items.length === 0) {
        throw new ValidationError("Order must contain at least one item")
    }
    
    // Safe calculation with defensive checks
    const totalWeight = calculateTotalWeightSafely(order.items)
    if (totalWeight <= 0) {
        throw new ValidationError("Order weight must be positive")
    }
    
    // Defensive address validation
    const fromCoords = extractCoordinatesSafely(order.fromAddress, "origin")
    const toCoords = extractCoordinatesSafely(order.toAddress, "destination")
    
    const distance = calculateDistanceSafely(fromCoords, toCoords)
    if (distance <= 0) {
        throw new ValidationError("Invalid shipping distance calculated")
    }
    
    // Defensive external API call with timeout and fallback
    try {
        const shippingRates = this.getShippingRatesWithTimeout(totalWeight, distance)
        return this.validateAndReturnRate(shippingRates)
    } catch (error) {
        // Fallback to default rate calculation
        logger.warn(`Shipping API failed: ${error.message}. Using fallback calculation.`)
        return this.calculateFallbackRate(totalWeight, distance)
    }
}

function calculateTotalWeightSafely(items) {
    let totalWeight = 0
    
    for (const item of items) {
        // Defensive checks for each item
        if (!item || typeof item !== 'object') {
            throw new ValidationError("All items must be valid objects")
        }
        
        const weight = parseFloat(item.weight)
        const quantity = parseInt(item.quantity)
        
        // Validate numeric values
        if (!Number.isFinite(weight) || weight < 0) {
            throw new ValidationError(`Invalid weight for item: ${weight}`)
        }
        
        if (!Number.isInteger(quantity) || quantity <= 0) {
            throw new ValidationError(`Invalid quantity for item: ${quantity}`)
        }
        
        // Reasonable bounds checking
        if (weight > 1000) {  // 1000 kg max per item
            throw new ValidationError(`Item weight too high: ${weight}kg`)
        }
        
        if (quantity > 100) {  // 100 items max per line
            throw new ValidationError(`Item quantity too high: ${quantity}`)
        }
        
        totalWeight += weight * quantity
    }
    
    // Final bounds check
    if (totalWeight > 10000) {  // 10,000 kg total max
        throw new ValidationError(`Total order weight too high: ${totalWeight}kg`)
    }
    
    return totalWeight
}

function extractCoordinatesSafely(address, addressType) {
    if (!address || typeof address !== 'object') {
        throw new ValidationError(`${addressType} address is required`)
    }
    
    if (!address.coordinates || typeof address.coordinates !== 'object') {
        throw new ValidationError(`${addressType} address must include coordinates`)
    }
    
    const lat = parseFloat(address.coordinates.latitude)
    const lon = parseFloat(address.coordinates.longitude)
    
    // Validate coordinate ranges
    if (!Number.isFinite(lat) || lat < -90 || lat > 90) {
        throw new ValidationError(`Invalid latitude for ${addressType}: ${lat}`)
    }
    
    if (!Number.isFinite(lon) || lon < -180 || lon > 180) {
        throw new ValidationError(`Invalid longitude for ${addressType}: ${lon}`)
    }
    
    return { latitude: lat, longitude: lon }
}

function calculateDistanceSafely(from, to) {
    try {
        // Defensive distance calculation with bounds checking
        const latDiff = to.latitude - from.latitude
        const lonDiff = to.longitude - from.longitude
        
        // Simple distance calculation (for example purposes)
        const distance = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff) * 111  // Rough km conversion
        
        // Sanity check on result
        if (!Number.isFinite(distance) || distance < 0) {
            throw new Error("Invalid distance calculation result")
        }
        
        if (distance > 20000) {  // Maximum reasonable shipping distance
            throw new ValidationError("Shipping distance too large")
        }
        
        return distance
    } catch (error) {
        throw new ValidationError(`Distance calculation failed: ${error.message}`)
    }
}

AI Prompt Example:

"Add defensive programming to this function. Validate all inputs, check for null/undefined values, add bounds checking, and provide fallbacks for external service failures. Assume all inputs could be malicious or malformed."

Exception Safety - Maintaining Magical Integrity

"Ensure your system remains in a consistent state even when exceptions occur"

Exception safety is like having magical cleanup spells that automatically restore order when other enchantments go wrong, ensuring your magical realm doesn't become corrupted.

❌ Unsafe Exception Handling - Corrupted Magic

// BAD: Operations that leave system in inconsistent state
class BankAccount {
    constructor(balance) {
        this.balance = balance
        this.transactionHistory = []
        this.isLocked = false
    }
    
    function transfer(amount, targetAccount) {
        // No exception safety - partial operations can leave inconsistent state
        
        this.isLocked = true  // Lock account
        
        // If this throws, account stays locked forever!
        this.validateTransfer(amount, targetAccount)
        
        // If this throws, money disappears!
        this.balance -= amount
        
        // If this throws, money is deducted but not added to target!
        targetAccount.deposit(amount)
        
        // If this throws, transaction not recorded!
        this.transactionHistory.push({
            type: 'transfer',
            amount: amount,
            target: targetAccount.accountNumber,
            timestamp: Date.now()
        })
        
        this.isLocked = false  // Unlock account (might never be reached!)
    }
    
    function deposit(amount) {
        // Another unsafe operation
        this.validateAmount(amount)  // Might throw
        
        this.balance += amount  // Money added but not recorded if next line fails
        
        this.transactionHistory.push({  // Might throw, leaving balance wrong
            type: 'deposit',
            amount: amount,
            timestamp: Date.now()
        })
    }
}

// Problems:
// - Account can be left permanently locked
// - Money can disappear without being transferred
// - Transaction history can become inconsistent with balance
// - No rollback mechanism for partial failures

✅ Exception-Safe Operations - Protected Magic

// GOOD: Operations maintain consistency even when exceptions occur
class BankAccount {
    constructor(balance) {
        this.balance = balance
        this.transactionHistory = []
        this.isLocked = false
    }
    
    function transfer(amount, targetAccount) {
        // Exception-safe transfer with proper cleanup
        
        // Validate everything BEFORE making changes
        this.validateTransfer(amount, targetAccount)
        targetAccount.validateDeposit(amount)
        
        // Create transaction record first (rollback if needed)
        const transaction = {
            id: generateTransactionId(),
            type: 'transfer',
            amount: amount,
            from: this.accountNumber,
            to: targetAccount.accountNumber,
            timestamp: Date.now(),
            status: 'pending'
        }
        
        // Use try-finally to ensure cleanup happens
        this.isLocked = true
        targetAccount.isLocked = true
        
        try {
            // Perform atomic operations in correct order
            this.balance -= amount
            
            try {
                targetAccount.balance += amount
                
                // Only record successful transaction
                transaction.status = 'completed'
                this.transactionHistory.push(transaction)
                targetAccount.transactionHistory.push({
                    ...transaction,
                    type: 'deposit'  // From target's perspective
                })
                
            } catch (error) {
                // Rollback balance change if target deposit fails
                this.balance += amount
                transaction.status = 'failed'
                transaction.error = error.message
                this.transactionHistory.push(transaction)
                throw error
            }
            
        } finally {
            // Always unlock accounts, regardless of success or failure
            this.isLocked = false
            targetAccount.isLocked = false
        }
    }
    
    function deposit(amount) {
        // Exception-safe deposit with validation first
        this.validateAmount(amount)
        
        // Prepare transaction record
        const transaction = {
            id: generateTransactionId(),
            type: 'deposit',
            amount: amount,
            timestamp: Date.now(),
            status: 'pending'
        }
        
        try {
            // Make changes atomically
            const oldBalance = this.balance
            this.balance += amount
            
            // Record successful transaction
            transaction.status = 'completed'
            transaction.previousBalance = oldBalance
            transaction.newBalance = this.balance
            this.transactionHistory.push(transaction)
            
        } catch (error) {
            // Record failed transaction for audit trail
            transaction.status = 'failed'
            transaction.error = error.message
            this.transactionHistory.push(transaction)
            throw error
        }
    }
    
    function validateTransfer(amount, targetAccount) {
        if (this.isLocked) {
            throw new AccountError("Account is locked")
        }
        
        if (targetAccount.isLocked) {
            throw new AccountError("Target account is locked")
        }
        
        this.validateAmount(amount)
        
        if (this.balance < amount) {
            throw new InsufficientFundsError("Insufficient balance for transfer")
        }
        
        if (targetAccount === this) {
            throw new AccountError("Cannot transfer to same account")
        }
    }
    
    function validateAmount(amount) {
        if (typeof amount !== 'number' || !Number.isFinite(amount)) {
            throw new ValidationError("Amount must be a valid number")
        }
        
        if (amount <= 0) {
            throw new ValidationError("Amount must be positive")
        }
        
        if (amount > 1000000) {  // Maximum transaction limit
            throw new ValidationError("Amount exceeds maximum limit")
        }
    }
    
    function validateDeposit(amount) {
        if (this.isLocked) {
            throw new AccountError("Account is locked")
        }
        
        this.validateAmount(amount)
        
        // Check if deposit would exceed account limits
        if (this.balance + amount > 10000000) {  // Maximum balance limit
            throw new AccountError("Deposit would exceed maximum balance")
        }
    }
}

// Custom error types for specific handling
class AccountError extends Error {
    constructor(message) {
        super(message)
        this.name = 'AccountError'
    }
}

class InsufficientFundsError extends AccountError {
    constructor(message) {
        super(message)
        this.name = 'InsufficientFundsError'
    }
}

class ValidationError extends Error {
    constructor(message) {
        super(message)
        this.name = 'ValidationError'
    }
}

AI Prompt Example:

"Make this code exception-safe. Ensure that if any operation fails, the system remains in a consistent state. Use try-finally blocks for cleanup and implement proper rollback mechanisms for partial failures."

Input Validation & Security - Magical Border Guards

"Trust nothing from the outside world"

Input validation is like having vigilant magical guards at every entrance to your realm, checking every visitor and package for potential threats before allowing entry.

✅ Comprehensive Input Protection

// GOOD: Thorough input validation and sanitization
class UserRegistrationService {
    function registerUser(registrationData) {
        // Validate and sanitize all input data
        const cleanData = this.validateAndSanitizeInput(registrationData)
        
        // Additional business logic validation
        this.validateBusinessRules(cleanData)
        
        // Create user with validated data
        return this.createUserAccount(cleanData)
    }
    
    function validateAndSanitizeInput(data) {
        const errors = []
        const cleanData = {}
        
        // Email validation and sanitization
        if (!data.email || typeof data.email !== 'string') {
            errors.push("Email is required")
        } else {
            cleanData.email = this.sanitizeEmail(data.email)
            if (!this.isValidEmail(cleanData.email)) {
                errors.push("Invalid email format")
            }
        }
        
        // Password validation
        if (!data.password || typeof data.password !== 'string') {
            errors.push("Password is required")
        } else {
            const passwordErrors = this.validatePassword(data.password)
            errors.push(...passwordErrors)
            cleanData.password = data.password  // Don't sanitize passwords
        }
        
        // Name validation and sanitization
        if (!data.name || typeof data.name !== 'string') {
            errors.push("Name is required")
        } else {
            cleanData.name = this.sanitizeName(data.name)
            if (cleanData.name.length < 2) {
                errors.push("Name must be at least 2 characters")
            }
        }
        
        // Age validation
        if (data.age !== undefined) {
            const age = parseInt(data.age)
            if (!Number.isInteger(age) || age < 13 || age > 120) {
                errors.push("Age must be between 13 and 120")
            } else {
                cleanData.age = age
            }
        }
        
        // Phone validation and sanitization
        if (data.phone) {
            cleanData.phone = this.sanitizePhone(data.phone)
            if (!this.isValidPhone(cleanData.phone)) {
                errors.push("Invalid phone number format")
            }
        }
        
        if (errors.length > 0) {
            throw new ValidationError("Invalid registration data", errors)
        }
        
        return cleanData
    }
    
    function sanitizeEmail(email) {
        return email.trim().toLowerCase()
    }
    
    function sanitizeName(name) {
        // Remove dangerous characters but preserve international names
        return name.trim()
                  .replace(/[<>"'&]/g, '')  // Remove HTML/script chars
                  .replace(/\s+/g, ' ')      // Normalize whitespace
                  .slice(0, 100)             // Limit length
    }
    
    function sanitizePhone(phone) {
        // Extract only digits and common separators
        return phone.replace(/[^\d\-\+\(\)\s]/g, '').trim()
    }
    
    function isValidEmail(email) {
        // Comprehensive email validation
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
        return emailRegex.test(email) && 
               email.length <= 254 &&  // RFC limit
               !email.includes('..') && // No consecutive dots
               !email.startsWith('.') && 
               !email.endsWith('.')
    }
    
    function validatePassword(password) {
        const errors = []
        
        if (password.length < 8) {
            errors.push("Password must be at least 8 characters")
        }
        
        if (password.length > 128) {
            errors.push("Password must be less than 128 characters")
        }
        
        if (!/[A-Z]/.test(password)) {
            errors.push("Password must contain at least one uppercase letter")
        }
        
        if (!/[a-z]/.test(password)) {
            errors.push("Password must contain at least one lowercase letter")
        }
        
        if (!/\d/.test(password)) {
            errors.push("Password must contain at least one number")
        }
        
        if (!/[!@#$%^&*(),.?\":{}|<>]/.test(password)) {
            errors.push("Password must contain at least one special character")
        }
        
        // Check against common passwords
        if (this.isCommonPassword(password)) {
            errors.push("Password is too common, please choose a different one")
        }
        
        return errors
    }
    
    function isValidPhone(phone) {
        // Support international phone formats
        const phoneRegex = /^[\+]?[\d\s\-\(\)]{10,15}$/
        return phoneRegex.test(phone)
    }
    
    function validateBusinessRules(userData) {
        // Check for existing email
        if (this.emailExists(userData.email)) {
            throw new BusinessRuleError("Email address is already registered")
        }
        
        // Rate limiting check
        if (this.hasExceededRegistrationLimit(userData.email)) {
            throw new RateLimitError("Too many registration attempts. Please try again later.")
        }
        
        // Domain restrictions
        if (this.isBlockedEmailDomain(userData.email)) {
            throw new BusinessRuleError("Registration not allowed from this email domain")
        }
    }
    
    function isCommonPassword(password) {
        const commonPasswords = [
            'password', '123456', 'password123', 'admin', 'letmein',
            'welcome', 'monkey', '1234567890', 'qwerty', 'abc123'
        ]
        return commonPasswords.includes(password.toLowerCase())
    }
}

class ValidationError extends Error {
    constructor(message, validationErrors = []) {
        super(message)
        this.name = 'ValidationError'
        this.validationErrors = validationErrors
    }
}

class BusinessRuleError extends Error {
    constructor(message) {
        super(message)
        this.name = 'BusinessRuleError'
    }
}

class RateLimitError extends Error {
    constructor(message) {
        super(message)
        this.name = 'RateLimitError'
    }
}

AI Prompt Example:

"Add comprehensive input validation to this function. Validate data types, sanitize strings, check ranges for numbers, validate formats for emails/phones, and protect against common security issues like injection attacks."

Building Resilient Systems with AI

When working with your AI familiar on error handling:

For Fail Fast:

"Add immediate validation that fails fast with clear error messages. Don't silently correct invalid data - make problems visible immediately."

For Graceful Degradation:

"Separate critical operations from non-critical features. Ensure core functionality continues even when secondary services fail."

For Defensive Programming:

"Add defensive checks for all inputs. Assume everything could be null, malformed, or malicious. Add bounds checking and fallback mechanisms."

For Exception Safety:

"Make this code exception-safe. Use try-finally blocks for cleanup and ensure the system stays consistent even when operations fail."

For Input Validation:

"Add comprehensive input validation and sanitization. Check data types, validate formats, and protect against security vulnerabilities."

The Shield Wall of Robust Code

When you combine these defensive principles, you create systems that are:

  • Predictable: Fail fast with clear error messages
  • Resilient: Continue core functionality when parts fail
  • Safe: Protect against malicious inputs and edge cases
  • Consistent: Maintain system integrity even during failures
  • Secure: Validate and sanitize all external data

Your Next Magical Steps

These error handling principles create the protective foundation for reliable systems. In our next scroll, we'll explore Testing & Quality Assurance Principles - the magical practices that help you verify your protective enchantments work correctly.

Remember: A paranoid wizard is a living wizard. Better to check twice and validate everything than to debug mysterious failures in production.

Until next time, may your errors fail fast and your systems degrade gracefully!


This scroll is part of our Vibe Coding Principles series, exploring how fundamental software principles enhance AI-assisted development.

Share this post