The Gilt technology organization. We make gilt.com work.

Using Dependency Injection To Incorporate A/B Testing Into Your Applications

If you work at an e-commerce company, chances are you’ve probably come across the term “A/B testing”. We all know that it has something to do with testing out new features on users and seeing which ones are “better”. A/B testing is really the practice of comparing the effect that a feature (“treatment” in statistical terms) has on different groups of users. For instance, you might be developing a new navigation bar for your Website. You can see if it improves how long users stay on your site by testing it on a group of users (A) and comparing them to users in another group (B) that are using the original navigation bar. Again in statistical lingo, group B would be considered your “control group”. It’s important to note that you can test a variety of navigation bars here and not violate the principles behind A/B testing since your treatment is the type of bar you are exposing.

Now you are probably wondering how dependency injection (DI) factors into A/B testing. DI is the design principle where your objects are assigned references to their dependencies at runtime. Generally speaking, objects using DI refer to their dependencies through interfaces or super classes so they are indifferent to how those dependencies are actually implemented and instantiated. There are a few frameworks in Java that leverage DI. For the work that we do at Gilt, we use Java Spring. The example in this post is in Spring but it’s general enough that it can be applied to other frameworks.

So now that we know what DI is, let’s apply it to make A/B testing a part of your applications. In A/B testing, groups of users are exposed to a given treatment. That means we need to have a mapping between groups of users and the treatment that they are receiving. Java Spring makes that very easy; in your Spring configuration, you can create such a mapping using the map tag. For example:

<util:map id="ABTestMapping">
  <entry>
    <key>
      <value>group 1</value>
    </key>
    <ref bean="baseline_nav" />
  </entry>

  <entry>
    <key>
      <value>group 2</value>
    </key>
    <ref bean="test_nav1" />
  </entry>

  <entry>
    <key>
      <value>group 3</value>
    </key>
    <ref bean="test_nav2" />
  </entry>
</util:map>
 
<bean id="baseline_nav" class="com.gilt.examples.BaselineNavBar"/>
<bean id="test_nav1"    class="com.gilt.examples.NewNavBar1"/>
<bean id="test_nav2"    class="com.gilt.examples.NewNavBar2"/>

So in this map, our keys are different test groups of users and their corresponding values are the different navigation bar we will expose to them. This example is a bit contrived but it illustrates the point quite well. Instead of having to code A/B testing into your application, you can externalize it in a configuration. Java Spring instantiates this map and your application can use it to determine which navigation bar a user should see. A nifty trick in Spring is to use the import tag in your configuration. 

<import resource="file:test_buckets.xml"/>

This tag allows you to put your A/B testing configuration into a separate file which is very powerful. Doing so allows you to change which groups receive various treatments without having to recompile or redeploy your code. 

Simple. Fast. Very Useful.