Feature Deployment Approaches: Exploring Feature Flags and Deployment Strategies

Feature Deployment Approaches: Exploring Feature Flags and Deployment Strategies

Introduction

This document talks about different ways to release new features in software. It explains the importance of using feature flags and looks at different ways to release features in a controlled manner. We'll break down what feature flags are, how they can be used, and how they work together with deployment methods.

Table of Contents

Introducing in Feature Flags

Feature flags, also known as feature toggles give developers the power to control when certain features are turned on or off. Using feature flags, developers can easily manage the release of new features and perform testing.

Implementation Options

Feature flags can be implemented in several ways.

Admin Panel Page with a Table

An admin page with a table helps change how features work. This way, you can easily turn features on or off in real-time, making it simpler to manage them.

Feature nameSubset criteriaIs enabled
New Dashboarduser_ids IN (1,2,3)true
Report pagerole_analytic IN user.rolestrue
Killer featureuser.tariff.type = ’paid’false

.env/config file

Putting feature flags in configuration files. These are often called "Static Flags" because they don't change dynamically.

Runtime Parameters

In some situations, like when using console commands, it's helpful to use parameters that affect how features work in real-time. You don't need to store these settings permanently.

Example:

    bash ./deploy.sh dev

Geo-Distribution Method

Geo-distribution uses different copies of the software with specific versions, controlled by load balancers. This helps slowly introduce new features to different subset of the users.

Decisions for Enabling Features

Front-end Approaches

Enabling features on the front-end entails several techniques:

  • Payload: In cases of JWT token-based authentication.
  • Response Headers: Custom response headers send information about which features are available to the front-end, allowing the user interface to adapt accordingly.
  • GET /api/features Endpoint: A dedicated API endpoint for enabled features provides real-time data to the front-end.
  • Cookies Usage: Storing feature flag data in cookies confers direct client-side control over feature behavior.

Interaction between Backend parts

  • Indirection: Introducing a service that queries feature availability by key centralizes feature status management. For example, using FeatureAwarService.isEnabled('feature_key')
  • Inversion of Decision (aka Dependency Inversion Principle): Low-level modules should be configured by High-level modules.

Avoiding Complexity in Code

  • Behavioral Patterns: Using patterns like the Strategy pattern makes it easier to make changes to how features work with feature flags.
  • Code Separation: Putting the logic for each feature in separate sections makes things less complicated, which makes it easier to manage and change features.
  • Inversion of Decision
Examples

PHP:

// Define the Strategy interface
interface FeatureStrategy {
    public function applyFeatureLogic(): string;
}

// Implement concrete strategies

class FeatureEnabledStrategy implements FeatureStrategy {
    public function applyFeatureLogic(): string {
        return "Feature is enabled! Performing advanced functionality...";
    }
}

class FeatureDisabledStrategy implements FeatureStrategy {
    public function applyFeatureLogic(): string {
        return "Feature is disabled! Performing basic functionality...";
    }
}

// Context class that uses the strategy
class FeatureManager {
    private $featureStrategy;

    public function __construct(FeatureStrategy $featureStrategy) {
        $this->featureStrategy = $featureStrategy;
    }

    public function performFeatureLogic(): string {
        return $this->featureStrategy->applyFeatureLogic();
    }
}

// Simulate fetching the feature flag from a configuration
$isFeatureEnabled = true; // You can change this value to test different behaviors

// Create the appropriate strategy based on the feature flag
if ($isFeatureEnabled) {
    $featureStrategy = new FeatureEnabledStrategy();
} else {
    $featureStrategy = new FeatureDisabledStrategy();
}

// Create the context with the selected strategy
$featureManager = new FeatureManager($featureStrategy);

// Perform the feature logic
$result = $featureManager->performFeatureLogic();

