Hybrid experimentation
Kameleoon can be operated in hybrid mode, using both server-side SDKs and the Kameleoon JavaScript application file, which brings many benefits by allowing different approaches to be used for different tasks. For example, variations can be implemented and deployed more easily on the back-end, while tracking can be performed more effectively on the front-end, since many third-party web analytics solutions only work on the front-end.
Kameleoon provides native integrations with analytics or CDPs platforms, and with Kameleoon Hybrid Experimentation, you can use any integration in combination with server-side experiments without needing any additional code from your team.
This article describes how you can implement an optimal integration between Kameleoon Back-end and Kameleoon Front-end, and have both sides communicate with each other. It assumes Kameleoon is already properly installed in your back-end server as well as in your front-end HTML pages.
To ensure the reliable performance of Kameleoon on Safari browser, it is now recommended to implement a back-end/front-end bridge for ITP management, even if you do not directly operate Kameleoon on the back-end. This will help overcome Intelligent Tracking Prevention (ITP) limitations and enable Kameleoon to function seamlessly.
Ensuring user identification consistency between the back-end and the front-end
To benefit from client-side capabilities on top of server-side experiments, you will need to implement both our SDK and our Kameleoon JavaScript tag. We recommend you implement the Kameleoon Asynchronous tag, which you can install at the end of your body, as it will be only used for tracking purposes.
One of the challenges of using Kameleoon both on the back-end and the front-end is to make sure each visitor is properly identified as the same individual from both sides. In Kameleoon, this means that the visitorCode, which is a unique id for every user / visitor, has to be identical everywhere. This allows correct tracking of the visitor's actions, which in turn leads to correct data and statistics. Ie, a user registered with the visitorCode 10ctbql0zpf4rwjy
for a variation of an experiment in the back-end also has to be associated with the same visitorCode when a conversion is triggered in the front-end.
From back-end to front-end, communication is easy (for instance via embedded JavaScript code in the HTML pages). But sending information from front to back can be difficult for a third party platform with an integration that needs to be as painless as possible. The only practical way is to write a cookie, that our back-end SDKs will read and use. However, recent developments with Apple's Intelligent Tracking Prevention (ITP) technology, as well as similar approaches from other browser vendors, heavily restrict what JavaScript based solutions can do. With ITP 2.3, cookies deposited via JavaScript can only have a lifespan of seven days before expiring.
So to ensure consistency between the back-end visitorCode and front-end visitorCode, we came up with the following approach.
Front-end side
On the front-end side, a visitorCode is generated and assigned for every new visitor. A visitor is technically considered a new visitor if no previous visitorCode can be found by the engine for this user. This usually happens when the Kameleoon engine is run for the first time on the visitor's browser. The logic of the visitorCode assignment is the following:
First we check if a kameleoonVisitorCode Local Storage key exists. If no, we check for the presence of the kameleoonVisitorCode cookie. If it is present, we will use this as the identifier for the visitor code. Else the identifier is generated randomly.
If the Local Storage key does exist, we will use this as the identifier for the visitor code. This happens even if a kameleoonVisitorCode cookie with a different value is present, which means the identifier from the LS takes precedence.
In any case, the defined visitorCode is then stored in the browser's Local Storage. It is also stored on the cookie if the cookie was absent or if its value differs from the defined identifier. Note that if values match, we do not set the cookie from JavaScript, to leave intact a cookie that may have been set on the server-side (important for ITP purposes).
When Kameleoon is only used on the front-end, this means that visitorCode identifiers are always assigned randomly in the JavaScript engine. But if it is used on both sides, the JS engine can also obtain the identifier from a cookie passed from the back-end. This allows the identifier to be set by the server, rather than in the front-end.
You don't need to implement anything on the front-end. All of this is performed automatically by the Kameleoon's engine. This section just gives technical details about the inner workings of the JavaScript Kameleoon engine.
Back-end side
On the back-end, several options are available to synchronize the visitorCode:
If you use a Kameleoon SDK, we designed the
getVisitorCode()
method. You should make sure to call this method everytime you need to obtain a visitor identifier, usually before triggering a server-side experiment via our SDKs. ThegetVisitorCode()
takes an optional string argument that you can use to pass your own identifier rather than rely on a dedicated, randomly generated Kameleoon id.If you don't use a SDK, other methods are possible to perform the synchronization, such as creating a custom DNS entry (CNAME) or adding a specific code snippet on your backend server. We list the different implementation options here.
In any case, the following logic is implemented:
First we check if a kameleoonVisitorCode cookie or query parameter associated with the current HTTP request can be found. If so, we will use this as the visitor identifier.
If no cookie / parameter is found in the current request, we either randomly generate a new identifier, or use the given argument as identifier if it is passed. This allows our customers to use their own identifiers as visitor codes, should they wish to. This can have the added benefit of matching Kameleoon visitors with their own users without any additional look-ups in a matching table.
In any case, the server-side (via HTTP header) kameleoonVisitorCode cookie is set with the value. If using the SDK, this identifier value is also returned by the method.
Conclusions and remarks
By following this approach, the visitorCode is coherently saved and shared between front-end and back-end, and cross-domain and ITP restrictions are also correctly taken into account. If an experiment is implemented on the first page of a visitor's journey on your website, the back-end will generate the identifier and pass it to the front. If the journey starts on a page where getVisitorCode()
is not called (or the synchronization snippet is not ran) on the back-end, then the front-end will generate the identifier first, pass it to the back-end, which will read it and then rewrite it as a server-side cookie to bypass ITP restrictions.
Cross-domain problems are also automatically treated. The only difficult case consists in a change of top-level domain with a server-side triggered experiment on the first page of the new top-level domain. In this setup, the cookie associated with the previous domain cannot be read on the new domain, and the Kameleoon JS engine won't have had the opportunity to run and restore the identifier from the Local Storage yet. So, if possible, you should pass the visitor code as a kameleoonVisitorCode HTTP query parameter from the old domain to the new one. Another solution is to wait until Kameleoon's JavaScript engine has run once on the new domain before accessing server-side SDK methods.
Since on the front-end the visitor code is stored in the Local Storage, the cookie is ONLY used for synchronization between front-end and back-end. Deleting or losing the cookie would not have any impact on the Kameleoon JavaScript engine's normal operations, since the identifier would be read from the Local Storage anyway. It just means that synchronization could be lost between front-end and back-end sides.
Linking server-side experiments with front-end tracking code (Hybrid Experimentation)
Often, it can be useful to setup an experiment where the implementation of variations is done on the back-end, but the tracking part is done on the front-end. This is a common situation because Web Analytics platforms (such as Google Analytics) tend to be implemented on the front-end, as well as Kameleoon tracking code. To make sure you can track and analyze your server-side experiments via your front-end tagging plan (with all your usual KPIs / goals), you need to link your server-side experiments with the front-end.
If you follow these guidelines, and you already use a standard client-side tagging plan for Kameleoon, then you don't even need to implement conversion calls on the back-end when using server-side experiments (although this can still be useful). Goals and conversions will be tracked via the front-end and will appear automatically in Kameleoon reports. This will also allow you to get experiment results in any Web Analytics solution with an integration to Kameleoon (Adobe Omniture, GA4, Mixpanel, Amplitude, etc).
To do that, you should create an Hybrid experiment in our interface. This will ensure the experiment is correctly recognized and injected both into the client-side and server-side. Then there are two steps to take into account: the targeting / triggering of the experiment and the variation allocation.
Ensuring correct triggering on the front-end
Our front-end engine needs to know whenever an experiment has taken place in the back-end, ie when a visitor has been bucketed into an experiment. This is important in order to count only visitors that have actually seen / triggered the experiment. There are two ways to achieve this:
- You can provide the correct targeting segment by using our segment builder. The targeting conditions should exactly match the triggering conditions on the back-end. For instance if your experiment runs on all your product pages, the targeting conditions should match this scope as well. This could be quite tricky, depending on your site and the exact experiment you are running. If it's difficult to establish a proper equivalence, it's recommended to proceed with the second method instead.
- You can use our Activation JavaScript API to trigger the experiment explicitly from your code, where the experiment’s triggering code is written. It can be for example embedded in the returned HTML page (see code below). Once the logic is managed in your code, you will need to create a segment with the targeting criteria Explicit trigger to tell Kameleoon that the targeting is explicitly triggered by code.
You can take advantage of the Kameleoon Command Queue to trigger the experiment explicitely, as shown in the example below.
window.kameleoonQueue = window.kameleoonQueue || [];
const experimentID = 1; // this value must be correctly entered and corresponds to the experiment's ID in Kameleoon
window.kameleoonQueue.push(['Experiments.trigger', experimentID, true]); // onlyTracking = true, which means we only activate the experiment's tracking. This is usually what we want with hybrid experiments. If your experiment also contains CSS / JS code on top of your back-end code, onlyTracking should set to "false" as you also want to trigger this code.
Ensuring correct variation allocation on the front-end
Obviously, the same variation the user was subject to on the server-side should be registered when the experiment is triggered on the client-side. Since variation allocation is computed from the Kameleoon visitorCode, if you followed the recommendations outlined earlier to synchronize the visitorCode between the back-end and front-end, you don't have to do anything else. The visitor will have the same identifier on both sides, so will also get the same variation automatically assigned (for a given experiment).
If you don't ensure proper user identification consistency between the back-end and the front-end (although you should), or if you want to be extra careful, you can use another Activation API method to explicitely set the variation, as outlined in the sample code below. This JavaScript snippet must be generated by the back-end side, where the experiment's triggering code is written. It can be for example embedded in the returned HTML page.
window.kameleoonQueue = window.kameleoonQueue || [];
const experimentID = 1; // this value must be correctly entered and corresponds to the experiment's ID in Kameleoon
const variationID = 3; // this value must be correctly entered and usually corresponds to the value returned by the trigger() method
window.kameleoonQueue.push(['Experiments.assignVariation', experimentID, variationID, true]); // override = true, which means that if an existing variation has been allocated to the user, it will be replaced by the new one provided.
window.kameleoonQueue.push(['Experiments.trigger', experimentID, true]); // onlyTracking = true, which means we only activate the experiment's tracking. This is usually what we want with hybrid experiments.
If you would like to send experiment data to third-party analytics, don't forget to activate the desired integration (Google Analytics, Adobe Omniture...) on the experiment. The relevant data (experiment ID, experiment Name, variation ID, variation Name) will then be automatically sent to the third party platform by the Kameleoon JavaScript engine.