0

Feature flag — toggling features without code deployment

Intermediate5 min read·eng-18-010
interview

Concept

Feature flag (feature toggle) — a configuration switch that enables or disables a feature in production without deploying new code. Turn features on or off for all users, or selectively for specific users, groups, or percentages of traffic.

What feature flags enable:

  • Dark launches: Deploy code to production but hide the feature. Enable for internal users first, then roll out.
  • Gradual rollouts: Enable for 1% of users, monitor, then 10%, 50%, 100%.
  • A/B testing: Enable feature variant A for 50% of users, variant B for 50%.
  • Kill switch: Instantly disable a feature if it causes problems (no deploy needed).
  • Beta access: Enable features for specific users or groups (beta testers, premium subscribers).

Trunk-based development: Feature flags allow developers to merge code to main daily (no long-lived feature branches) — the unfinished feature is just hidden behind a flag.

Types:

  • Boolean: Feature is on or off.
  • Percentage-based: On for X% of users.
  • User-targeting: On for specific user IDs, emails, or roles.
  • Scheduled: On between dates (e.g., a sale feature).

Tools: Laravel Pennant (official), GrowthBook, LaunchDarkly, Unleash, Flipper (Ruby, but popular reference).

Cleanup: Feature flags accumulate as technical debt. Once a feature is fully rolled out and stable, remove the flag and the old code path.

Code Example

php
<?php
// LARAVEL PENNANT — official feature flag package
// Install: composer require laravel/pennant

// Define features in AppServiceProvider::boot()
use Laravel\Pennant\Feature;

Feature::define('new-checkout', fn (User $user) => $user->isInBetaProgram());

Feature::define('ai-recommendations', function (User $user) {
    // Gradual rollout: 10% of users
    return $user->id % 10 === 0;
});

Feature::define('dark-mode', true); // enabled for everyone

// CHECK feature flags in controllers/views
class CheckoutController extends Controller
{
    public function show(Request $request): View
    {
        if (Feature::active('new-checkout')) {
            return view('checkout.v2');
        }
        return view('checkout.v1');
    }
}

// In Blade views
@feature('new-checkout')
    <x-checkout-v2 />
@else
    <x-checkout-v1 />
@endfeature

// In API responses
return response()->json([
    'features' => [
        'new_checkout'     => Feature::active('new-checkout'),
        'ai_recs'          => Feature::active('ai-recommendations'),
        'dark_mode'        => Feature::active('dark-mode'),
    ],
]);

// SEGMENT-BASED targeting
Feature::define('premium-analytics', function (User $user) {
    return $user->subscription?->plan === 'premium';
});

// PERCENTAGE ROLLOUT
Feature::define('new-editor', function (User $user) {
    return Feature::lottery(0.25); // 25% of users
});

// STORAGE — flags stored in DB (laravel_pennant table) or cache
// php artisan pennant:install
// php artisan migrate

// Enabling for specific users via artisan or admin panel:
Feature::activateForEveryone('dark-mode');
Feature::activate('new-checkout', $specificUser);
Feature::deactivate('new-checkout', $specificUser);