Implement Adobe Target Server Side - Part 4

A demo of Adobe Target server side implementation in a real world application, the differences between the sample app and a real world app and the common pitfalls to watch out for.


So previously I talked through an ideal world scenario, the simplest possible version of this in the demo in from the NPM packages documentation. It gets a bit more complicated when you come to put this into an actual website and do it in practice. It’s not that complicated, it’s just there's a few things / nuances that are worth noting. I own and run my own conversion agency, which you'll probably be either watching this on YouTube or in the blog so you’ll either know of you won’t, but the advantage of that is it gives me a website to play with. So, this is the live site, this is my staging environment. So what I've done is, I've implemented a server-side test on my actual live or staging site. I've already completed it so I'm not going to go through step by step in terms of me implementing it, but it’s probably worth just running through how it works.

So, previously all the code that we had in the demo all lived inside the file called target-server.js. Now in your real application, it's unlikely that it will live there, it's most likely going to live in a route rather than living in the application itself. So nothing actually goes in the app.js, everything lives in the route. So in my case for my agency website, there is some pretty simple routes out for thank you, or contact or index. So, what I've essentially done is create a new route for /AB test. Now it's just a replication of my homepage, so for my homepage, the routes are pretty simple - if someone goes to /root, it will just render the index and the index is a pug template. If you know what pug is great, if you don’t, it's a templating engine that basically will compile into HTML on the back end but it allows you to use kind of variables and such like. The way I go about doing this is always a duplication of a template. So, the reason for that is if Target fails, in the test that I'm doing here this is the duplication. I'm replacing the content here, which is h1.intro title, which is this main copy here.

So, this main copy I'm replacing in this test. Now the reason I don't do it in index.pug is if Target fails, nothing will go there because I will have overridden the default content. So I leave the original index.pug and I create a duplication of it. Then in the route itself (up here), in the error handler which I spoke about earlier (which is here), basically if there's an error with Target, I just render the normal homepage. I don't do anything clever, it just is a safeguard hence creating index in index.2. Now index.2 (here) is what’s identical to index1 or just index, but rather than the content being hard-coded like it is here, it's actually a variable which is pound sign content. Now don't worry about the variable too much if you don’t know about pug, but this is just essentially how we declare an unescaped variable. Now that content is being passed in to this template from the route. So, all this content here all the way down is exactly what was in the demo which I went through earlier. The only thing that changes is you need a way of passing the content into a templating engine or if you're using a single page app framework like a Node and or an Angular, some way of storing it.

Now the way it’s done here is I render index 2, so basically it goes through all that it needs to do and note this time there is no template which there was in the previous demo because the template is my actual page which doesn't live here. But it goes through everything it needs to do, it creates the payload, it gets a response from Target and then I assign three variables, which is the org ID, the visitor’s state and the content which is exactly same as before. Cookies save, but this time on success I don't render the template, I don't send the template like it does in the previous demo. I fire a render and I render index 2 (which is my testing copy of index) and I pass in the org ID. the visitor’s state and the content, those three things.

So then when the /AB test route runs, it will actually in here replace org ID with a correct org ID which is passed in, the server state will be passed in the service state object, and the content will be whatever is returned from the server, which could be experience A or experience B. If Target fails, times out or the server’s down (something like that), it will default back to the normal index.pug and just show my homepage. So, I'm going to show this on my local so if I just navigate to this on my local, by the time this gets on my blog, it will be in the staging environment. That staging URL ( is actually probably accessible so feel free to have a look at the implementation there.

I’m going to navigate to this and I’m going to run NPM, and this is just going to run my company website on my local which is just exactly what you can see here. So this will now render and at this point nothing's happening, because this is just the homepage. But if we go to the route /abtest it will render this exact page. But it's rendered, this is a client-side headline from Adobe Target. Now the reason it says this is a client-side headline from Adobe Target is because there is one thing I want to make everyone aware of, which is that client-side will work with server-side, but it will override it. So here I have a separate test setup called client-side override, it's just one thing to be aware of that anything that the server returns once the browser renders it, is fair game for the Adobe Target client-side interface or the form editor to then manipulate. So, it's something to be aware of that can happen.

