Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Using JSON for Feature Flags and Toggles
Feature flags (or feature toggles) are a powerful technique that enables developers to modify system behavior without changing code. By using JSON for feature flags, you gain flexible configuration that can be updated independently of code deployments. This article explains how to implement feature flags with JSON and best practices for managing them effectively.
What Are Feature Flags?
Feature flags are boolean values or parameters that determine whether a particular feature is enabled or disabled for specific users or environments. They act as conditional switches that control program flow without requiring code changes or redeployments.
Common Use Cases for Feature Flags:
Use Case | Description | Benefit |
---|---|---|
Progressive Rollouts | Release features to a small percentage of users first | Reduces risk of widespread issues |
A/B Testing | Show different versions to different user groups | Data-driven decision making |
Canary Releases | Enable features for specific test environments | Early detection of problems |
Instant Rollbacks | Disable problematic features without deployment | Faster incident response |
User Entitlements | Enable features based on subscription level | Simplified product tier management |
JSON Feature Flag Structures
1. Simple Boolean Flags
The most basic approach is a simple key-value pair where the key is the feature name and the value is a boolean.
Example JSON:
{ "newHomepage": true, "darkMode": false, "premiumFeatures": true, "betaAnalytics": false }
Implementation Code:
// JavaScript example if (featureFlags.newHomepage) { // Show the new homepage } else { // Show the old homepage }
When to Use:
This simple approach works well for smaller applications or when features are either completely on or off across your entire user base.
2. Environment-Specific Flags
For applications that run in multiple environments, you might want different flag values for each environment.
Example JSON:
{ "environments": { "development": { "newHomepage": true, "darkMode": true, "premiumFeatures": true }, "staging": { "newHomepage": true, "darkMode": false, "premiumFeatures": true }, "production": { "newHomepage": false, "darkMode": false, "premiumFeatures": true } } }
Implementation Code:
// JavaScript example const currentEnv = process.env.NODE_ENV || 'development'; const envFlags = featureFlags.environments[currentEnv] || {}; if (envFlags.newHomepage) { // Show the new homepage }
3. User-Targeted Flags
For more sophisticated feature targeting, you can define rules for specific user segments or IDs.
Example JSON:
{ "features": { "newCheckout": { "enabled": true, "enabledFor": { "userIds": ["123", "456", "789"], "userGroups": ["beta-testers", "employees"], "percentageRollout": 25 } }, "recommendationEngine": { "enabled": false, "enabledFor": { "userIds": [], "userGroups": ["premium-subscribers"], "percentageRollout": 0 } } } }
Implementation Code:
// JavaScript example function isFeatureEnabledForUser(featureName, user) { const feature = featureFlags.features[featureName]; // Feature is globally disabled if (!feature || !feature.enabled) { return false; } const { enabledFor } = feature; // Check user ID if (enabledFor.userIds.includes(user.id)) { return true; } // Check user groups if (user.groups && user.groups.some(group => enabledFor.userGroups.includes(group))) { return true; } // Check percentage rollout if (enabledFor.percentageRollout > 0) { // Generate a consistent hash based on user ID const hash = hashFunction(user.id); const normalizedHash = hash % 100; return normalizedHash < enabledFor.percentageRollout; } return false; }
Pro Tip:
For percentage-based rollouts, ensure you use a consistent hashing mechanism based on a user identifier. This guarantees the same users always get the same experience, rather than randomly changing on each page load.
4. Feature Flags with Configuration Values
Sometimes you need more than just an on/off switch - you might need configurable values for features.
Example JSON:
{ "features": { "searchResults": { "enabled": true, "config": { "maxResults": 50, "cacheTimeInMinutes": 15, "sortOrder": "relevance" } }, "recommendations": { "enabled": true, "config": { "algorithm": "collaborative-filtering", "maxItems": 5, "minConfidenceScore": 0.8 } } } }
Implementation Code:
// JavaScript example function getSearchResults(query) { const searchFeature = featureFlags.features.searchResults; if (!searchFeature.enabled) { return getDefaultSearchResults(query); } const { maxResults, sortOrder } = searchFeature.config; return performSearch(query, maxResults, sortOrder); }
Loading JSON Feature Flags
1. Server-Side Loading
Loading feature flags on the server is typically more secure and prevents exposing sensitive flag configurations to clients.
Example with Node.js:
// Node.js example import fs from 'fs'; import path from 'path'; function loadFeatureFlags() { try { const filePath = path.join(process.cwd(), 'config', 'feature-flags.json'); const fileContent = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(fileContent); } catch (error) { console.error('Error loading feature flags:', error); return { features: {} }; // Default fallback } } const featureFlags = loadFeatureFlags(); // In Express.js application app.get('/api/feature-flags', (req, res) => { // Only return client-safe flags, filtering out internal ones const clientFlags = filterFlagsForClient(featureFlags, req.user); res.json(clientFlags); });
2. Client-Side Loading
For client-side applications, you can fetch flags via an API or bundle them with your application.
Example with React:
// React example with hooks import { useState, useEffect, createContext, useContext } from 'react'; const FeatureFlagContext = createContext({}); export function FeatureFlagProvider({ children }) { const [flags, setFlags] = useState({}); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchFlags() { try { const response = await fetch('/api/feature-flags'); const data = await response.json(); setFlags(data); } catch (error) { console.error('Failed to fetch feature flags:', error); } finally { setLoading(false); } } fetchFlags(); }, []); return ( <FeatureFlagContext.Provider value={{ flags, loading }}> {children} </FeatureFlagContext.Provider> ); } export function useFeatureFlag(flagName) { const { flags, loading } = useContext(FeatureFlagContext); return { enabled: flags[flagName] === true, loading }; } // Usage in a component function NewFeature() { const { enabled, loading } = useFeatureFlag('newCheckout'); if (loading) return <div>Loading...</div>; if (!enabled) return null; return <div>New checkout experience!</div>; }
Managing JSON Flag Updates
1. Static File Approach
The simplest approach is to store flags in a static JSON file that gets updated with deployments.
Pros and Cons:
Pros | Cons |
---|---|
Simple to implement | Requires code deployment to update flags |
No external dependencies | Not suitable for frequent flag changes |
Version controlled with code | Difficult to coordinate across team members |
2. Database-Driven Approach
For more dynamic control, store flags in a database and provide an admin interface to update them.
Implementation Approach:
- Store feature flags in a database table
- Create an admin UI for updating flags
- Implement caching to avoid frequent database queries
- Add an API endpoint to fetch current flag states
- Consider adding audit logging for flag changes
3. External Configuration Service
Dedicated configuration services offer more sophisticated flag management capabilities.
Implementation Options:
- Use cloud configuration services like AWS AppConfig or Azure App Configuration
- Implement a Redis or etcd-based configuration service
- Develop a centralized configuration microservice in your architecture
- Consider commercial feature flag platforms for larger applications
Best Practices for JSON Feature Flags
- Include default values and fallbacks to ensure your application works even if flag loading fails
- Keep feature flag logic separate from your business logic to maintain clean code
- Document your feature flags with descriptions and intended behavior
- Clean up old feature flags once they're permanently enabled or removed
- Implement a caching strategy to avoid loading flags too frequently
- Consider security implications when exposing flags to client-side code
- Add monitoring and analytics to track feature flag usage and impact
Example Feature Flag Documentation:
{ "features": { "newCheckout": { "description": "New streamlined checkout flow with fewer steps", "owner": "Checkout Team", "addedOn": "2023-04-15", "status": "beta", // (active, beta, deprecated) "enabled": true, "enabledFor": { "userIds": ["123", "456", "789"], "userGroups": ["beta-testers", "employees"], "percentageRollout": 25 } } } }
Common Challenges and Solutions
Quick Reference: Challenges and Solutions
Challenge | Solution |
---|---|
JSON parsing errors | Implement try/catch blocks and provide fallback values |
Flag configuration gets too complex | Break down into smaller, more manageable flags |
Flag dependencies (one flag depends on another) | Create explicit dependency documentation or combine related flags |
Too many conditional statements in code | Use strategy pattern or factory methods to encapsulate variations |
Feature flags becoming permanent | Implement scheduled reviews to clean up old flags |
Inconsistent user experience with percentage rollouts | Use sticky sessions or consistent hashing based on user ID |
Testing with different flag combinations | Create testing flag presets and add flag override capabilities in test environments |
Conclusion
Using JSON for feature flags provides a flexible, readable way to implement runtime configuration in your applications. Whether you choose a simple file-based approach or integrate with sophisticated configuration management systems, feature flags enable controlled rollouts, experimentation, and risk reduction.
As your application grows, consider evolving your feature flag implementation to match your team's needs. Start simple with basic boolean flags and add complexity only when necessary. Remember that the ultimate goal is to increase deployment confidence and flexibility while maintaining a great user experience.
With proper design and management, JSON feature flags can become a powerful tool in your development workflow, enabling you to ship faster while maintaining control over how and when your features are released to users.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool