Skip to content
advanced Phase 111 · Migration

Feature Flags in Magento 2

Using feature toggles for gradual rollout, migration control, and safe deployments

45m
2 problems
Topic Progress 0%

Feature Toggle Types

Types of Feature Flags

Release Toggles

// Control feature visibility during rollout
public function isNewCheckoutEnabled(): bool
{
    return $this->featureFlagManager->isEnabled('new_checkout');
}

// Usage in code
public function getCheckoutAction()
{
    if ($this->isNewCheckoutEnabled()) {
        return $this->actionFactory->create(
            \Vendor\Checkout\Controller\Index\NewIndex::class
        );
    }
    
    return $this->actionFactory->create(
        \Magento\Checkout\Controller\Index\Index::class
    );
}

Experiment Toggles

// A/B testing configuration
public function getHomepageVersion(): string
{
    $userId = $this->customerSession->getCustomerId();
    
    // Deterministic assignment based on user ID
    $hash = crc32((string)$userId);
    $percentage = $hash % 100;
    
    if ($percentage < 50) {
        return 'control';
    }
    return 'variant_a';
}

Ops Toggles

// Emergency kill switches
public function isPaymentGatewayEnabled(): bool
{
    return $this->featureFlagManager->isEnabled('payment_gateway');
}

// Disable broken payment gateway immediately
if ($this->hasPaymentErrors()) {
    $this->configWriter->save('feature_flags/payment_gateway', false);
}

Permission Toggles

// Admin-only features
public function isAdvancedReportingEnabled(): bool
{
    if (!$this->authorization->isAllowed('Vendor_AdvancedReporting::view')) {
        return false;
    }
    
    return $this->featureFlagManager->isEnabled('advanced_reporting');
}

Migration Toggles

// Control migration traffic
public function useNewOrderService(): bool
{
    $rolloutPercentage = (int)$this->config->get('migration/order_rollout');
    
    return rand(1, 100) <= $rolloutPercentage;
}

Flag Implementation

Feature Flag System

Database Storage

CREATE TABLE feature_flags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    flag_key VARCHAR(50) UNIQUE NOT NULL,
    is_enabled BOOLEAN DEFAULT FALSE,
    description TEXT,
    rollout_percentage INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Sample flags
INSERT INTO feature_flags (flag_key, is_enabled, description, rollout_percentage) VALUES
('new_checkout', FALSE, 'New checkout flow', 0),
('product_recommendations', TRUE, 'AI product recommendations', 100),
('migration_orders', FALSE, 'New order service migration', 25);

Service Implementation

namespace Vendor\FeatureFlag\Service;

class FeatureFlagService implements FeatureFlagInterface
{
    public function __construct(
        private ResourceConnection $resource,
        private CacheInterface $cache,
        private ConfigInterface $config
    ) {}
    
    public function isEnabled(string $flagKey): bool
    {
        $cacheKey = "feature_flag_{$flagKey}";
        $cached = $this->cache->load($cacheKey);
        
        if ($cached !== false) {
            return (bool)$cached;
        }
        
        $connection = $this->resource->getConnection();
        $select = $connection->select()
            ->from($this->resource->getTableName('feature_flags'))
            ->where('flag_key = ?', $flagKey);
        
        $result = $connection->fetchRow($select);
        
        if (!$result) {
            return false; // Default to disabled
        }
        
        $isEnabled = (bool)$result['is_enabled'];
        $this->cache->save($isEnabled ? '1' : '0', $cacheKey, [], 60);
        
        return $isEnabled;
    }
    
    public function getRolloutPercentage(string $flagKey): int
    {
        $connection = $this->resource->getConnection();
        $select = $connection->select()
            ->from($this->resource->getTableName('feature_flags'))
            ->where('flag_key = ?', $flagKey);
        
        $result = $connection->fetchRow($select);
        return (int)($result['rollout_percentage'] ?? 0);
    }
}

Configuration File

// app/config/feature-flags.json
{
    "flags": {
        "new_checkout": {
            "enabled": false,
            "description": "New checkout flow",
            "rollout_percentage": 0
        },
        "product_recommendations": {
            "enabled": true,
            "description": "AI product recommendations",
            "rollout_percentage": 100
        }
    }
}

CLI Management

# Enable a flag
php bin/magento feature-flag:enable new_checkout

# Disable a flag
php bin/magento feature-flag:disable new_checkout

# Set rollout percentage
php bin/magento feature-flag:rollout new_checkout 25

# List all flags
php bin/magento feature-flag:list

# Show flag status
php bin/magento feature-flag:status new_checkout

Gradual Rollout

Rollout Strategies

Percentage-Based Rollout

public function shouldUseNewFeature(string $featureKey, int $userId): bool
{
    $rolloutPercentage = $this->flagService->getRolloutPercentage($featureKey);
    
    if ($rolloutPercentage === 0) {
        return false;
    }
    
    if ($rolloutPercentage === 100) {
        return true;
    }
    
    // Deterministic hash-based assignment
    $hash = crc32($featureKey . $userId);
    return ($hash % 100) < $rolloutPercentage;
}

User Segment Rollout

public function isFeatureEnabledForUser(string $featureKey, int $customerId): bool
{
    $customer = $this->customerRepository->getById($customerId);
    
    // Rollout to specific groups
    $groups = $this->flagService->getTargetGroups($featureKey);
    if (!empty($groups) && !in_array($customer->getGroupId(), $groups)) {
        return false;
    }
    
    // Rollout to specific websites
    $websites = $this->flagService->getTargetWebsites($featureKey);
    if (!empty($websites) && !in_array($customer->getWebsiteId(), $websites)) {
        return false;
    }
    
    return $this->shouldUseNewFeature($featureKey, $customerId);
}

