User identification and Adobe Target

08 Nov 2020 » MSA

A year ago I wrote about Declared IDs. I briefly mentioned then an issue that arises with setting these IDs: lazy loading and Adobe Target. However, I know that I did not explain too much about the issue. In this post I will get into more detail.

Order of execution

The typical order of execution of the Adobe tools on a page is as follows:

  1. Request ECID on the first page of the first visit or read the stored value
  2. Call Adobe Target
  3. Apply changes to the DOM according to the response from Target
  4. Call Adobe Analytics
  5. Call Adobe Audience Manager or use server side forwarding.

Steps 1 & 2 must happen inside the HEAD section of the HTML page. Expect the unexpected if moved outside. Steps 4 & 5 can be moved to the very bottom of the page, just before the </body>  tag, although you could put them elsewhere in the HTML. Number 3 takes place automatically as the page loads.

What happens when you have a declared ID? You are free to call Visitor.setCustomerIDs()  (help page) at any point after step 1. All Adobe calls will use the declared ID after the previous JavaScript code executes. However, it is clear what will happen if you make the call after step 2: Target will not know about your declared ID. Why is this an issue? Read on…

Target and the user profile

Strictly speaking, Adobe Target does not need a declared ID to work. It can still do all its magic just on anonymous visitors. However, if you have declared IDs, Target can use it to enhance its segmentation capabilities:

  • Customer Attributes. Target can use additional attributes of the person, beyond the browser, to segment visitors. These attributes use a declared ID as the key.
  • Profile merge rules. Most segments created in AAM can be used in Target. If you have an AAM license, Target makes a call, either every page view or every visit, to AAM to get the visitor’s segments in AAM. If the call to Target includes a declared ID, it will also forward it to AAM, which can then apply profile merge rules to evaluate all segments that the visitor qualifies for.

In other words, it is a bad idea not to use declared IDs with Target.

The problem

If you are a marketeer, it all makes sense and you do not see where the real issue is. Why should this be a problem? But if you are a developer, you probably have spotted it: you do not want to call Visitor.setCustomerIDs()  in the HEAD, before loading Target. Many modern CMS implementations use various techniques that prevent or make it very difficult or expensive to surface this declared ID that early.

Acceptable solutions

In the post I referenced at the top, I briefly explained how to solve it, without getting into too much detail. However, I want to be now more explicit about the solutions I recommend to make sure Target can use the declared ID.

Ideal solution: server-side

To me, the ideal solution is very simple: load a data layer at the top of the page, before calling Target, with the declared IDs. In this case, the CMS generates the data layer, with all the known data about the page, the visitor and any other information you need for marketing purposes. I do not care which type of data layer you are using, as long as there is one and is structured.

I know what my friend on the CMS side will say: it is not easy, it has a cost implication, it adds more load to the web servers… If it is a matter of costs, I would then recommend the following. Evaluate the additional cost my proposal would have and compare it with the potential revenue you could make from using Target the best way possible.

Finally, if you are using AEM, you could use a feature called Sling Dynamic Includes. Strictly speaking, this feature relies on Apache’s Server-Side Includes, so other CMSs may also benefit from it. The idea is that you can separate the web content into two types:

  • Static. Most of the HTML content, images, CSS, JavaScript and other assets fall into this type. Regarding the data layer, some attributes are known at the moment of publishing and do not change from visitor to visitor. Examples of these attributes are URL, page name, section, product name and author. All static content can and should be cached.
  • Dynamic. Only a very small fraction of the HTML should be dynamic. In the case of the data layer, variables that rely on the current visitor, like the declared ID, are dynamic. You should then create a Sling Dynamic Include or Server-side Include to execute some minimal code to generate the dynamic part of the data layer.

Alternative solution: client-side

As the browsers become faster and the CPUs more powerful, many developers are shifting the processing effort from server- to client-side. I understand that trend, although I only agree with it partially. In theory, client-side processing should not affect the moment when the ECID and Adobe Target are called. The reality, though, is a bit more complex. And, if your website is an SPA, then it is way more complex.

In summary, in this alternative solution, you need to keep the declared ID somewhere in the browser (cookie or localStorage). One thing to note is that you need to store it the very moment the user logs in, not on the next page. Then, on every page and before calling Target, execute some JavaScript code to instantiate a minimal data layer, including this declared ID.

You may be wondering why this is not my ideal solution. I agree that there are benefits from this shift from server to client. The problem I see is that server efficiency is the only yardstick: the faster, the better. We should consider other dimensions, like digital marketing technology. An extremely fast website, with no personalisation and no A/B testing will never achieve its maximum value.

Unacceptable solutions

I am going to also summarise a couple of other solutions, which could be tempting. However, I will never recommend them and there is now way anybody can convince me otherwise. It is actually like shooting yourself in the foot.

Ignoring Target

Basically, this means that you do not care about Target getting the declared ID. It is probably the simplest solution from the CMS perspective. In my experience, some web developers will want this one, arguing that it is the cheapest option.

And, if you are using the cost as the basis of the decision, let me show you how this is wrong:

  • You have very powerful marketing tools, which do not come cheap, and you have decided to limit their capabilities. All the savings that you can make with the CMS development, will be compensated by paying licenses of products you cannot use at their full potential.
  • I have seen how A/B tests and personalisations done right can make a massive change in revenue. We are talking of 100,000s of pounds/dollars/euros. Now, go an explain the CEO that, in order to save a few thousands, you are preventing a growth in revenue 100 times bigger.

Delaying Target

Google and others recommend to load and execute JavaScript at the bottom of the page. However, it has a catch: it causes flicker. Sometimes it is barely noticeable, but others it is. As an example, I am sure you have noticed that some pages take a few seconds to replace the “Sign in” button with “Hi, Pedro” or “Your account” text. However, if the change in content is bigger than just that, flicker becomes unacceptable. What would happen if an offer disappears 2 seconds after it has shown up?

Well, this is what happens if you try to move the Target calls to the bottom of the page. In principle, it looks perfect: apparently a faster website and the declared ID is available to Target. However, since Target will only be able to modify the HTML after it has been fully loaded, massive flicker will happen. Your friendly marketeer will pull her hair when seeing it. Again, a no-go.


I would love to hear your point of view, your experiences and the solutions you have applied in similar situations. If you have any examples, please share them in the comments!