Seasonal Marketing for Booking Businesses: Planning Promotions That Actually Work

13 min read 2,457 words
Seasonal Marketing for Booking Businesses: Planning Promotions That Actually Work featured image

What Feature Flags Are and Why They Change How You Deploy Code

Deploying code to a live website carries risk. Even well-tested changes can behave unexpectedly when they meet real traffic, real users, and real data. A feature that works perfectly in a staging environment may cause problems once it encounters the unpredictable nature of production.

Feature flags offer a way to manage that risk. Rather than treating deployment and release as the same event, you separate them. Code gets deployed to production in an inactive state. The feature exists in the codebase but is turned off for everyone except the specific users, sessions, or percentage of traffic you choose. This gives you control over exactly when and how a feature becomes visible.

The concept applies across many programming environments, but implementing feature flags in PHP is straightforward and suits small teams or solo developers who need reliable deployment practices without complex infrastructure.

How Feature Flags Work in Practice

At its simplest, a feature flag is a conditional check in your code. The flag evaluates whether a feature should be active for a given request. If it is active, the new code path runs. If it is not, the existing code continues unaffected.

This means you can deploy code at any time, even late on a Friday afternoon, without committing to a full release. The deployment happens safely. The release decision happens separately, under your control.

The benefits become clear in several common scenarios. You can enable a new feature for internal users first, watching how it behaves with real data before widening access. You can gradually increase the percentage of users who see a feature, rolling out to ten percent, then fifty, then everyone. You can schedule a feature to activate automatically at a specific date and time, such as a seasonal promotion going live at midnight.

Implementing a Basic Feature Flag System in PHP

You do not need a third-party service to start using feature flags. A simple configuration file works well for small projects and gives you full control over how flags are evaluated.

Storing Flag Configuration

Store your flags in a PHP array or a configuration file that loads at runtime. Keep the structure clean so you can add, modify, or remove flags without touching application logic.

// config/feature_flags.php

return [
    'new_booking_form' => [
        'enabled' => false,
        'rollout_percentage' => 0,
        'allowed_users' => ['admin@example.com'],
        'start_date' => null,
        'end_date' => null,
    ],
    'seasonal_promotion' => [
        'enabled' => true,
        'rollout_percentage' => 100,
        'allowed_users' => [],
        'start_date' => '2024-11-25 00:00:00',
        'end_date' => '2024-12-31 23:59:59',
    ],
];

Building a Flag Evaluation Helper

Create a helper function or class that evaluates whether a flag is active for the current request. This centralises the logic and makes it easy to change how flags are evaluated across your application.

class FeatureFlag
{
    private static $flags = [];

    public static function load(array $config): void
    {
        self::$flags = $config;
    }

    public static function isActive(string $flagName, ?string $userEmail = null): bool
    {
        if (!isset(self::$flags[$flagName])) {
            return false;
        }

        $flag = self::$flags[$flagName];

        if (!$flag['enabled']) {
            return false;
        }

        // Check user allowlist first
        if (!empty($flag['allowed_users']) && $userEmail !== null) {
            if (in_array($userEmail, $flag['allowed_users'], true)) {
                return true;
            }
        }

        // Check rollout percentage
        if ($flag['rollout_percentage'] < 100) {
            $hash = crc32($userEmail ?? session_id());
            $bucket = $hash % 100;
            if ($bucket >= $flag['rollout_percentage']) {
                return false;
            }
        }

        // Check date window
        if ($flag['start_date'] !== null) {
            if (time() < strtotime($flag['start_date'])) {
                return false;
            }
        }

        if ($flag['end_date'] !== null) {
            if (time() > strtotime($flag['end_date'])) {
                return false;
            }
        }

        return true;
    }
}

Load the configuration early in your application bootstrap and use the helper wherever you need to check a flag.

// bootstrap.php

$flags = require __DIR__ . '/../config/feature_flags.php';
FeatureFlag::load($flags);

Using Flags in Your Application Code

Call the flag check from wherever you need to conditionally run code. Keep the checks lightweight and consistent throughout your application.

$userEmail = $_SESSION['user_email'] ?? null;

if (FeatureFlag::isActive('new_booking_form', $userEmail)) {
    include __DIR__ . '/../views/booking/new-form.php';
} else {
    include __DIR__ . '/../views/booking/original-form.php';
}

