Feature Flags
Control feature availability across tenants and environments.
Overview
Feature flags let you:
- Enable features for specific tenants
- Roll out features gradually
- Toggle features without code deployment
- A/B test different implementations
Environment-Based Flags
Use NEXT_PUBLIC_* environment variables for client-side feature flags:
# .env.local
NEXT_PUBLIC_ENABLE_CHAT=true
NEXT_PUBLIC_ENABLE_AI=false
NEXT_PUBLIC_ENABLE_ANALYTICS=true
Using Environment Flags
// Check flag in component
function ChatWidget() {
if (process.env.NEXT_PUBLIC_ENABLE_CHAT !== 'true') {
return null;
}
return <ChatInterface />;
}
Server-Side Flags
For server-only features, use non-public variables:
# .env.local
ENABLE_FEATURE_X=true # Server only
// In API route or server component
if (process.env.ENABLE_FEATURE_X === 'true') {
// Feature enabled
}
Tenant-Based Flags
Add feature flags to tenant configuration:
export const myTenantConfig: EAIConfig = {
tenantId: 'my-tenant',
displayName: 'My Application',
features: {
chat: true,
aiAssistant: false,
analytics: true,
experimentalFeatures: false,
},
// ... rest of config
};
Using Tenant Flags
import { useConfig } from '@enterpriseaigroup/client';
function FeatureGate({ feature, children }) {
const config = useConfig();
if (!config.features?.[feature]) {
return null;
}
return children;
}
// Usage
<FeatureGate feature="chat">
<ChatWidget />
</FeatureGate>
Component-Level Flags
Use showWhen for conditional component rendering:
// In tenant config
{
component: 'ExperimentalFeature',
priority: 1,
showWhen: (state) => state.features?.experimental === true,
}
Flag Patterns
Gradual Rollout
Enable for percentage of users:
const enableForPercentage = (percentage: number) => {
const userId = getUserId(); // Get stable user identifier
const hash = hashString(userId);
return (hash % 100) < percentage;
};
// Enable for 10% of users
if (enableForPercentage(10)) {
// Show new feature
}
A/B Testing
features: {
checkoutVariant: 'A', // or 'B'
}
// In component
const variant = config.features?.checkoutVariant;
if (variant === 'A') {
return <CheckoutFlowA />;
} else {
return <CheckoutFlowB />;
}
Feature Dependencies
features: {
baseFeature: true,
advancedFeature: true, // Requires baseFeature
}
// Check both flags
const canUseAdvanced =
config.features?.baseFeature &&
config.features?.advancedFeature;
Runtime Configuration
For dynamic flag changes without redeploy, use the runtime config endpoint:
// Server: /api/eai/config returns
{
features: {
maintenance: true, // Dynamically updated
}
}
// Client
import { useRuntimeConfig } from '@enterpriseaigroup/client';
function App() {
const { features } = useRuntimeConfig();
if (features?.maintenance) {
return <MaintenancePage />;
}
return <MainApp />;
}
Best Practices
1. Name Flags Clearly
// Good
features: {
enableNewCheckout: true,
showBetaFeatures: false,
}
// Avoid
features: {
flag1: true,
newThing: false,
}
2. Default to Off
New features should default to disabled:
const isEnabled = config.features?.newFeature ?? false;
3. Clean Up Old Flags
Remove flags once features are stable:
// TODO: Remove after 2024-06-01
features: {
legacyCheckout: false, // Deprecated
}
4. Document Flag Purpose
features: {
// Enable AI-powered search suggestions
// Owner: @search-team
// Rollout: 50% of enterprise tenants
aiSearch: true,
}
5. Test Both States
Always test with flags both enabled and disabled:
describe('ChatWidget', () => {
test('renders when chat enabled', () => {
// Test with flag on
});
test('returns null when chat disabled', () => {
// Test with flag off
});
});
Related Documentation
- Environment Variables - Environment configuration
- Tenant Config Reference - Configuration options