Sebastian Wismayer

Growth Marketer | Digital Enthusiast

Shared Mobility – App update frequency tied to customer satisfaction

A recent analysis by Lufthansa Innovation Hub looked at the 10 leading airline carriers and determined the frequency with which they released a new version of their app. They conclude that faster release cycles allows developers to more rapidly improve and add features, indicating an app development team that is well integrated with the other departments.

I want to take this a step further by exploring the connection between release cycles and app ratings, while also highlighting further benefits of faster update cycles. My hypothesis is that companies with shorter timespans between updates are more customer-centric and that this customer-centricity is reflected in an app’s rating.

Shared mobility app landscape

For the analysis I’ve reviewed 7 apps that offer short-term vehicle rental in Berlin. This is an industry that continues to be highly competitive (beyond Berlin) and that is mobile only. To increase comparability, the apps in focus require the customer to have a driver’s license and to operate the vehicle. Subsequently, ride hailing apps (e.g. BerlKönig, MyTaxi, Uber, etc.) and bike sharing apps (e.g. JUMP bikes, LIDL-bike, nextbike, LimeBike, etc.) are excluded.

Days between app updates

Days between app updates for mobility apps

When looking at the frequency with which these apps are updated, the above chart shows us that the update frequency varies considerably, with only two apps releasing more often than monthly. While it doesn’t differentiate between small updates and larger releases, it’s interesting to see such variance. The chart alone doesn’t provide evidence that frequent updates are better than less frequent ones though.

Including the app’s rating

Days between app updates for mobility apps including their average app rating

When we include the average rating of these apps across iOS and Android, a couple interesting insights emerge: The three apps with a longest time between updates also have the three worst ratings. The app with the most frequent updates also has the highest rating.
Just a coincidence? The correlation coefficient is -0,77, so while it doesn’t indicate a strong relationship and the sample size is small, it indicates a relationship nonetheless.

The benefits of frequent updates

Why do frequent updates seem to matter then? Keeping the customer front of mind, I would split the benefits into three categories: Fixes, Frictions, and Features.

Fixes

  • From app crashes that annoy users to more general usability issues, more frequency updates means bugs are fixed more quickly and users don’t have to live with them as long. When users identify a new bug that is addressed within a short period of time, it demonstrates that their feedback is valued and acted upon.

Frictions

  • All apps have some sort of funnel. Regardless of whether it’s a sign-up flow, a purchase, or finishing an in-game tutorial, some users will drop-off along the way. More frequent updates allow for more opportunities to test and improve the funnel iteratively.

Features

  • When adding new features or making changes to existing ones, smaller, more frequent modifications are easier for users to learn and accept. This approach allows for a consistent user experience, while still adding functionalities.

When addressing these three categories through frequent updates, users experience an app that is consistent, fixes bugs quickly, and adds/refines features in a manner that doesn’t fundamentally change the way they’re used to using the app.

Practical Implications

App development shouldn’t happen in isolation and in a manner that just works toward major feature releases. Increasing the frequency of updates means that developers are working toward smaller goals. It gives teams the ability to include customer feedback that is both explicit (e.g. from reviews) and implicit (based on user behavior, retention, funnel drop-offs). Moreover, it allows teams to prioritize frequently and more easily, based on the situation at hand.

Keep in mind that in many market segments, users aren’t locked in after initially choosing a service and loyalty has little benefit to the them. Shared mobility is one of these services, where users are agnostic in their selection and factors such as vehicle proximity are key.

Nevertheless, the vast amount of providers means that users’ switching costs are extremely low and after they’ve churned it becomes increasingly difficult to win them back. A seamless app experience that stays current and listens to user feedback can therefore become a differentiator in a crowded market and increase user affinity over time.

[Guide] Step-by-step visual guide to GDPR-conform cookie consent

This is the next iteration of the original cookie consent guide I wrote. The new version works the following way:

  • Cookies are divided into one of four categories using a free consent management platform: (1) Strictly necessary cookies, (2) functional cookies, (3) analytics/performance cookies, and (4) marketing cookies
  • The user lands on your site and the only tag that initially fires is the cookie banner, allowing the user to set their preferences (but without hindering access)
  • Based on the user’s preferences, only the tags will fire that belong to the category of tags they’ve accepted
    • The benefit here is that while the default will be to accept all cookies, it gives users the option to disallow some cookies (e.g. marketing) while still allowing others (such as analytics)

Disclaimer: I am not a lawyer and this guide is not meant as legal advice. Instead, consider it a best practice recommendation that lets your cookie consent form be compliant without overdoing it. There are many real-life examples of companies that are so over-compliant, they’re undoubtedly losing valuable analytics insights even though there is no legal need.

The aspects I was uncertain about I cross-checked with GDPR lawyers and they confirmed compliance. This guide is based on an article by Julius Fedorovicius, which I’ve adapted to increase clarity and ease of implementation.

5 Steps to GDPR-compliant cookie consent

0. Pre-requisites:

  • Google Analytics Account
  • Google Tag Manager Account

