Software Teams

Feature Flag Systems: Ship Faster with Less Risk

How to implement feature flag systems that enable continuous delivery, gradual rollouts, A/B testing, and instant kill switches.

Decoupling Deployment from Release

Feature flags (also called feature toggles) separate the act of deploying code from the act of releasing functionality to users. You merge incomplete features into main, deploy them to production, and control who sees them through configuration rather than code branches.

This simple concept transforms how teams ship software. Long-lived feature branches disappear. Deployments become routine instead of events. And when something goes wrong, you flip a flag instead of rolling back a deployment.

Types of Feature Flags

Not all flags serve the same purpose. Understanding the types helps you manage them properly.

Release Flags

Control the rollout of new features. Typically short-lived (days to weeks).

if (Feature::active('new-invoice-editor')) {
    return view('invoices.editor-v2', $data);
}

return view('invoices.editor', $data);

Once the feature is fully rolled out and stable, remove the flag and the old code path. Release flags that live for months become technical debt.

Operational Flags

Kill switches for degrading gracefully under load. Typically long-lived.

if (Feature::active('enable-recommendation-engine')) {
    $recommendations = $this->recommendationService->getForUser($user);
} else {
    $recommendations = $this->getFallbackRecommendations();
}

When the recommendation service is overloaded or experiencing issues, disable the flag. Users get fallback content instead of errors.

Experiment Flags

Control A/B tests and gradual rollouts. Tied to analytics.

$variant = Feature::variant('checkout-flow', $user);

return match ($variant) {
    'control' => view('checkout.current', $data),
    'single-page' => view('checkout.single-page', $data),
    'express' => view('checkout.express', $data),
};

Permission Flags

Control access to features based on user attributes. Often long-lived.

if (Feature::active('beta-features', $user)) {
    // Show beta features to users who opted in
}

Implementation Approaches

Configuration-Based (Simple)

Store flags in a config file or database table:

// config/features.php
return [
    'new-invoice-editor' => env('FEATURE_NEW_INVOICE_EDITOR', false),
    'recommendation-engine' => env('FEATURE_RECOMMENDATIONS', true),
];

// Usage
if (config('features.new-invoice-editor')) { ... }

Pros: Zero dependencies, easy to understand. Cons: Requires redeployment (for config files) or cache clearing (for database) to change flags. No targeting or gradual rollout.

Database-Backed with Admin UI

Store flags in a database with a management interface:

class Feature extends Model
{
    protected $casts = [
        'is_active' => 'boolean',
        'rollout_percentage' => 'integer',
        'allowed_users' => 'array',
    ];

    public static function active(string $name, ?User $user = null): bool
    {
        $feature = static::where('name', $name)->first();

        if (! $feature || ! $feature->is_active) {
            return false;
        }

        // User-specific override
        if ($user && in_array($user->id, $feature->allowed_users ?? [])) {
            return true;
        }

        // Percentage rollout
        if ($feature->rollout_percentage < 100) {
            return crc32($name . ':' . ($user?->id ?? 'anonymous')) % 100
                < $feature->rollout_percentage;
        }

        return true;
    }
}

The crc32 hash ensures a consistent experience per user. User 42 either always sees the feature or never sees it at a given rollout percentage, rather than getting a random result on each page load.

Pros: Change flags without deployment. Build your own admin panel. Cons: Need to build the management UI and handle caching yourself.

Laravel Pennant

Laravel's first-party feature flag package:

use Laravel\Pennant\Feature;

// Define a feature
Feature::define('new-invoice-editor', function (User $user) {
    return $user->isInternalTeam()
        || $user->created_at->isAfter('2026-01-15');
});

// Check a feature
if (Feature::active('new-invoice-editor')) {
    // ...
}

Pennant stores resolved feature values per user and provides Blade directives, middleware, and a clean API. It is a good middle ground between a custom solution and a full-featured platform.

Third-Party Platforms

LaunchDarkly, Flagsmith, Unleash, and similar platforms provide:

  • Management dashboards
  • Targeting rules (by user attribute, geography, plan tier)
  • Gradual rollout with monitoring
  • A/B testing with analytics integration
  • Audit logs of flag changes

Worth the cost when: You have many flags, multiple teams, and need sophisticated targeting. Overkill for teams with fewer than 10 active flags.

Gradual Rollout Strategy

A disciplined rollout process for new features:

  1. 0% - Feature deployed but invisible. Verify no errors in logs.
  2. Internal team (5%) - Dogfood the feature internally. Catch obvious issues.
  3. Beta users (10%) - Users who opted into early access. Gather feedback.
  4. 25% - Broader rollout. Monitor error rates and performance metrics.
  5. 50% - Compare metrics between flag-on and flag-off cohorts.
  6. 100% - Full rollout. The feature is now generally available.
  7. Remove the flag and old code path.

At any step, if metrics degrade, roll back to the previous percentage. This is the power of feature flags: instant rollback without a deployment.

Flag Hygiene

Feature flags create technical debt if not managed. Every flag adds a code path that must be maintained and tested.

Practices that keep flags under control:

  • Set expiration dates. When creating a release flag, set a date by which it must be either fully rolled out or removed. Most release flags should not live longer than 4-6 weeks.
  • Track active flags. Maintain a list of all flags, their type, owner, and status. Review monthly.
  • Automate cleanup reminders. Send notifications when flags approach their expiration date.
  • Test both paths. Your test suite should cover both flag-on and flag-off behavior.
  • Limit active flags. If your team has more than 15-20 active flags, some are not being cleaned up.

Feature Flags in Practice

A typical workflow combining feature flags with continuous delivery:

  1. Developer creates a branch from main
  2. Wraps new functionality behind a feature flag
  3. Merges to main (the feature is hidden behind the flag)
  4. Code deploys to production automatically
  5. The team enables the flag for internal testing
  6. QA verifies the feature in production
  7. Gradual rollout begins
  8. Once at 100% and stable, the developer removes the flag and old code in a cleanup PR

No long-lived branches. No "big bang" releases. No deployment anxiety. Just small, safe, incremental delivery.

Let's talk about your software teams needs

Whether you're modernizing your infrastructure, navigating compliance, or building new software - we can help.

Book a 30-min Call