With the rebranding of Episerver to Optimizely there have been several questions floating around about the Optimizely stack and how it can be used in a traditional Episerver project.
This is a quick four part series where we look at some potential ways the products can be integrated. Here are the links to all of the posts in this series (please check back as new articles will be added):
- Part 1 – Full Stack Feature (Current Article)
- Part 2 – Full Stack A/B Test
- Part 3 – Web A/B Test – Coming Soon
- Part 4 – Personalization Campaign – Coming Soon
Optimizely Full Stack vs Web
One of the reasons that we will be reviewing both the Full Stack and Web projects is that each can serve a different need. The web projects are designed for business users to conduct experiments without having to deploy new Episerver code for each experiment. Because of this the business can see results faster with less cost.
A good business case for Optimizely’s Front End experiments would be to determine where on a page a news letter sign up buttons could be added. The possibilities of where to place this button are almost endless…. It could be added in the header, the footer, part way through the body, in the menu, utility nav, or even in multiple places throughout the page.
With Optimizely you can inject that button all over the page and record what drives the most traffic through that funnel. If this was done purely with backend code the developer would be responsible for making sure the button could be added to each of these areas. This would likely incur development costs and delays in order to schedule releases.
The Full Stack SDK provides the ability to tap into places where the front end code can’t go. The second post in this series will provide a very simple experiment where the price is being manipulated as part of an A/B test. In a full implementation we would need to call on the Optimizely Full Stack SDK in several different areas of the code base so that an end user was displayed the same price through out their journey. This would include updating the product search and the checkout process with the variations to determine price for the user. Without the capabilities to tie into the Full Stack SDK this experiment would not be possible.
The Code Base/Demo Site
The site that is being used for this project was created to display various wine products (who doesn’t love wine, right?) but does not use Episerver Commerce. The site has a basic product catalog that is formed from various blocks and pages in Episerver.
A quick note about the code. This has been put together as a quick demo to show the basics and is not production ready code and does not always follow best practices. It is solely shown for demo purposes 🙂
Overview
The outline of the steps are as follows:
- Create a feature flag in Optimzely with variables to pass into Episerver
- Install the Optimizely SDK via Nuget into the project
- Update the model for the product variation with variables returned from Optimzely
Creating the Feature Flag
Creating feature flags inside of Optimzely is fairly straight forward. Simply go to the Features section and select ‘Create New Feature. The wizard will then walk you through the steps to set up your feature.
The first step is to define your feature with a Feature Name, a Feature Key and a brief description of the feature. This screen also allows you to pass variables into your feature. For the fun of it I will be passing a couple of variables to determine how the ‘Add to Cart’ button will be displayed.
Next we will be setting up our rules. The rules allow the editor to define an audience for which the feature will be turned on. Audiences help you show your experiment to a specific group of visitors, based on conditions that you specify. For example, you might want your experiment to target visitors who’ve seen a specific AdWords campaign. Or you might show an experiment to visitors on mobile devices only. For this simple demonstration we will be displaying the feature for everyone.
From this point we can go to the SDK Setup tab and see the code that will be needed to retrieve our feature.
Using the feature in code
Using the feature code in our Episerver project is, again, fairly straight forward. After installing the SDK from Nuget it is as simple as retrieving the feature values as indicated by the code provided in the SDK Setup tab and pass that information on as needed. For our demo project we have a helper method that retrieves the product variations and returns a view model. To quickly facilitate our feature flag we have added the properties to the view model. When the class is instantiated we create an instance of the Optimizely client and retrieve the settings for the feature. The values are then available via getters.
A quick note about the user Id. The code to get user id code has been hidden behind a helper method for this demo and will depend on your implementation. You will want the user id to be something that can be tracked through one or more sessions. Possibly the Google Analytics User Id.
public class ProductVariantListViewModel
{
private Optimizely _optimizely;
private string _userId;
private UserAttributes _userAttributes;
public ProductVariantListViewModel()
{
var configManager = new HttpProjectConfigManager
.Builder()
.WithSdkKey(ConfigurationManager.AppSettings["Optimizely.SdkKey"])
.Build(false); // sync mode
_optimizely = OptimizelyFactory.NewDefaultInstance(configManager);
_userId = UserHelpers.GetUserId();
_userAttributes = new UserAttributes
{
{"browserType", HttpContext.Current.Request.Browser.Type }
};
}
public List<ProductVariantBlock> ProductVariants { get; set; }
public Variation Variation
{
get
{
return _optimizely.Activate("price_increase", _userId);
}
}
public bool FeatureEnabled
{
get
{
return _optimizely.IsFeatureEnabled("display_buy_button", _userId, _userAttributes);
}
}
public string ButtonColor
{
get
{
return _optimizely.GetFeatureVariableString("display_buy_button", "button_color", _userId, _userAttributes);
}
}
public string ButtonFontColor
{
get
{
return _optimizely.GetFeatureVariableString("display_buy_button", "button_font_color", _userId, _userAttributes);
}
}
public string ButtonText
{
get
{
return _optimizely.GetFeatureVariableString("display_buy_button", "button_text", _userId, _userAttributes);
}
}
}
Now that the properties have been retreived, we can then use this model as needed. The following example shows the feature implemented in razor view. As we iterate through our variants for the product we create the ‘Add to Cart’ button with the variables provided if the feature is enabled for the user.
@for (var i = 1; i <= variants.ProductVariants.Count; i++)
{
var id = string.Format("tab-price{0}", i);
var style = i == 1 ? "active show" : string.Empty;
var variant = variants.ProductVariants[i - 1];
<div class="tab-pane fade score-tab-panel @style" data-anchorable="True" data-ux-state="loaded" id="@id">
<div class="bj-variant-price" style="display:flex" @Html.EditAttributes(x => variant.Price)>
<div>
@variant.Price?.ToString("C2")
</div>
@if (variants.FeatureEnabled)
{
<div style="padding-top: 10px; padding-left: 70px;">
<a class="score-button product-add"
title="@variants.ButtonText"
style="background-color: @variants.ButtonColor; color: @variants.ButtonFontColor">
@Html.Raw(variants.ButtonText)
</a>
</div>
}
</div>
<div class="bj-variant-picker-content bj-image-thumbnails-@i" style="display: none;">
@Html.DisplayFor(x => variant.Image)
</div>
</div>
}
The Results
Now that the feature has been implemented we can go to our page and see that the ‘Add to Cart’ button is being displayed on the page. If we update the feature to no show for no one the button will not be displayed. We can also update the variables to change the color and text of the button.
In our next article we will update the same view model to add an A/B test that determines the price of the item to add to the cart.