1. Request a free consent management platform account

  • Go to https://www.onetrust.com/free-edition/ and request a free account
  • The request typically takes two working days. Alternatively, a paid account costs $30/month (or use the code swdc19 and save 10%)
  • After you receive access to your OneTrust account and set it up, you’ll create a custom HTML tag with the script from your OneTrust account (setup details below)

2. Set up your OneTrust Account

2.1 Nav Menu → Websites → Add a website:

  1. Enter just the domain and without https or www (e.g. sebastianwismayer.com), this ensures it will work across subdomains as well. 
  2. Target pages to scan: From experience, it currently seems better to enter the ULRs of all pages in order for the tool to identify as many cookies as possible. Use a tool like Screaming Frog to determine all pages
  3. Wait for the scan to finish (unless your site is massive this should take less than half an hour)

2.2 Nav Menu → Cookie Banner :

  1. Layout + Colors can be adjusted how you wish
  2. Content:
    • Notice Description (unless you have a different, preferred one): “By continuing to browse or by clicking ‘Accept Cookies’, you agree to the storing of first- and third-party cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts.”
    • Cookie Settings Button: Yes (“Cookie Settings”)
    • Accept Cookies Button: Yes  (“Accept Cookies”)
  3. Behavior:
    • Toggle to yes the following: 
      • Close button accepts all cookies
      • Click accepts all cookies and closes banner
      • Scroll accepts all cookies and closes banner

2.3 Nav Menu → Cookie List:

  1. Assing cookies:
    • Update from scan
    • Auto Assign
      • For the cookies that the tool is unable to assign automatically, look up the cookie in Google and determine which category of cookies it falls into
      • IMPORTANT: Make sure you have at least one cookie in each of the four categories (Strictly Necessary, Performance, Functional, and Targeting). If you don’t, the category doesn’t appear in the cookie preference center and cannot be switched to ‘active’, resulting in all cookies of that category not firing
  2. Consent Settings:
    • Set the Consent Model to “Implied Consent”
      • Strictly Necessary Cookies are “Always Active”
      • The other three should be set to “Inactive LandingPage”

2.4 Nav Menu → Script Integration:

  1. Here you can change the overall look of the Cookie Consent Preference Center, which includes text, colors, and logo. Match it to your CI.

2.5 Nav Menu → Preference Center:

  1. Copy the Production CDN code and create the Tag: cHTML – Cookie Consent in your Google Tag Manager account (explained in more detail in step 5 below)
  2. Cookie Settings Button: Place the snippet on your site (e.g. in the footer) to allow users to adjust their cookie settings. 
  3. Publish

3. Add the necessary variables to Google Tag Manager

We will create 7 necessary variables (1 cookie, 3 JavaScript codes, 3 data layer variables)

1. Cookie for Consent

  • Variable Type: 1st Party Cookie
  • Name: Cookie – Actual Cookie Consent
  • Cookie Name: actualOptanonConsent

2. Custom JavaScript – Functional Cookies

  • Variable Type: Custom JavaScript
  • Name: Custom JS – Functional Cookies Allowed
