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 CaseDescriptionBenefit
Progressive RolloutsRelease features to a small percentage of users firstReduces risk of widespread issues
A/B TestingShow different versions to different user groupsData-driven decision making
Canary ReleasesEnable features for specific test environmentsEarly detection of problems
Instant RollbacksDisable problematic features without deploymentFaster incident response
User EntitlementsEnable features based on subscription levelSimplified 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:

ProsCons
Simple to implementRequires code deployment to update flags
No external dependenciesNot suitable for frequent flag changes
Version controlled with codeDifficult 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:

  1. Store feature flags in a database table
  2. Create an admin UI for updating flags
  3. Implement caching to avoid frequent database queries
  4. Add an API endpoint to fetch current flag states
  5. 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

  1. Include default values and fallbacks to ensure your application works even if flag loading fails
  2. Keep feature flag logic separate from your business logic to maintain clean code
  3. Document your feature flags with descriptions and intended behavior
  4. Clean up old feature flags once they're permanently enabled or removed
  5. Implement a caching strategy to avoid loading flags too frequently
  6. Consider security implications when exposing flags to client-side code
  7. 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

ChallengeSolution
JSON parsing errorsImplement try/catch blocks and provide fallback values
Flag configuration gets too complexBreak 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 codeUse strategy pattern or factory methods to encapsulate variations
Feature flags becoming permanentImplement scheduled reviews to clean up old flags
Inconsistent user experience with percentage rolloutsUse sticky sessions or consistent hashing based on user ID
Testing with different flag combinationsCreate 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