So what I will do here is actually just quickly edit this and I will just change this quickly, just so it won't fire anymore. I could just pause it, but with caching it might take a while for me to actually get out of the test so probably the best way of doing it is just to make myself not in the audience (just let that sync). So what was actually happening there is that, the server-side test was running and had been overwritten by the client-side test.

So in theory if I go back here now and just reload, there you go. So, now this is the server-side headline from Adobe Target, which is being returned not by the override but by the server-side testing (it's just basically this content here). Now what's worth mentioning is this doesn't have to be HTML - this could be a huge JSON file that you could just put loads of objects in there and you could just be picking them out left, right and centre to manipulate that page or be changing an algorithm, or pricing, or personalise. This is a very basic example and to be honest this example probably would be better off doing client-side but it's just for demonstration purposes.

So now we've got our test here let's just go into our debugger and clear everything off and have a look at what happens. So, as I mentioned previously, in an ideal world, Target, Analytics and the server side instance that is in the head (in here) all match the SDID and f they do, it will be sending all the data across as one visit and happy days. So you'll see this will change when I refresh so let’s work from this.

So we’ve got Target firing, the SDID (supplementary date ID) ends in A01. For Analytics it ends in A01 and then for my actual site in terms of service-side, it ends in A01 and it says here supplemental data id consumed with the payload of server-side test one which is server test one which is exactly what we've used. So, that's creating essentially a merged visit across the server side and the client side, which is what we want. Now when you first call this getinstance up here, it created a problem where it stopped my analytics in launch firing.

So, I am still implementing app measurement and the visitor API service, not Target but just the app measurement which is the analytics and the visitor API. I'm still doing that manually from my public folder. Now the reason I'm doing that is because, what happens when you get to your actual main site and you try and do it all through launch is that, this visitor.getinstance needs to fire really early. And depending on the sequence of firing, visitor is not created. So in launch I have the Experience Cloud ID service (or whatever it's called these days!). I have it implemented, so theoretically you shouldn't have to manually trigger the visitor API service from the public folder. But you do and the reason you have to do that is because it's here (this is the visitor 4.3) and I also have it here. Now the reason it's called here is because when this visitor.getinstance is called which has to be called right at the top of the page, the vistor object doesn't exist yet because launch basically doesn't fire it quick enough and so you have to manually invoke it here.

So, that's the reason it exists and that's the reason why I also manually trigger the app measurement at the bottom just to make sure it fires in the correct sequence. You probably don't actually need to do that you can probably take this app measure one out and just do it in launch or DTM providing you've got the correct timing triggers setup. But be aware there is a nuance, you just need to make sure that these are all firing in the correct order at the right time and if they don't, what you’ll find is that most likely analytics and Target will have the same SDID but it won't be the SDID that's been consumed from your visit.getinstance call earlier on.

And that is essentially it for a very simple (I know it’s gone on for quite a while!) view of how this works. There is much much more you can do with this, this is just scratching the surface. But when I was looking at documentation for this, there wasn't much, there was only the actual Target documentation not many walkthroughs, and I think a visual representation of this is easier to follow than just the documentation.

I went through my LinkedIn earlier on in the first part of this video. If you have any questions, I don't have a question section on my blog (there probably will be on YouTube) but the easiest way if you're trying to connect with me is on LinkedIn and just fire me any questions you may have – whether there needs to be some clarification or whether there needs to be some form of additional follow-up video for something that you guys may need. Just drop me a message and I'll get on it as soon as I can. I'll leave this LinkedIn link in my blog description or on YouTube, so it should be accessible.

Hopefully, this all makes sense, it's a little bit complicated. Just to wrap up, in my opinion server side is not the answer to the vast majority, it's the answer to the minority of testing situations you come across. Client side is still the way I'd go with everything if I could. From a performance aspect, Target is so so quick at loading, it has borderline no impact, security I don't see it as a big concern and it's only when you have complex tests.

If you start to move server-side with too much stuff your developers will be over the moon, they may love that and that's great, but there is massive value in a technical Marketing team running tests especially when you come to personalisation you’re just changing probably copy. So, to have both definitely is a huge advantage, it’s definitely more in the arsenal. There isn't a right or wrong, it is definitely a hybrid approach that I would advise.

But thanks very much for listening and there will be follow-up videos on this. So, thank you.