Notice the use of session_id() as a fallback when no user is logged in. This ensures that anonymous users get consistent flag behaviour across page requests. A visitor either sees the new feature or they do not, and it stays consistent until the session changes.

Safe Deployment Practices With Feature Flags

Having a flag system in place is useful, but how you use it matters. A few practices help you get reliable results from your implementation.

Deploy Often, Release Carefully

Feature flags support a cadence where you deploy small, frequent changes to production rather than batching many changes together. Each deployment is low-risk because no features are activated by default. You control the moment of release separately, watching for problems before widening access.

This approach reduces the pressure around deployments. You do not need to wait for a quiet window to push code. The code is already there, inactive, and safe.

Monitor After Every Activation

When you activate a flag for more users, watch your error logs, response times, and any custom monitoring you have in place. Set a simple checklist before each rollout stage:

  • Check error logs: Look for new warnings or exceptions in the minutes after activation.
  • Check response times: A sudden slowdown often indicates a query problem or resource issue.
  • Check conversion data: For booking systems or checkout flows, watch whether completion rates change.
  • Have a rollback plan: If problems appear, you need to be able to deactivate the flag quickly.

Keep Flags Short-Lived

Flags that stay in code indefinitely become technical debt. Each flag you add is a branch in your application logic that someone needs to understand and maintain. Old flags eventually get forgotten and can cause confusion during future development.

Set a policy for flag lifecycles. A flag should exist only long enough to complete its rollout and verification. Once a feature is active for everyone and stable, remove the flag and the old code path. This keeps your codebase clean and reduces the cognitive load for anyone working on the project later.

Common Mistakes When Using Feature Flags

Feature flags are straightforward in concept but have practical pitfalls that catch teams out.

One common mistake is creating too many flags. When every small variation in behaviour gets its own flag, the configuration becomes hard to manage and the conditional logic spreads throughout the codebase. Use flags for meaningful changes that genuinely need staged rollout, not for every minor decision.

Another mistake is treating flags as permanent configuration. Flags that never get removed accumulate over time until the application is full of conditional paths that are never turned off. This makes the code harder to understand and test. Plan to remove flags as part of your process, not as an afterthought.

A third issue is poor naming. A flag called feature_v2 tells you nothing about what it controls. Name flags after the behaviour they affect, such as new_booking_form or seasonal_discount_2024. Descriptive names make it easier to audit your flags and understand what each one does months later.

Testing Code That Uses Feature Flags

Testing is an important part of any feature flag implementation. You need to verify that both the active and inactive paths work correctly, now and in the future.

Write tests that cover the flag evaluation logic itself. Verify that a disabled flag returns false, that allowed users pass the check, that rollout percentages work as expected, and that date windows restrict access correctly.

public function testDisabledFlagReturnsFalse(): void
{
    FeatureFlag::load(['test_flag' => [
        'enabled' => false,
        'rollout_percentage' => 100,
        'allowed_users' => [],
        'start_date' => null,
        'end_date' => null,
    ]]);

    $this->assertFalse(FeatureFlag::isActive('test_flag'));
}

public function testAllowedUserPassesCheck(): void
{
    FeatureFlag::load(['test_flag' => [
        'enabled' => true,
        'rollout_percentage' => 0,
        'allowed_users' => ['admin@example.com'],
        'start_date' => null,
        'end_date' => null,
    ]]);

    $this->assertTrue(FeatureFlag::isActive('test_flag', 'admin@example.com'));
    $this->assertFalse(FeatureFlag::isActive('test_flag', 'other@example.com'));
}

Beyond testing the flag logic, test the features that depend on flags. Make sure the old code path works correctly when the flag is off, and the new path works when the flag is on. This catches problems where a flag controls a feature that has drifted from its original implementation.

Your test environment should mirror your production flag configuration as closely as possible. Consider having a test configuration file that you use during automated testing, so your tests run against the same flag rules your application uses.

When Feature Flags Add the Most Value

Feature flags are useful in several situations, though they are not always necessary.

If you run a booking website where promotions change seasonally, flags let you schedule changes in advance without manual deployment on the day. You deploy the code once, configure the start and end dates, and the system activates and deactivates the promotion automatically.

If you offer different service tiers or membership levels, flags let you test features with a subset of users before committing to a full release. You can gather feedback from early adopters and adjust before everyone sees the change.