Rollout Stages

Stage 1: Internal (0-5%)
- Team members only
- Verify basic functionality
- Monitor for critical errors

Stage 2: Beta (5-25%)
- Selected customers
- Gather feedback
- Performance validation

Stage 3: Gradual (25-75%)
- Increase percentage weekly
- Monitor metrics
- Adjust based on feedback

Stage 4: Full (75-100%)
- Complete rollout
- Remove flag
- Clean up code

Rollback Automation

// Auto-rollback on error threshold
public function monitorAndRollback(string $featureKey): void
{
    $errorRate = $this->monitoring->getErrorRate($featureKey);
    $threshold = $this->flagService->getRollbackThreshold($featureKey);
    
    if ($errorRate > $threshold) {
        $this->flagService->disable($featureKey);
        $this->alertService->send('Feature flag auto-rolled back', [
            'flag' => $featureKey,
            'error_rate' => $errorRate,
            'threshold' => $threshold
        ]);
    }
}

Flag Lifecycle Management

Flag Lifecycle

Creation

// Create new flag
$this->flagService->create([
    'flag_key' => 'new_search_algorithm',
    'is_enabled' => false,
    'description' => 'Elasticsearch 8.x search algorithm',
    'rollout_percentage' => 0,
    'owner' => 'search-team',
    'created_by' => $this->adminSession->getUser()->getId()
]);

Monitoring

// Track flag usage and metrics
public function trackFlagUsage(string $flagKey, string $action): void
{
    $this->statsd->increment("feature_flags.{$flagKey}.{$action}");
    
    $this->logger->info('Feature flag used', [
        'flag' => $flagKey,
        'action' => $action,
        'user_id' => $this->adminSession->getUser()->getId()
    ]);
}

Cleanup

// Remove flag after full rollout
public function removeFlag(string $flagKey): void
{
    // 1. Verify 100% rollout for 2+ weeks
    $rolloutHistory = $this->flagService->getRolloutHistory($flagKey);
    if (!$this->isFullyRolledOut($rolloutHistory)) {
        throw new \Exception('Cannot remove flag not fully rolled out');
    }
    
    // 2. Remove flag references from code
    $this->codeCleanupService->removeFlagReferences($flagKey);
    
    // 3. Remove flag from database
    $this->flagService->delete($flagKey);
    
    // 4. Update documentation
    $this->documentationService->removeFlag($flagKey);
}

Documentation Template

## Feature Flag: new_checkout

**Owner:** Checkout Team
**Created:** 2024-01-15
**Status:** In Progress (75% rollout)
**Target Completion:** 2024-03-01

### Description
New checkout flow with one-page checkout and address validation.

### Rollout Plan
- [x] Internal testing (0-5%)
- [x] Beta customers (5-25%)
- [x] Partial rollout (25-75%)
- [ ] Full rollout (75-100%)
- [ ] Flag removal

### Metrics
- Conversion rate: +2.3%
- Error rate: 0.02%
- Performance: 150ms avg

### Rollback Trigger
Error rate > 1% or conversion drop > 5%

Flag Audit

# Find unused flags
rg 'isEnabled.*old_feature' app/code/ --include='*.php'

# Find flags with low usage
mysql -u root -p magento -e "
    SELECT flag_key, last_used_at 
    FROM feature_flags 
    WHERE last_used_at < DATE_SUB(NOW(), INTERVAL 30 DAY)
"

# Generate flag report
php bin/magento flag:report

Practice Problems

0 / 2 solved
Feature Flag Implementation

Implement a complete feature flag system for gradual checkout rollout with percentage-based control.

Flag Lifecycle Management

Manage the lifecycle of a feature flag from creation to removal after successful rollout.

Quiz

1. What is a release toggle?

Question 1 options

2. What is the recommended rollout order?

Question 2 options

3. When should a feature flag be removed?

Question 3 options

4. What triggers automatic rollback?

Question 4 options

Flashcards

Question

What are the 4 types of feature flags?

Answer

Release toggles, experiment toggles, ops toggles, permission toggles

Question

How does percentage-based rollout work?

Answer

Hash-based assignment ensures consistent user experience within same percentage

Question

When to remove a flag?

Answer

After 2+ weeks at 100% rollout with stable metrics

Question

What is auto-rollback?

Answer

Automatic flag disable when error rate exceeds threshold

Question

What tracks flag usage?

Answer

StatsD metrics and application logging

Revision Notes

Key Takeaways

  • 1. 4 flag types: release, experiment, ops, permission toggles
  • 2. Use hash-based assignment for consistent percentage rollout
  • 3. Rollout stages: Internal (0-5%) → Beta (5-25%) → Gradual (25-75%) → Full (75-100%)
  • 4. Auto-rollback on error rate threshold prevents user impact
  • 5. Remove flags after 2+ weeks stable at 100% to avoid code debt
  • 6. Document flags with owner, rollout plan, and metrics

Interview Tips

  • Explain the different types of feature flags
  • How do you implement percentage-based rollout?
  • What is the lifecycle of a feature flag?
  • How do you prevent feature flag debt?
  • Describe automatic rollback implementation

Cheat Sheet

Feature Flags Cheat Sheet

Types:

  • Release: visibility during rollout
  • Experiment: A/B testing
  • Ops: emergency kill switches
  • Permission: admin-only access

Rollout Stages:

  1. Internal: 0-5%
  2. Beta: 5-25%
  3. Gradual: 25-75%
  4. Full: 75-100%

Lifecycle:

  1. Create (disabled)
  2. Enable (gradual %)
  3. Monitor metrics
  4. Full rollout (100%)
  5. Remove (2+ weeks stable)

Auto-Rollback:
Trigger: error rate > threshold
Action: disable flag immediately