How to integrate Optimizely Feature Rollout with EPiServer
I have been writing a number of posts about agency clients and use-cases for agencies pain points here on EPiServer World and on other blogging platforms in the past. I also had the chance to work for startups and product companies and I could notice that the process for feature delivery is sometimes a bit different.
Product companies tend to focus on a small number of products and a large number of deployments for features, micro-features and components. One requirement that is listed is that It must be possible to release specific features for specific targets like markets, user groups or audiences without “corrupting” the integrity of the project.
As an example, social networks with a few billion users in hundreds of countries are regularly publishing different features for different countries with full control over the delivery and no downtime.
I have had the opportunity to play with Optimizely during my free time, and I was very pleased with the capabilities of their latest ‘Feature Rollout’ release.
One of the perks regarding feature rollout is that it helps avoiding messy rollbacks and hotfixes as it is possible to use feature flags to roll out features to users with an option to roll back if needed to reduce the risk using a convenient interface.
Technical limitations
Let’s get back into EPiServer for a second. We have a wonderful system with flags that can be controlled in a separate backoffice. However, due to the nature of c# and the ‘code first’ approach in EPiServer regarding data models, there are items that we cannot ‘roll out’, I listed a few of them:
- Page & Block types
- Product types in EPiServer Commerce
- Selection factories & rules about allowed content
- New Scheduled jobs
The main reason regarding this limitation is that the flag coming from Optimizely is a Boolean and it is not possible to set wrap ‘if’ statements around classes or attributes.
The default behaviour in EPiServer regarding components like Scheduled Jobs is that they become available once the class is included in the dll.
While we can’t ship pages or blocks as Optimizely “features”; we still have a wide range of opportunities to use feature rollout:
We can use the flag to hide / show new features in a page like new sections of a page:
@{
var optimizelyClient = OptimizelySDK.OptimizelyFactory.NewDefaultInstance(System.Configuration.ConfigurationManager.AppSettings["Optimizely:sdkKey"]);
//testing for all clients
var user = optimizelyClient.CreateUserContext("");
//feature flag
var decision = user.Decide("enable_messenger_chat");
var enableMessengerChat = decision.Enabled;
}
@if (enableMessengerChat)
{
<div>new feature here</div>
}
We can also use the flag at the page controller level to “disable” pages and blocks from being rendered until the Optimizely feature is marked as enabled:
public class NewFeaturePageController : PageController<NewFeaturePage>
{
public ActionResult Index(NewFeaturePage currentPage)
{
var optimizelyClient = OptimizelySDK.OptimizelyFactory.NewDefaultInstance(System.Configuration.ConfigurationManager.AppSettings["Optimizely:sdkKey"]);
var user = optimizelyClient.CreateUserContext("");
var decision = user.Decide("enable_new_feature");
var isEnabled = decision.Enabled;
if(isEnabled)
return View(currentPage);
else
throw new NotImplementedException();
}
}
With this code, even if the page is deployed, users - like the content team - will not be able to see the page until the feature is enabled in Optimizely.
Update: there is a way to "hijack" the creation of pages, blocks and other episerver components using Initialization modules and the right service, solving the "we can't wrap a boolean around a class or an attribute" issue. However this is a lot more advanced and I chose to cover examples that are available using the standard online training classes.
Integration
I created a test project in github to review the capabilities of Feature Rollout, the code is available here: https://github.com/giuunit/optimizely-feature-rollout-test - it's an example of a feature (enabling facebook messenger web plugin) using Optimizely Rollout Feature. Most of the code is inside the master layout as I wanted the plugin to be available for all the pages in the web project. The code below shows how to integrate the FB Messenger chat web plugin using Feature Rollout and a variable for the page id:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
//setting up Optimizely;
var optimizelyClient = OptimizelySDK.OptimizelyFactory.NewDefaultInstance(System.Configuration.ConfigurationManager.AppSettings["Optimizely:sdkKey"]);
var user = optimizelyClient.CreateUserContext("");
//feature flag
var decision = user.Decide("enable_messenger_chat");
var enableMessengerChat = decision.Enabled;
//retrieving the page id
var pageId = decision.Variables.GetValue<string>("page_id");
}
@if (enableMessengerChat & !string.IsNullOrWhiteSpace(pageId))
{
<!-- Messenger Chat Plugin Code -->
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
xfbml : true,
version : 'v10.0'
});
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = 'https://connect.facebook.net/en_US/sdk/xfbml.customerchat.js';
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<!-- Your Chat Plugin code -->
<div class="fb-customerchat"
attribution="biz_inbox"
page_id="@pageId">
</div>
}
Do you have ideas about integrations between Optimizely and EPiServer CMS ? Leave a comment below 😊
Nice. I do like the capabilities of rollouts, particularly for progressive or targetted rollout rather than just switching a feature on or off for everyone.
Interestingly, the topic of dynamically showing/hiding content types that you mention as a limitation came up in conversation with one of my colleagues yesterday and it's certainly possible, you just need to know where to look. I wrote a post on feature switching per language a while back which shows how it can be done:
https://world.episerver.com/blogs/paul-gruffydd/dates/2019/3/language-based-feature-switching/
I suspect the other limitations could be worked around too if you wanted to though I've never tried it.
Thank you for your comment Paul 😊 It's an excellent suggestion & article. I added a note about the possibility to "hijack" some of the components that I listed as part of the limitations paragraph. I wanted to keep this article at a level where new developers would be comfortable trying it.