UNPKG

@sleekio/sdk

Version:

Modern A/B Testing SDK for JavaScript Applications - Production-ready with graceful fallbacks

375 lines (299 loc) โ€ข 8.69 kB
# ๐Ÿš€ Sleek SDK Production Guide ## Overview This guide shows you how to safely integrate Sleek SDK into production applications without disrupting user experience, even when the API fails. ## ๐Ÿ”ง Key Changes Made ### 1. **Updated API URL** - Changed from `localhost:3001` to `https://sleek-api.vercel.app` - Production-ready endpoint configuration ### 2. **Enhanced Error Handling** - Graceful fallbacks to 'control' variant when API fails - Fast timeouts (2-3 seconds) to prevent blocking - Silent error handling that doesn't break user experience ### 3. **Production Configuration** - Optimized batching and caching - Privacy-friendly defaults - Minimal logging in production ## ๐Ÿ›ก๏ธ Safe Production Integration ### Basic Setup ```javascript import { createAnonymousSleek } from 'sleek-sdk'; // Production configuration const sleek = createAnonymousSleek('https://sleek-api.vercel.app', { requestTimeout: 2000, // Fast timeout retryAttempts: 1, // Single retry gracefulFallback: true, // Always fallback to 'control' enableLogging: false, // No console logs batchEvents: true, // Efficient event batching batchSize: 25, flushInterval: 15000 // 15 seconds }); ``` ### Safe Variant Assignment ```javascript async function getExperimentVariant(experimentId) { try { // Add timeout protection const variant = await Promise.race([ sleek.getVariant(experimentId), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 1000) ) ]); return variant; } catch (error) { console.warn('Experiment failed, using control:', error.message); return 'control'; // Safe fallback } } ``` ### React Integration ```jsx import { useState, useEffect } from 'react'; function useExperiment(experimentId, defaultVariant = 'control') { const [variant, setVariant] = useState(defaultVariant); const [loading, setLoading] = useState(true); useEffect(() => { let mounted = true; async function loadExperiment() { try { const result = await sleek.getVariant(experimentId); if (mounted) setVariant(result); } catch (error) { if (mounted) setVariant(defaultVariant); } finally { if (mounted) setLoading(false); } } loadExperiment(); return () => { mounted = false; }; }, [experimentId, defaultVariant]); return { variant, loading }; } // Usage in component function ProductPage() { const { variant, loading } = useExperiment('product_layout_test'); if (loading) { return <div>Loading...</div>; } return ( <div className={`layout-${variant}`}> {/* Your content */} </div> ); } ``` ## ๐ŸŽฏ Real-World Usage Patterns ### 1. E-commerce Product Page ```javascript class ProductPageExperiment { constructor() { this.sleek = createAnonymousSleek('https://sleek-api.vercel.app', { requestTimeout: 1500, gracefulFallback: true }); this.experimentId = 'product_page_layout_v2'; } async initialize() { try { const variant = await this.sleek.getVariant(this.experimentId); this.applyLayout(variant); this.trackPageView(); } catch (error) { this.applyLayout('control'); // Safe fallback } } applyLayout(variant) { switch (variant) { case 'new_layout': document.body.classList.add('new-product-layout'); break; case 'minimal': document.body.classList.add('minimal-layout'); break; default: document.body.classList.add('default-layout'); } } trackPurchase(orderValue) { // Safe tracking - won't throw errors this.sleek.trackConversion(this.experimentId, { value: orderValue, currency: 'USD' }); } } ``` ### 2. Feature Flags ```javascript class FeatureManager { constructor() { this.sleek = createAnonymousSleek('https://sleek-api.vercel.app'); } async isFeatureEnabled(featureName, defaultValue = false) { try { const variant = await this.sleek.getVariant(`feature_${featureName}`); return variant === 'enabled'; } catch (error) { return defaultValue; // Safe fallback } } } // Usage const features = new FeatureManager(); if (await features.isFeatureEnabled('new_checkout', false)) { // Show new checkout flow } else { // Show existing checkout flow } ``` ### 3. Gradual Rollouts ```javascript async function shouldShowNewFeature() { try { const variant = await sleek.getVariant('new_feature_rollout'); // Gradual rollout: 10% -> 'enabled', 90% -> 'control' return variant === 'enabled'; } catch (error) { return false; // Conservative fallback } } ``` ## ๐Ÿ”’ Error Handling Best Practices ### 1. **Never Let Experiments Break Your App** ```javascript // โŒ Bad: Can break user experience const variant = await sleek.getVariant('experiment_id'); if (variant === 'new_design') { // This might fail if API is down } // โœ… Good: Always has fallback const variant = await sleek.getVariant('experiment_id').catch(() => 'control'); switch (variant) { case 'new_design': // Apply new design break; default: // Apply default design (always works) } ``` ### 2. **Use Timeouts** ```javascript // Add timeout protection const variant = await Promise.race([ sleek.getVariant('experiment_id'), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 1000) ) ]).catch(() => 'control'); ``` ### 3. **Silent Tracking** ```javascript // โŒ Bad: Can throw errors sleek.track('experiment_id', 'button_click'); // โœ… Good: Silent failure try { sleek.track('experiment_id', 'button_click'); } catch (error) { // Log error but don't break user flow console.warn('Tracking failed:', error.message); } ``` ## ๐Ÿ“Š Performance Optimization ### 1. **Caching** ```javascript // SDK automatically caches assignments const variant1 = await sleek.getVariant('experiment_id'); // API call const variant2 = await sleek.getVariant('experiment_id'); // Cached ``` ### 2. **Batching Events** ```javascript // Events are automatically batched sleek.track('exp1', 'click'); sleek.track('exp2', 'view'); sleek.track('exp3', 'conversion'); // All sent together after 15 seconds or 25 events ``` ### 3. **Preloading** ```javascript // Preload critical experiments async function preloadExperiments() { const criticalExperiments = [ 'homepage_hero', 'checkout_flow', 'pricing_display' ]; await Promise.allSettled( criticalExperiments.map(id => sleek.getVariant(id)) ); } ``` ## ๐Ÿงช Testing Your Integration ### 1. **Test with API Down** ```javascript // Simulate API failure const mockSleek = { getVariant: () => Promise.reject(new Error('API Down')), track: () => Promise.reject(new Error('API Down')) }; // Your code should still work ``` ### 2. **Test Slow API** ```javascript // Simulate slow API const slowSleek = { getVariant: () => new Promise(resolve => setTimeout(() => resolve('control'), 5000) ) }; // Should timeout and fallback quickly ``` ## ๐Ÿš€ Deployment Checklist - [ ] API URL updated to production endpoint - [ ] Timeouts set to 2-3 seconds max - [ ] Graceful fallbacks implemented - [ ] Error logging disabled in production - [ ] Event batching enabled - [ ] Cleanup handlers added - [ ] Tested with API failures - [ ] Tested with slow network - [ ] Performance monitoring added ## ๐Ÿ“ˆ Monitoring ### Track SDK Health ```javascript // Monitor SDK performance let apiSuccessRate = 0; let totalRequests = 0; const originalGetVariant = sleek.getVariant; sleek.getVariant = async function(experimentId) { totalRequests++; try { const result = await originalGetVariant.call(this, experimentId); if (result !== 'control') apiSuccessRate++; return result; } catch (error) { // Track failures analytics.track('sleek_sdk_error', { experiment_id: experimentId, error: error.message }); throw error; } }; ``` ## ๐Ÿ”ง Common Issues & Solutions ### Issue: Slow Page Load **Solution:** Reduce timeout to 1-2 seconds ### Issue: Too Many API Calls **Solution:** Enable caching and batching ### Issue: Experiments Breaking App **Solution:** Add proper error boundaries ### Issue: Inconsistent Results **Solution:** Check user ID persistence ## ๐Ÿ“ž Support If you encounter issues: 1. Check browser console for errors 2. Verify API endpoint is accessible 3. Test with mock data 4. Check network timeouts 5. Verify experiment IDs exist Remember: **The SDK should never break your application.** If it does, you need better error handling!