Python:

    # Define the Strategy interface
    class FeatureStrategy:
        def apply_feature_logic(self):
            pass

    # Implement concrete strategies

    class FeatureEnabledStrategy(FeatureStrategy):
        def apply_feature_logic(self):
            return "Feature is enabled! Performing advanced functionality..."

    class FeatureDisabledStrategy(FeatureStrategy):
        def apply_feature_logic(self):
            return "Feature is disabled! Performing basic functionality..."

    # Context class that uses the strategy
    class FeatureManager:
        def __init__(self, feature_strategy):
            self.feature_strategy = feature_strategy

        def perform_feature_logic(self):
            return self.feature_strategy.apply_feature_logic()

    # Simulate fetching the feature flag from a configuration
    is_feature_enabled = True  # You can change this value to test different behaviors

    # Create the appropriate strategy based on the feature flag
    if is_feature_enabled:
        feature_strategy = FeatureEnabledStrategy()
    else:
        feature_strategy = FeatureDisabledStrategy()

    # Create the context with the selected strategy
    feature_manager = FeatureManager(feature_strategy)

    # Perform the feature logic
    result = feature_manager.perform_feature_logic()
    # Define the strategies

    class FeatureStrategy
    def apply_feature_logic
        raise NotImplementedError, 'Subclasses must implement this method'
    end
    end

    class FeatureEnabledStrategy < FeatureStrategy
    def apply_feature_logic
        'Feature is enabled! Performing advanced functionality...'
    end
    end

    class FeatureDisabledStrategy < FeatureStrategy
    def apply_feature_logic
        'Feature is disabled! Performing basic functionality...'
    end
    end

    # Context class that uses the strategy

    class FeatureManager
    def initialize(feature_strategy)
        @feature_strategy = feature_strategy
    end

    def perform_feature_logic
        @feature_strategy.apply_feature_logic
    end
    end

    # Simulate fetching the feature flag from a configuration
    is_feature_enabled = true # You can change this value to test different behaviors

    # Create the appropriate strategy based on the feature flag
    feature_strategy = if is_feature_enabled
                        FeatureEnabledStrategy.new
                    else
                        FeatureDisabledStrategy.new
                    end

    # Create the context with the selected strategy
    feature_manager = FeatureManager.new(feature_strategy)

    # Perform the feature logic
    result = feature_manager.perform_feature_logic

When to Use Feature Flags

  • Testing New Functionality: Gather real-world feedback by rolling out features to select users.
  • Handling Uncertainty: Evaluate behavior under varying loads, for new features or architectural changes.
  • A/B Testing: Compare performance and user responses across feature variations.
  • Real Environment Testing: Test new functionality with genuine data and interactions.
  • Parallel Development: Enable independent development while maintaining code cohesion.
  • Integrations: Manage compatibility when integrating with external products.

When NOT to Use Feature Flags

  • Clear Vision and Requirements: When requirements are well-defined and the management team has a precise vision, feature flags could add unnecessary complexity.
  • Identical Test Environments: If your testing environment mirrors the production environment closely and doesn't require feature subsets, feature flags might be redundant.
  • Simplicity with Separate Instances: If maintaining separate instances for different functionalities suits your needs, feature flags could be avoided.

Pros of Feature Flags

  • Dynamic Activation: Toggle features dynamically without altering core code.
  • Flexibility: Precise feature toggling for different scenarios.
  • Real-World Testing: Test in authentic conditions for early issue detection.
  • Feedback Collection: Gather user feedback through A/B testing.

Cons of Feature Flags

  • Complex Development: Conditional paths and states can complicate coding.
  • Complex Testing: Many flag combinations demand thorough testing.
  • Challenging Onboarding: Newcomers may struggle with flag interactions.
  • Architectural Chaos: Poor management leads to tangled code and issues.
  • Complex Database Migrations: Transitions with flags require meticulous database handling.

Deployment Strategies

Feature Flags vs. Canary Deployment

Canary deployment is a method of delivering new functionality to a specific group of users. On the other hand, feature flags are a means of controlling the activation or deactivation of specific features.

If canary deployment is feasible, then feature flags might not be required.

Blue-Green Deployment

Blue-Green deployment is typically used for zero-downtime, but it can also be used as a way to release new features. Traffic is kept on the blue deployment while the test team checks the green deployment on real data.

**Feature Flags + A/B testing**

Feature Flags can be used as a way to implement A/B testing due to similarities in the technical implementation. However, it is preferable to avoid conflating these two concerns: while Feature Flags are a good way to detect problems and regressions, A/B testing is a way to test a hypothesis using variant implementations.

Conclusion

Feature flags are a handy tool for managing when certain features are turned on or off while developing and testing. They give flexibility, allow testing with real users and environments, and help gather feedback. But, they can also make development and testing more complex and may be tricky for newcomers. It's crucial to think carefully about when and how to use feature flags to avoid causing confusion in the code and other problems.