Free feature flagging in Optimizely


Feature flagging in Optimizely allows gradual rollout of new features, or performing A/B/n tests, without need for deploying new code.

Estimated read time : 4 minutes

Jump to

Key takeaways

  • Optimizely feature flagging is a free feature of Full Stack experimentation
  • Each user should be assigned a unique ID to include when checking if a flag is enabled
  • Flags can be enabled per environment and/or for specific audiences
  • Feature flagging, just like personalization, may impact caching strategies

Prerequisites

  1. Free feature flagging account: http://optimizely.com/free-feature-flagging
  2. Optimizely CMS 12 website

Create a Full Stack project

Sign in to https://app.optimizely.com and create a new Full Stack project: 

imageychsl.png

Add a feature flag

Go to Flags ➡ Create Flag and give it a descriptive name. Make note of the key and change it if necessary:

imageciia9.png

Switch the flag on

For now we only want our new feature to be enabled in development environments, so we switch it to On for Development while keeping it Off for Production:

imagez524c.png

Make note of your SDK keys

Go to Settings and make note of the SDK Key for each environment:

imageh87l.png

Prepare your website project

First we need to add the SDK. Since we're doing this for an Optimizely CMS website, we need the C# SDK. It is published as a NuGet package called Optimizely.SDK.

Next we add some code to the ConfigureServices method in our Startup class to initialize the SDK:

ProjectConfigManager fullStackProjectConfig = 
    new HttpProjectConfigManager.Builder()
        .WithSdkKey("BzNk7YXpCd5NvVZm4n8Co") // Should be in environment-specific config
        .WithPollingInterval(TimeSpan.FromMinutes(1)) // Refresh data file once per minute
        .Build();

services.AddSingleton<IOptimizely>(new Optimizely(fullStackProjectConfig));

Implement feature flipping based on the flag

Next we add some code to conditionally (based on the feature flag) apply a browser theme color.

In this case we use Razor pages, so we add some logic to the page model:

public class SamplePageTypeModel : RazorPageModel<SamplePageType>
{
    private readonly IOptimizely _fullStack;

    public SamplePageTypeModel(IOptimizely fullStack)
    {
        _fullStack = fullStack;
    }

    public bool ApplyBrowserTheme { get; set; }

    public void OnGet()
    {
        ApplyBrowserTheme = _fullStack.IsFeatureEnabled("apply_browser_theme_color", GetOrCreateUserId());
    }

    protected string GetOrCreateUserId()
    {
        const string cookieName = "fullStackUser";

        // Return existing user ID, if any
        if(Request.Cookies.TryGetValue(cookieName, out string? userId) && !string.IsNullOrWhiteSpace(userId))
        {
            return userId;
        }

        // Create new user ID
        var newUserId = Guid.NewGuid().ToString();

        Response.Cookies.Append(cookieName, newUserId);

        return newUserId;
    }
}

Finally we add some markup to be rendered only if the feature flag is On:

@if(Model.ApplyBrowserTheme)
{
    <p>Theme is applied.</p>
    <meta name="theme-color" content="#4285f4" />
}
else
{
    <p>Theme is NOT applied.</p>
}

Testing the result

If we browse to our page, we see that our feature is indeed enabled:

imageu3qwm.png

We can now test switching the flag to Off (don't forget to hit Save at the bottom right):

image96ojv.png

When refreshing our page, we see the feature is no longer enabled:

imagepmdp8.png

Note: It may take up to a minute for the change to take effect since we configured Optimizely to only poll for changes once per minute.

What is the GetOrCreateUserId() method?

In the Razor page model we added a method called GetOrCreateUserId() to get a unique user ID which we pass on as an argument when invoking IsFeatureEnabled() to see if a flag is enabled.

The reason for this is to be able to divide users into different buckets (or audiences) for A/B tests, or to gradually roll out a feature, for example by enabling a flag for 25 % of the users.

To ensure a user isn't assigned to a different bucket/audience on every page load, it makes sense to assign a unique ID to each user.