@sleekio/sdk
Version:
Modern A/B Testing SDK for JavaScript Applications - Production-ready with graceful fallbacks
375 lines (299 loc) โข 8.69 kB
Markdown
This guide shows you how to safely integrate Sleek SDK into production applications without disrupting user experience, even when the API fails.
- Changed from `localhost:3001` to `https://sleek-api.vercel.app`
- Production-ready endpoint configuration
- 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
});
```
```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
}
}
```
```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>
);
}
```
```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'
});
}
}
```
```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
}
```
```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
}
}
```
```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)
}
```
```javascript
// Add timeout protection
const variant = await Promise.race([
sleek.getVariant('experiment_id'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 1000)
)
]).catch(() => 'control');
```
```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);
}
```
```javascript
// SDK automatically caches assignments
const variant1 = await sleek.getVariant('experiment_id'); // API call
const variant2 = await sleek.getVariant('experiment_id'); // Cached
```
```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
```
```javascript
// Preload critical experiments
async function preloadExperiments() {
const criticalExperiments = [
'homepage_hero',
'checkout_flow',
'pricing_display'
];
await Promise.allSettled(
criticalExperiments.map(id => sleek.getVariant(id))
);
}
```
```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
```
```javascript
// Simulate slow API
const slowSleek = {
getVariant: () => new Promise(resolve =>
setTimeout(() => resolve('control'), 5000)
)
};
// Should timeout and fallback quickly
```
- [ ] 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
```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;
}
};
```
**Solution:** Reduce timeout to 1-2 seconds
**Solution:** Enable caching and batching
**Solution:** Add proper error boundaries
**Solution:** Check user ID persistence
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!