For ongoing PHP development work, flags give you a safety net when making significant changes to existing functionality. You can replace an old booking form with a new one, for example, and keep the old version running for users where the new one causes problems while you investigate.

If your deployment process currently involves tension or anxiety, flags can reduce that by giving you control over the moment of release. Code is deployed safely throughout the day. Features go live when you choose, with the ability to roll back instantly if something goes wrong.

Maintaining Your Feature Flag System Over Time

A flag system needs maintenance like any other part of your application. Without regular attention, flags accumulate and configuration becomes hard to audit.

Keep a register of active flags, their purpose, and when they were created. This register helps you identify flags that have been active long enough to be removed. A quarterly review of flags is a useful practice, checking whether each flag still serves a purpose or whether the old code path can be retired.

Document how flags are used in your IT documentation. When someone new works on the project, they should be able to understand the flag system quickly. Good documentation also helps you remember your own decisions when you return to the project months later.

When you remove a flag, clean up completely. Delete the configuration entry, remove the flag checks from your code, and delete any old code paths that existed only to support the flag. Partial removal causes confusion and can lead to errors when remaining code references deleted configuration.

Alternatives to Consider

Simple configuration files work well for small projects, but as the number of flags grows, you may want a more structured approach.

Third-party feature flag services offer dashboards for managing flags across multiple environments, user targeting based on attributes, and analytics showing how flags are performing. These tools add overhead and cost, so evaluate whether the benefits justify them for your situation. For a single website or small application, a custom implementation is often sufficient.

Environment variables provide another lightweight option. You can store flag states in environment variables and load them at runtime, keeping configuration separate from code. This approach works well when you already use environment variables for other settings.

// Read flag from environment variable
$flags = [
    'new_booking_form' => getenv('FEATURE_NEW_BOOKING_FORM') === '1',
    // ... other flags
];

FeatureFlag::load($flags);

Choose the approach that fits your project scale. Start simple and add complexity only when you have a reason to. A configuration file with a helper function handles most small-to-medium PHP projects without requiring additional infrastructure.

Getting Started With Feature Flags in Your PHP Project

If you manage a PHP website where deployments currently feel risky or where seasonal changes require repeated manual work, feature flags offer a practical way to improve your process. The implementation described here uses basic PHP and a configuration file, which means no additional dependencies and full control over how flags work.

Start with a single flag for a small, reversible change. Build the habit of deploying code inactive, then activating it when you are ready. Once the workflow feels comfortable, add flags for more significant changes and expand the system as your needs grow.

Regular maintenance matters. Review your active flags every few months, remove those that have served their purpose, and keep your configuration clean and documented.

If you need help reviewing your current PHP deployment process or setting up a feature flag system for an existing project, you can get in touch with details of your setup and what you want to improve.

Frequently Asked Questions

Do I need a third-party service to use feature flags in PHP?
No. A simple PHP configuration file and a helper function handle most use cases. Third-party services become useful when you need cross-environment management, advanced user targeting, or analytics across a large number of flags and applications.
Can feature flags replace proper testing?
No. Feature flags are a deployment tool, not a testing strategy. You still need unit tests, integration tests, and staging verification before activating a flag in production. Flags add an extra safety layer, but they do not replace the need for thorough testing.
How many feature flags should I have active at once?
There is no fixed limit, but each active flag adds complexity to your codebase. As a general guide, review flags that have been active for more than three months and consider removing those whose rollout is complete. If you find yourself managing more than twenty active flags, it may be worth assessing whether the system is still appropriate for your needs.
What happens if a flag configuration has an error?
It depends on how your code handles missing flags. In the helper example shown earlier, a missing flag returns false by default, which means the feature stays inactive. This behaviour is safe but means you should validate your configuration when loading flags and log warnings for unexpected entries.
Can I use feature flags for A/B testing?
Yes. The rollout percentage feature in a basic flag system lets you split traffic between the old and new versions. However, for serious A/B testing with statistical analysis and conversion tracking, dedicated A/B testing tools offer more sophisticated capabilities than a simple flag system provides.
How do I roll back a feature quickly if something goes wrong?
The fastest approach is to set the flag's enabled value to false in your configuration file and redeploy or clear any configuration cache. For immediate rollbacks, some implementations support hot reloading of configuration without a full deployment. In any case, having a plan for quick rollback before you activate a flag is worth the small amount of preparation time.