function() {
  var consentFromCookie = {{Cookie - Actual Cookie Consent}};
  var consentFromDataLayer = {{dlv - Active Consent Groups}};
  if (consentFromDataLayer != undefined && consentFromCookie == undefined) {
    if (consentFromDataLayer.indexOf(",3,") >= 0) {
      return true // functional cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie != undefined) {
    if (consentFromCookie.indexOf(",3,") >= 0) {
      return true // functional cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer != undefined && consentFromCookie != undefined) {
    if (consentFromDataLayer.indexOf(",3,") >= 0) {
      return true // functional cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie == undefined) {
    return false // functional cookies not allowed
  }
}

3. Custom JavaScript – Performance and Analytics Cookies

  • Variable Type: Custom JavaScript
  • Name: Custom JS – Performance and Analytics Tracking Allowed
function() {
  var consentFromCookie = {{Cookie - Actual Cookie Consent}};
  var consentFromDataLayer = {{dlv - Active Consent Groups}};
  if (consentFromDataLayer != undefined && consentFromCookie == undefined) {
    if (consentFromDataLayer.indexOf(",2,") >= 0) {
      return true // analytics allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie != undefined) {
    if (consentFromCookie.indexOf(",2,") >= 0) {
      return true // analytics allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer != undefined && consentFromCookie != undefined) {
    if (consentFromDataLayer.indexOf(",2,") >= 0) {
      return true // analytics allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie == undefined) {
    return false // analytics not allowed
  }
}

4. Custom JavaScript – Marketing Cookies

  • Variable Type: Custom JavaScript
  • Name: Custom JS – Marketing Cookies Allowed
function() {
  var consentFromCookie = {{Cookie - Actual Cookie Consent}};
  var consentFromDataLayer = {{dlv - Active Consent Groups}};
  if (consentFromDataLayer != undefined && consentFromCookie == undefined) {
    if (consentFromDataLayer.indexOf(",4,") >= 0) {
      return true // marketing cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie != undefined) {
    if (consentFromCookie.indexOf(",4,") >= 0) {
      return true // marketing cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer != undefined && consentFromCookie != undefined) {
    if (consentFromDataLayer.indexOf(",4,") >= 0) {
      return true // marketing cookies allowed
    } else {
      return false
    }
  }
  if (consentFromDataLayer == undefined && consentFromCookie == undefined) {
    return false // marketing cookies not allowed
  }
}

5. Data Layer Variable – Active Consent

  • Variable Type: Data Layer Variable
  • Name: dlv – Active Consent Groups
  • Data Layer Variable Name: OnetrustActiveGroups
  • Data Layer Version: Version 2

6. Data Layer Variable – Engaging with the cookie consent banner

  • Variable Type: Data Layer Variable
  • Name: dlv – optanonAction
  • Data Layer Variable Name: optanonAction
  • Data Layer Version: Version 2

7. Data Layer Variable – Keeping track of consent

  • Variable Type: Data Layer Variable
  • Name: dlv – consentData for the record
  • Data Layer Variable Name: consentData
  • Data Layer Version: Version 2

Bonus. Google Analytics

While not strictly necessary a part of this guide, I highly recommend setting up your Google Analytics Settings as a variable within Google Tag Manager. It allows you to set anonymized IP addresses, which is a further requirement for compliance.

  • Variable Type: Google Analytics Settings
  • Name: Google Analytics
  • Tracking ID: enter your tracking ID here
  • Cookie Domain: auto
  • More settings -> Fields to Set
    • Field Name: anonymizeIp
    • Value: true

4. Add the necessary triggers to Google Tag Manager

We will create 10 necessary triggers (3 Pageview triggers, 4 blocking triggers, and 3 custom triggers)

1. Pageview – Functional Cookies

  • Trigger Type: Page view
  • Name: Pageview – All Pages – Functional Cookies Allowed
  • Trigger fires on: Some Page Views
    • Custom JS – Functional Cookies Allowed equals true

2. Pageview – Performance and Analytics Cookies

  • Trigger Type: Page view
  • Name: Pageview – All Pages – Analytics Tracking Allowed
  • Trigger fires on: Some Page Views
    • Custom JS – Performance and Analytics Tracking Allowed equals true

3. Pageview – Marketing Cookies

  • Trigger Type: Page view
  • Name: Pageview – All Pages – Marketing Cookies Allowed
  • Trigger fires on: Some Page Views
    • Custom JS – Marketing Cookies Allowed equals true

4. Blocking – Functional Cookies

  • Trigger Type: Custom Event
  • Name: Blocking – Functional Cookies are Not Allowed
  • Event Name: .*
  • Trigger fires on: Some Custom Events
    • Custom JS – Functional Cookies Allowed equals false

5. Blocking – Analytics Cookies

  • Trigger Type: Custom Event
  • Name: Blocking – Analytics Tracking is Not Allowed
  • Event Name: .*
  • Trigger fires on: Some Custom Events
    • Custom JS – Performance and Analytics Tracking Allowed equals false

6. Blocking – Marketing Cookies

  • Trigger Type: Custom Event
  • Name: Blocking – Marketing Cookies are Not Allowed
  • Event Name: .*
  • Trigger fires on: Some Custom Events
    • Custom JS – Marketing Cookies Allowed equals false

7. Blocking – Total Optout

  • Trigger Type: Custom Event
  • Name: Blocking – Total Optout
  • Event Name: .*
  • Trigger fires on: Some Custom Events
    • Custom JS – Functional Cookies Allowed equals false
    • Custom JS – Performance and Analytics Tracking Allowed equals false
    • Custom JS – Marketing Cookies Allowed equals false

8. Consent Data for the Record

  • Trigger Type: Custom Event
  • Name: Custom – Consent Data For The Record
  • Event Name: consentDataForTheRecord
  • Trigger fires on: All Custom Events
    • Custom JS – Functional Cookies Allowed equals false
    • Custom JS – Performance and Analytics Tracking Allowed equals false
    • Custom JS – Marketing Cookies Allowed equals false

9. Cookie Notification Closed

  • Trigger Type: Custom Event
  • Name: Custom – Cookie Notification Closed
  • Event Name: trackOptanonEvent
  • Trigger fires on: Some Custom Events
    • dlv – optanonAction matches RegEx (ignore case) Banner Accept Cookies|Banner Auto Close|Banner Close Button|Preferences Save Settings

10. Consent Updated

  • Trigger Type: Custom Event
  • Name: Custom – Optanon Consent Updated
  • Event Name: optanonConsentUpdated
  • Trigger fires on: All Custom Events
    • dlv – optanonAction matches RegEx (ignore case) Banner Accept Cookies|Banner Auto Close|Banner Close Button|Preferences Save Settings

5. Add the 4 necessary tags to Google Tag Manager

1. Cookie Consent

  • Tag Type: Custom HTML
  • Name: cHTML – Cookie Consent
  • Firing Trigger: Pageview – DOM ready
    • This trigger fires on: All DOM ready events
  • HTML: Copy and paste the Production CDN code from your OneTrust account here (see step 2.5 above if you’re unsure where to find it)

2. Consent Data for the Record

  • Tag Type: Custom HTML
  • Name: cHTML – Push To Data Layer – Consent Data For The Record
  • Firing Trigger: Custom – Optanon Consent Updated
<script>
  var timestamp = new Date().toString();
  var userAgent = navigator.userAgent;

  function check_ga() {
    if ({{Custom JS - Performance and Analytics Tracking Allowed}} === true) {
      if (typeof ga === 'function' && typeof ga.getAll === 'function') {
        var clientId = ga.getAll()[0].get('clientId');
        window.dataLayer.push({
          'event': 'consentDataForTheRecord',
          'consentData': "Consent groups: " + {{Cookie - Actual Cookie Consent}} + "; " + timestamp + "; " + clientId + "; " + userAgent
        });
      } else {
        setTimeout(check_ga, 500);
      }
    } else {
      window.dataLayer.push({
        'event': 'consentDataForTheRecord',
        'consentData': "Consent groups: " + {{Cookie - Actual Cookie Consent}} + "; " + timestamp + "; clientId is unavailable; " + userAgent
      });
    }
  }
  check_ga();
</script>

3. Consent Updated

  • Tag Type: Custom HTML
  • Name: cHTML – Push To Data Layer – Consent Updated
  • Firing Trigger: Custom – Cookie Notification Close
<script>
  setTimeout(function(){ 
    window.dataLayer.push({
    'event': 'optanonConsentUpdated'
    }) 
  
  }, 1000);
</script>

4. Actual Cookie Consent Active Groups

  • Tag Type: Custom HTML
  • Name: cHTML – Set Cookie – Actual Cookie Consent Active Groups
  • Firing Trigger: Custom – Optanon Consent Updated
<script>
 
 var cookieName = "actualOptanonConsent"; 
 var cookieValue = encodeURIComponent({{dlv - Active Consent Groups}});
 var expirationTime = 31536000; // One year in seconds
 expirationTime = expirationTime * 1000; // Converts expirationtime to milliseconds
 var date = new Date(); 
 var dateTimeNow = date.getTime(); 

 date.setTime(dateTimeNow + expirationTime);
 var expirationTime = date.toUTCString();
 document.cookie = cookieName+"="+cookieValue+"; expires="+expirationTime+"; path=/; domain=." + location.hostname.replace(/^www\./i, ""); 

</script>

Adding your own tags

  1. When adding new tags to your GTM container, make sure to:
    • Add a firing trigger
    • And a blocking trigger based on the user’s preferences.
      • I.e.: The tag fires when consent is updated and marketing cookies are allowed, but is blocked when marketing cookies are not allowed. (Example for the Facebook conversion pixel below)
  • Similarly, if you’re setting up Google Analytics through Google Tag Manager (and you should), you should set up the tag to fire when Analytics Tracking is allowed and blocked when Analytics Tracking is Not Allowed (see below)

Summary

And there you have it!
Let’s recap how it works:

  • The user visits your site and the only tag that fires is the cookie banner, asking for consent
  • On accepting all (or some) cookies, the user’s consent is stored as a first party cookie
  • The record of their consent is also collected
  • If they disallowed any of the cookie groups, this choice becomes a blocking trigger for the corresponding tags
  • The cookie groups they allowed can fire normally
  • At any point, the user can update their cookie preferences

I hope you like the new and improved cookie consent, that is both GDPR-compliant and relatively simple to setup if you follow the step-by-step guide.

Let me know in the comments how it worked for you!

[Guide] Setting up GDPR-compliant cookie consent without sacrificing data

I am not a lawyer and this guide is not meant as legal advice. Instead, consider it a best practice recommendation that lets your cookie consent form be compliant without overdoing it. There are many real-life examples of companies that are so over-compliant, they’re undoubtedly losing valuable analytics insights even though there is no legal need.

The aspects I was uncertain about I cross-checked with GDPR lawyers and they confirmed compliance. This guide is based on an article by Michael Wiegand and Ian Lurie, which I’ve improved and expanded to make certain things clearer.

The following guide shows you how to create a cookie consent form that asks EU visitors to opt into cookies.

For EU visitors, no cookies will be placed until they accept the cookie policy.

For non-EU visitors, the cookie consent form does not appear and cookies are placed without explicit consent.

Note: Your privacy policy must still contain an option for visitors to opt out.

13 Steps to GDPR-compliant cookie consent

0. Prerequisites: Google Tag Manager implemented

1. Sign up for a free account on ipinfo.io

(Limit: 1,000 IP lookups per day)

IP Info Account

2. Copy your Access Token

IP Info Access Token

3. Go to Google Tag Manager and setup a new tag

Type: Tag

Name: Country Lookup

Tag Type: Custom HTML

Content:

<script type="application/javascript">
function callback(json) {
dataLayer.push({'event': 'ipEvent', 'ipCountry': json.country}); 
}
</script>
<script type="application/javascript" src="https://ipinfo.io/?token=YOUR-TOKEN-HERE&callback=callback"></script>

Replace YOUR-TOKEN-HERE with your access token from step 2

Trigger: Fire on all pages

Country Lookup Table

4. Use the IP lookup tool to determine the visitor’s country and assign it as a data layer variable

Type: Data Layer Variable

Name: Country

Data Layer Variable Name: ipCountry

Country Data Layer Variable

5. With the country code in the data layer, we need to differentiate between EU and non-EU countries with the help of a new variable

Type: Variable

Name: In the EU

Variable Type: Lookup Table

Input Variable: {{Country}}

Input (each in its own row):
AT
BE
BG
CZ
DE
DK
EE
ES
FI
FR
GB
GR
HU
IE
IT
LT
LU
LV
MT
NL
PL
PT
RO
SE
SI
SK

Output (for each row): Yes

Country Lookup Table

6. When the visitor is from an EU country, we want to fire a specific trigger

Type: Trigger

Name: IP Event – EU

Trigger Type: Custom Event

Event name: ipEvent

This trigger fires on: In the EU contains Yes

Trigger - In the EU contains yes

7. We still need a cookie policy

Head over to https://cookieconsent.insites.com/download/# to create a free cookie policy.

Feel free to customise the 1. Position, 2. Layout, 3. Palette, and 4. Learn More link to your liking.

For 5. Compliance type, choose ‘Ask users to opt into cookies (Advanced)’.

A recommendation for the custom text:

By continuing to browse or by clicking “Accept Cookies,” you agree to the storing of first- and third-party cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts.

If you use this text, make sure to rename your ‘Accept button text’ to “Accept Cookies”.

Cookie Consent Script

8. Copy the code and head back to Google Tag Manager and create a new tag

Type: Tag

Name: Cookie Consent

Tag type: Custom HTML

Content: <your cookie code, example below>

<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
<script>
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": {
"background": "#efefef",
"text": "#404040"
},
"button": {
"background": "#3a99d9",
"text": "#ffffff"
}
},
"theme": "classic",
"type": "opt-in",
"content": {
"message": "By continuing to browse or by clicking “Accept Cookies,” you agree to the storing of first- and third-party cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts.",
"dismiss": " ",
"allow": "Accept Cookies",
"href": "YOUR-PRIVACY-POLICY-LINK-HERE"
}
})});
</script>

Firing Trigger: IP Event – EU

Cookie Consent Tag

9. Create a 1st Party Cookie variable

Type: Variable

Name: Cookie Consent

Variable type: 1st Party Cookie

Cookie Name: cookieconsent_status

1st Party Cookie Consent

10. We want to know when the user accepts the cookie policy, so create a new trigger

Type: Trigger

Name: cookie consent – allow

Trigger type: Click – All Elements

Trigger fires on: Click Classes contains cc-btn cc-allow

Cookie consent - allow

Note: ‘Click Classes’ is an out-of-the-box Tag Manager variable that is unselected by default.

Go to Variables -> Configure and mark the Checkbox if you can’t see it

Built in Variables

11. Now we need to create a tag that sends a data layer event and trigger

Type: Tag

Name: DL Event – Cookie Consent

Tag type: Custom HTML

Content:

<script>
dataLayer.push({'event': 'cookieConsent'});
</script>

Trigger (1):

cookie consent – allow

Trigger (2):

Type: Scroll Depth

Vertical Scroll Depths, Percentages, 25

This trigger fires on: All Pages

Cookie consent with scroll depth

Note: I’ve checked with the lawyers and they have confirmed that continuing to browse the website is considered a form of “active behaviour” and as such a form of consent. It’s also a method that Facebook recommends 

12. Create a new trigger

Type: Trigger

Name: event – cookie consent

Trigger type: Custom Event

Event name: cookieConsent

This trigger fires on: All Custom Events

Event - Cookie Consent

13. Lastly, we also want a trigger for all non-EU visitors

Type: Trigger

Name: Window Loaded – Not EU

Trigger type: Page View – Window Loaded

This trigger fires on: In the EU does not equal Yes

Window Loaded - Not EU

All other tags (analytics, tracking, pixels, etc.) will only fire if the visitor is outside the EU (Window loaded – not EU) or has accepted cookies (event – cookie consent).

Example for Google Analytics:

Google Analytics Snippet after cookie policy or for non EU visitors

14. Test!

Use Google Tag Manager’s Preview function (in incognito mode) to make sure the cookie banner shows up and the right tags fire at the right time.

I also recommend using a proxy service to test other country IPs.

Scripts fired on initial page visit and without allowing cookies or scrolling

After allowing cookies

Scripts fired after accepting cookies

Do you have any questions or did you use this guide?

Write me in the comments below!

Why Facebook’s ‘relevance score’ is useless – and what to use instead

The problem with ‘Relevance Score’

Have you ever prepared a Facebook ad campaign and were pretty sure it was going to do well? The target audience was defined, the creatives looked good, and the copy clearly communicated your value proposition.

But shortly after launching it, Facebook’s algorithm determines that your ads’ relevance score is a 2 or a 3 out of 10. Now what?

Frantically, you consider ways to improve your relevance score: Should you adjust the copy? Maybe the creatives are too bold? Or too boring? Perhaps try different placements or another ad type?

Turns out you’re optimizing for the wrong metric

When you’re starting a campaign, you don’t have significant amounts of useful data coming in (e.g. for conversion rates or CPAs). Especially during a campaign’s learning stage, costs, click through rates, and cost per actions should be taken with a grain of salt.

CPAs vary during learning stage

In comparison, the relevance score is determined after an ad receives 500 impressions, which is why many marketers like to use it as a leading indicator for performance.

The problem is that the relevance score is a poor proxy because it doesn’t measure what you think it does.

Even Facebook’s own definition of the metric is misleading:

Facebook's relevance score definition

Here’s the problem according to a source within Facebook: your ad’s relevance score is ranked relative to other advertisers targeting the same audience. 

“Relevance score aggregates various ad quality and relevance factors to give you an idea of how relevant your ads are to the people in your target audience compared to other ads targeting that same audience.”

So you’re not actually finding out how relevant your ad is to your audience.

Instead, your relevance score is a ranking against other companies (not even business competitors), who offer varying products or services, but happen to target the same audience.

Facebook even advises against trying to improve your relevance score:

“We recommend not focusing too much on raising your relevance score. Raising it is not directly tied to improved performance. Instead, we recommend using it to get a sense of your ad’s relevance, and then focusing on improving your targeting and creative. […] we don’t recommend making changes just to raise your relevance score.”

Two simplified scenarios that highlight the problem

Scenario A: Your (amazing) ad is up against another company who is offering free ice cream to the same target audience as you.

While I don’t know your business, I’m going to assume it’s not as attractive as free ice cream…

What will happen is that more people engage with the ice cream ad and convert on the website, indicating to Facebook that the ad was highly relevant.

In turn, because your ad won’t drive the same level of engagement, Facebook deems it less relevant and assigns a lower relevancy score.

Scenario B: Unfortunately, another ad you created is mediocre at best.

Unbeknownst to you, the other companies targeting the same audience have terrible ads that are even less engaging.

The result? Even though your ad isn’t engaging, it receives a high quality score because it still does better than the other companies.

If you then increase your ad spend as a result of this false positive, you’ll end up burning money.

The better alternative – positive and negative feedback

Fortunately, there are two metrics you can look at that provide a much better indicator of relevance: positive and negative feedback.

Positive feedback

Facebook Positive Feedback Description

Negative feedback

Facebook Negative Feedback Description

You see, these two feedback types actually inform you what actions people take regarding your ad.

So what actions directly impact these two scores?

 

Actions impacting positive feedback

Users can influence your ad’s positive feedback in four separate ways:

  1. Clicking the ad’s link/CTA
  2. Time spent on the ad (especially important if it’s a video)
  3. Reacting to the post
  4. Commenting on the ad

I want to clear up two misconceptions regarding reactions and comments.

First off, all types of reactions on a post are considered positive.

Types of Facebook Reactions

An ‘angry’ or ‘sad’ reaction is as equally positive as ‘love’.

Secondly, hiding comments left by people doesn’t have a negative impact.

Responding to comments (even negative ones) might lead to more interactions and therefore more positive feedback.

But even if you simply hide the comments left by others, regardless of their content, you won’t be penalized.

 

Actions impacting negative feedback

The one indicator on Facebook that will result in a higher negative feedback score is when people hide your ad.

Hiding ads on Facebook

Let’s consider why people might hide your ads.

The three main reasons why ads are hidden are:

  1. The user doesn’t consider the ad relevant to them
  2. They keep seeing the same ad (ad fatigue)
  3. They find the ad misleading or otherwise offensive

To prevent people from hiding your ad, make sure to target well, keep the creatives fresh, set a frequency cap, and don’t be offensive or misleading.

 

Where to find your positive and negative feedback

In your Facebook Ads Manager, scroll to the far right and hit the blue plus symbol.

Simply check the “Positive Feedback” and “Negative Feedback” boxes and hit “Apply”.

Adding-metrics-to-Facebook-ads-manager-v2

Positive and Negative Feedback Metrics on Facebook Ads Manager

Translating your feedback score

Unfortunately, the feedback your ad receives can only put it into one of three categories: Low, Medium, or High.

Which of these ads would you pause?

Low, Medium, High Feedback as an alternative to relevance score

As a general rule:

  • When the positive outweighs the negative, keep the ad running (and potentially scale)
  • When the negative outweighs the positive, pause the ad and revise
  • For the cases in between, where positive and negative feedback seem to be balanced, keep a close watch. An ad that has both highly positive and negative feedback might be polarizing, which can potentially work well

Positive-to-negative-feedback-table-v2

For the example campaigns above, the following picture emerges:

Ad evaluation as an alternative to relevance score

If this was the only information I had to go on, I would scale the budget for the first ad, pause ads 4 and 6, and focus on improving the others that I’ve marked as orange.

Key take-aways

  • Facebook’s relevance score doesn’t actually tell you how relevant your ad is
  • Relevance score is a value relative to other companies targeting the same audience and even Facebook recommends to not focus on improving it
  • A better indicator of your ad’s performance is the positive and negative feedback score it receives

Ultimately, positive and negative feedback is only an indicator of ad performance and shouldn’t be the end goal.

Your campaign objectives and business goals need to be the primary lenses through which you evaluate whether or not your advertising efforts are successful.

 

What experience do you have with Facebook’s relevance score? Have you overemphasized its importance in the past? Let me know in the comments!

GDPR causes passive unsubscribing – Last minute considerations

The flood before the deadline

The closer we get to the GDPR deadline of May 25th, the more emails flood inboxes asking users to opt-in. While the first emails about GDPR were fresh, companies that are still preparing to blast their email lists over the coming days are at a disadvantage: Most users can surmise what the email will be about and have likely stopped caring, especially if the email has a generic subject line such as “Update to Privacy Policy” or “Updates to Privacy Notice”. The result? Unopened and ignored emails.

Under normal circumstances (e.g. a monthly newsletter) a low opening rate wouldn’t be such a big deal – better luck next month! But companies that are trying to receive consent now only have a limited number of attempts. After that, an unopened email is the same thing as unsubscribing. Except now users don’t even have to actively press the “unsubscribe” button to make that decision, they can just ignore the email and passively unsubscribe.

 

Don’t be just another GDPR Email

Keep in mind that you won’t be the only GDPR email in your customer’s inbox. Many, many companies are also informing their customers that they’ve updated their privacy policy, cookie policy, are compliant with GDPR, etc. If you’re just another “Updates to our Terms and Conditions” email, you’ll have less customers opening your email, removing the chance of even opting in.

I recently worked with a company where GDPR was initially an afterthought for them, even though they were aware that they still needed to get proper consent. Their approach was to write a (generic) email, asking customers to opt-in, and be done with it. One size fits all and let’s hope for the best…

After some discussions, they fortunately agreed to test the subject line and copy with a random set of 5000 customers. The result of their generic subject line was an opening rate of ~16%.

The second test sought to break through the noise. Instead of the generic “Updates to x” subject line, we changed it to “Don’t miss out!”. After all, customers who wouldn’t opt-in were going to miss out on future emails. The result? An opening rate of ~24%.

While opening rates don’t tell the full story, consider the following: this client has an email list of ~250,000 customers in one country alone. If they had sent that original, generic email to all of them, only 40,000 would have had the chance to even opt-in.

The second test increased that amount to 60,000 – that’s 50% more customers that have the potential of opting in than before.

Three last minute considerations

  1. Test, test, test, and focus on the subject line. While you can wordsmith the copy as much as you like, the real impact will come from changing the subject line. In the previous example, 76-84% of customers didn’t open the email. An extra few percentage points can make a substantional difference.
  2. While GDPR is about the law, don’t make that an excuse to be boring. Liven up the subject line, include a personal touch, do something to break through the noise. And relate back to point 1 to see what works.
  3. Don’t lose focus. You’re doing this to build an email list of customers who have opted in to receiving communications from you. This means that you want high opening rates, high opt-in rates, and low unsubscription rates. It can be that simple, so check out the two examples below.

The good and the bad

From all the GDPR related emails I’ve received, I’ve picked two that exemplify what I mean above (albeit in different ways).

The Good – App Annie

While I was originally considered the multiple Call-To-Actions in the body to be distracting, at least two of them are actually the opt-in themselves. The other highlights the benefits of App Annie and reminds customers why they should opt in. Clear visuals, clear CTA.

The Bad – Heathrow

While you should of course allow users to unsubscribe, you shouldn’t make that their primary option… In the example below, Heathrow puts a big “Unsubscribe” button at the end of their email, making this the only CTA of the entire body. I wouldn’t be surprised if customers accidentally unsubscribed, given the strange approach of putting the unsubscription front and center.

A final note

While the GDPR deadline is looming, it’s not too late to get customer consent and the most out of your existing email list. Test multiple variations on subsets of your customer list to see what works best (various headlines, HTML vs text only, etc.). Don’t treat GDPR as an afterthought and once you’ve sent out that email, you’re done. By putting your customer first and breaking through the other email noise, you’ll be able to maintain as much of your list as possible.

[Guide] Determine your website’s share of voice relative to the competition

Every time someone enters a keyword or query for which your website appears in the search results, your site generates an impression. Google Search Console (GSK) tracks this information along with the number of clicks (among other things). By connecting this data with the total number of searches for the given queries, you’re able to establish your Share of Voice (SOV).

Before we get into the details, let me construct an example:
Your website ranks for 100 queries and in sum generated 5,000 impressions last month. These same 100 queries were searched for 50,000 times last month. Given that you appeared 5,000 times out of the possible 50,000, your share of voice is 10%.
This means that for all the keywords your website ranks for, your site appears on every 10th search.

Defining impressions differently

One thing to note is that Google’s definition of an impression is not based on the link actually being seen:
“Impressions – How many links to your site a user saw on Google search results, even if the link was not scrolled into view.”

Impressions are limited to the search page the user is on. So if your website ranks 9th for a certain query, your site generates an impression, regardless of whether the user sees it or not. If it ranks 14th though and the user doesn’t go to the second page of search results, it will not generate a search console impression.

Creating a quantified snapshot of how well your site performs and improves in terms of SEO

Determine your current share of voice, improve your SEO (e.g. increase site speed, optimize meta tags and descriptions, etc.) and take another snapshot of your share of voice in the future.
Because the delta compares you to the other sites ranking for the same queries, it provides a more holistic picture than just looking at your site’s performance in isolation.
You can filter your search console data in many ways (e.g. brand vs. non-brand), which you can use to focus your SEO efforts.

Step-by-step guide for calculating share of voice

Google Search Console Sidebar

Sign into your search console and navigate to Search Traffic -> Search Analytics

 

Next, check clicks and impressions and enter a data range. The date range should be a full calendar month (you’ll see why in a minute).

You should also enter a filter for the country that matches your primary market.

Filter for Google Search Console

If you wanted to split between branded and non-branded searches, you would set the appropriate filter in the ‘Queries’ option. But for this guide we’ll keep all queries unfiltered. Scroll down and hit ‘Download’ (we’ll choose CSV).

A limitation: Search Console only allows you to export 1000 queries, which usually suffices for most. If you do rank for more than 1000 queries, I suggest filtering your queries to make them mutually exclusive (e.g. brand vs. non-brand).

Open the file and transform the data to get all queries into a single column (if you’re using Excel, highlight column A, select the ‘Data’ ribbon, click ‘Text to Columns’, choose ‘Delimited’ and check ‘Comma’ as the only delimiter).

Sample data clicks and impressions

You should now have the Queries in column A, Clicks in column B, Impressions in column C, CTR in column D, and Position in column E.

 

Find the sum of all impressions (column D) and note this down. This is the number of impressions your queries generated over your selected time period. Do the same for clicks (column C).

 

In my example excerpt, you see 1,424 clicks and 4,723 impressions during the month of February, which is a CTR of 30.2%.

 

Using Google Keyword Planner for historic data

Next, select all queries and copy them to your clipboard, then head to Google Keyword Planner. Because we’re looking at historic data, we’ll choose ‘Get search volume data and trends’.

Google Keyword Planner Start

Paste the keywords and set the same country and date range you had in GSK. In our case it’s the United States for February 2018.

Paste keywords into Google Keyword Planner

Hit ‘Get search volume’ to see your results.

By looking at the graph below, you can see that for the queries we rank for, there were roughly 8,460 searches in February 2018.

February 2018 Search Volume

Share of Voice

Putting it together:
We now know how many searches there were (8,460) and of those, how many many impressions we generated (4,723).
This means our share of voice is 55.8%.

 

Next Steps

Now that you’ve established your share of voice, you should focus on increasing it. This might include overarching site speed improvements, or focusing on improving a number of pages that are at the top of page 2, in order to move them to the first page.
Taking another snapshot the next month will quantitatively show whether your efforts paid off.

Share of Voice in summary

  1. Sign into Google Search Console
  2. Choose your primary market and select a whole calendar month for the date range
  3. Download the data and determine your total impressions and clicks
  4. Copy all queries and go to Google Keyword Planner
  5. After selecting the same country and calendar month, paste your keywords to get total searches
  6. Divide your impressions by total searches to calculate your share of voice

That’s it! Good luck and let me know if you have any questions!

Growth vs Marketing – They’re not the same

Marketing is a Part of Growth

“What is growth?” is one of the most frequent questions I’m confronted with. “Is it just another term for marketing?”

Especially clients from more traditional companies try to fit the term into a silo they are familiar with. Given that it shares more elements with marketing than with other departments, it’s readily placed into the marketing drawer.

While marketing and growth go hand-in-hand, they are not identical and therefore not synonymous.

Traditional Marketing

If we consider traditional marketing, it serves two primary purposes:

  1. Raise awareness
  2. Acquire potential buyers

Subsequently, the focus of these activities is external. It’s about attracting individuals to you, but once they’ve arrived, they’re not part of marketing’s responsibility anymore.

Now it’s usually up to the product to convince users, or for sales to close the deal.

In growth, awareness and acquisition are externally focused

Growth goes beyond this external focus and augments it with an internal view.

How do your users behave when they’re on your site? How do they engage with your product? What actions do they take? When do they leave and why might they be leaving?

Growth looks at ways to activate and retain the users that visit your page. As such, it considers user engagement and seeks to continually improve their experience.

This is where roles become less clear and different departments begin overlapping.

Whose responsibility is this user experience (UX)? Product? Design? Marketing? Maybe disengaged users simply indicate a lacking product/market fit?

User Experience lies at the intersection between product marketing and design

The Need for Growth

Because user experience intersects product, design, and marketing, it needs to be coordinated. And given that growth focuses on activating and retaining previously acquired users, it’s in an ideal position to do so.

In Growth, activation and retention are internally focused

Growth doesn’t end at retention though. The final piece of the puzzle aims for users to become advocates and refer the product.

Primarily, this referral is likelier to happen if the entire funnel process was consistent and positive, which it should be as part of a holistic growth strategy.

Then, by facilitating various methods of sharing (and potentially incentivizing users to do so), viral loops form.

When users promote your product on your behalf, they start executing externally focused marketing and drive user acquisition.

Except that instead of a company trying to promote their product, now friends and family are advocates. This leads to better awareness, higher acquisition,  and ideally greater activation and retention rates, further driving the viral loop.

Viral loops drive awareness and acquisition anew

Ultimately, growth goes beyond driving awareness and acquiring users, by connecting the external activity to the internal focus of user activation, retention, and referral.

Users that experience a positive and consistent experience are more likely to refer others, thereby contributing to organic growth.