A case study in how to improve page load speed

We used one of Littledata's own benchmarks to identify an issue with mobile page load speed and fix the underlying problems. Here's how we did it. Page load speed benchmark Using benchmarking in the Littledata app, we compared our website with 72 other marketing services sites benchmarked in December. (Our app lets you compare web performance by sector). Paying attention to both mobile and desktop experiences, we looked at things like the average delay before page content appears and the average time before full mobile page load. Overall we are above average, but against one benchmark - delay before page content appears - we are close to the median. That means 35 sites in our sector manage faster page load speeds. As website professionals, we can't let that rest! We care because Google cares about speed. If our landing pages take a long time before first 'paint' - before the content is visible - then more visitors will bounce or pick another article to answer their questions. If we invest time into writing articles like this one on our analytics blog, we should also invest time in making sure they are fast to load, and fast for Google to crawl and index. Here's how we acted on the benchmark data. [subscribe heading="Benchmark your site" button_text="sign up"] How fast were pages loading? We used the excellent PageSpeed Insights tool to pinpoint what was going wrong with the pages loading. Our original speed on mobile For mobile browsers, where traffic increasingly comes from, we were in the bottom 10% for speed, and below average for desktop. That sucks, and was definitely penalising our organic search rankings. Fixes to improve page load speed 1. Removing unnecessary JavaScript modules As a 'web app' built using Meteor, our pages are heavy with Javascript - effectively the whole site is pre-loaded with the first page. This means a module used by only one page can slow down ALL pages. By visualising the bundles for our Meteor App we found 2 very large modules which were blocking page load for all pages: the D3 library, used to visualise our industry category coverage, and the autoform package used for a single signup form. Using the clever dynamic imports feature in Meteor were were able to cut those modules from the landing page, and half the size of the Javascript bundle loaded from 1.6Mb to 0.8Mb. 2. Using Webp images for Chrome and compatible browsers Google prefers you to use the more efficient .webp image filetype, which is typically half the file-size of older .png or .jpeg filetypes. We found our chosen image server, Cloudinary, includes a simple feature to automatically chose the best file type for the browser it detects. Very quick fix! 3. Lazy-loading of landing page images Even if the images are efficiently served, trying to fetch all the images for a long-form landing page takes time - and is wasteful for users who only read the first few paragraphs. Instead, we can wait until the user scroll down before images are loaded for that section. This results in a small flicker as the user scrolls the first time, but faster initial load time. 4. Caching pages from our blog Wordpress is a great CMS, but the PHP + MySQL stack it uses is not the fastest for generating the pages. Luckily there are some great cache plugins, including WP Fastest Cache which we set up. What this does is save a rendered HTML file on the WP server, and only refresh the cache when the post is edited or new comments are added. 5. Cleaning up unused CSS and HTML templates Like all sites that have evolved their design and content over a few years, ours had lots of redundant code that no-one had spring-cleaned. Generally it's easier with older projects just to leave code, but for Meteor apps particularly it can slow down every page (see fix 1). So a new front-end developer starting was a good chance to rewrite templates from scratch, and chuck out the trash. The page load speed improvements Those 5 improvements, plus a database upgrade, let to some pretty good speed improvements - above average for mobile and top 2% for desktop speed! Even a few days after the release we saw a boost in the visibility of our landing pages in Google Search. Looking at Littledata's benchmarks for February, included in the newly released mobile vs desktop benchmarks, we can see the delay before content appears has dropped from 2.6 to 1.8 seconds (down by 40%). And the desktop speed is now in the top 10% of all sites at 1.7 seconds. The only area we still lack is the mobile page speed, so maybe we will look at some AMP versions in the next sprint. Ready to benchmark your site? Littledata's free plans include a Google Analytics connection and free benchmarks in ecommerce, engagement, marketing and site speed.

2019-03-13

Littledata's Shopify connection is now using gtag and GTM data layer

Littledata’s Shopify app is updating to use Google’s latest tracking code library. This will simplify your setup for Google Ads and speed up your site. Google’s ‘global site tag’ or gtag has been live for a year now and is stable for Littledata to adopt. In version 5 of our tracking script we now use gtag for all the events sent to Google Analytics. The advantages of gtag are: Integrates with Google Ads out of the box – no need for separate Google Ads conversion tracker Smaller Javascript library = faster page load times Future proof for using Google Optimize In addition, we are now using the standard 'data layer' format used by Google Tag Manager. This will make it easier for all you hackers to extend Littledata's tracking and use GTM with the enhanced ecommerce data layer, and easily create tags for marketing platforms like: Facebook, Criteo, etc. [subscribe] We've also moved to using the default ecommerce event naming recommended by Google. For example, the event category 'Ecommerce' is now 'ecommerce' (lower case) and event action 'Add to cart' is now 'add_to_cart' (snake case). If you have goals or reports based on the old event names you may need to update them. One final change is that we're only sending page views to GA when the page is not hidden in the browser. Certain advertising campaigns, including SnapChat ads, preload your webpages to provide a faster experience for users, but this skews your analytics with lots of low-grade visits who didn't actually 'see' your landing page. How to update the script If your store already has our tracking script installed, just click on the in-app notification to update. Not a Littledata user yet? If you're struggling with implementing Google Ads conversion tracking or GTM for a Shopify store, check out our Google Analytics connections for Shopify and Shopify Plus stores. Let our app fix your tracking, so you can get back to business!

2019-02-12

How to stop Google Tag Manager being hacked

In two high-profile data breaches this year – at Ticketmaster and British Airways – over half a million credit cards were stolen via a compromised script inserted on the payment pages. Update 8/7/19: British Airways was fined a record £183m over this data breach, under new GDPR regulation. They are contesting the fine. Google Tag Manager is a powerful tool which enables you to insert any script you want onto pages of your website, but that power can used against you by hackers if you're not careful – and below we’ll look at how to stop GTM being a security risk on your payment pages. Firstly, how did the hackers get the card details from these sites? And how is it relevant to GTM on your site? Security firm RiskIQ has traced the breach to a compromised Javascript file which skimmed the card details from the payment form. So when a user entered their credit card number and security code on BritishAirways.com (or their mobile app) those details were posted to a third party server, unknown to British Airways or the customer. This is a high-scale equivalent of placing a skimming devices on an ATM, which reads one card at a time. In Ticketmaster’s hack the script was one loaded from a chatbot vendor on their site, Inbenta. Inbenta claims not even to have been aware the script was used on payment pages. The changes to the script were subtle: not breaking any functionality, and in BA’s case using a domain ‘baway.com’ which looked somewhat authentic. To protect your site against a similar attack you obviously need to lock down accounts used by your developers to change scripts in the page source code, but you also need to secure GTM – which can be used to deploy such scripts. We have a few rules at Littledata to help reduce risks in using tag management on payment pages: 1. Use pixels over custom JavaScript tags on payment pages You probably need a few standard tags, such as Google Analytics, on payment pages but try to avoid any custom scripts which could possibly skim card details. Many non-standard tags use JavaScript only to create the URL of a tracking pixel – and it is much safer (and faster) to call the tracking pixel directly. Contact the vendor to find out how. (Littledata's Shopify app even removes the need to have any script on the payment pages, by hooking into the order as it's registered on Shopify's servers) 2. Avoid loading external JavaScript files in GTM Many vendors want you to load a file from their server (e.g. myvendor.com/tracking.js) from GTM, so they can update the tracking code whenever they want. This is flexible for them, but risky for you. If the vendor gets hacked (e.g. with Inbenta above) then you get compromised. It’s less risky to embed that script directly in GTM, and control version changes from there (although a fraction slower to load the page). Of particular risk is embedding a tag manager within a tag manager – where you are giving the third party rights to publish any other scripts within the one tag. Don’t do that! [subscribe] 3. Lock down Edit and Publish rights on GTM Your organisation probably has a high turnover of contract web developers and agencies, so have you checked that only the current staff or agencies have permission to edit and publish? It's OK to have external editors use 'workspaces' for version control in GTM, but ideally someone with direct accountability to your company should check and Publish. 4. Blacklist custom JavaScript tag on the payment pages You can set a blacklist from the on-page data layer to prevent certain types of tags being deployed on the payment pages. If you have a GTM container with many users, this may be more practical that step 3. 5. Remove tags from old vendors There are many thousands of marketing tools out there, and your company has probably tried a few. Do you remove all the tags from vendors when you stop working with them? These are most at risk of being hacked. At Littledata we run a quarterly process for marketing stakeholders opt-in tags they still need for tracking or optimisation. 6. Ensure all custom JavaScript tags are reviewed by a developer before publishing It can be hard to review minimised JavaScript libraries, but worth it for payment pages if you can’t follow rules 1 and 2. If you’re still worried, you can audit the actual network requests sent from payment pages. For example, in Chrome developer tools, in the 'Network' tab, you can inspect what requests sent out by the browser and to what servers. It’s easy for malicious code to hide in the patchwork of JavaScript that powers most modern web experiences, but what is harder to hide is the network requests made from the browser to external servers (i.e. to post the stolen card information out). This request to Google Analytics is fine, but if the domain of a request is dubious, look it up or ask around the team. Good luck, and keep safe with GTM!

2018-11-24

Categorising websites by industry sector: how we solved a technical challenge

When Littledata first started working with benchmark data we found the biggest barrier to accuracy was self-reporting on industry sectors. Here’s how we built a better feature to categorise customer websites. Google Analytics has offered benchmarks for many years, but with limited usefulness since the industry sector field for the website is often inaccurate. The problem is that GA is typically set up by a developer or agency without knowledge or care about the company’s line of business - or understanding of what that industry sector is used for. To fix this problem Littledata needed a way to categorise websites which didn’t rely on our users selecting from a drop-down list. Google Analytics has offered benchmarks for many years, but with limited usefulness since the industry sector field for the website is often inaccurate. [subscribe] The first iteration: IBM Watson NLP and a basic taxonomy Our first iteration of this feature used a pre-trained model as part of IBM Watson’s set of natural language APIs. It was simple: we sent the URL, and back came a category according to the Internet Advertising Bureau taxonomy. After running this across thousands of websites we quickly realised the limitations: It failed with non-English websites It failed when website homepage was heavy with images rather than text It failed when the website was rendered via Javascript Since our customer base is growing most strongly outside the UK, with graphical product lists on their homepage, and using the latest Javascript frameworks (such as React), the failure rate was above 50% and rising. So we prioritised a second iteration. The second iteration: Extraction, translation and public APIs The success criteria was that the second iteration could categorise 8 sites which the first iteration failed with, and should go on to be 80% accurate. We also wanted to use mainly public APIs, to avoid maintaining code libraries, so we broke the detection process into 3 steps: Extracting meaningful text from the website Translating that text into English Categorising the English text to an IAB category and subcategory The Watson API seemed to perform well when given sufficient formatted text, at minimal cost per use, so we kept this for step 3. For step 2, the obvious choice was Google Translate API. The magic of this API is that it can detect the language of origin (with a minimum of ~4 words) and then provide the English translation. That left us focussing the development time on step 1 - extracting meaningful text. Initially we looked for a public API, and found the Aylien article extraction API. However, after testing it out on our sample sites, it suffered from the same flaws as the IBM Watson processing: unable to handle highly graphical sites, or those with Javascript rendering. To give us more control of the text extraction, we then opted to use a PhantomJS browser on our server. Phantom provides a standard function to extract the HTML and text from the rendered page, but at the expense of being somewhat memory intensive. Putting the first few thousand characters of the website text into translation and then categorisation produced better results, but still suffered from false positives - for example if the text contained legal-ease about data privacy it got categorised as technical or legal. We then looked at categorising the page title and meta description, which any SEO-savvy site would stuff with industry language. The problem here is that the text can be short, and mainly filled with brand names. After struggling for a day we hit upon the magic formula: categorising both the page title and the page body text, and looking for consistent categorisation across the two. By using two text sources from the same page we more than doubled the accuracy, and it worked for all but one of our ‘difficult’ websites. This hold-out site - joone.fr - has almost no mention of its main product (diapers, or nappies), which makes it uniquely hard to categorise. So to put it all the new steps together, here’s how it works for our long-term enterprise client MADE.com's French-language site. Step 1: Render the page in PhantomJS and extract the page title and description Step 2: Extract the page body text, remove any cookie policy and format Step 3: Translate both text strings in Google Translate Step 4: Compare the categorisations of the title vs page body text Step 5: If the two sources match, store the category I’m pleased that a few weeks after launching the new website classifier we have found it to be 95% accurate. Benchmarking is a core part of our feature set, informing everything that we do here at Littledata. From Shopify store benchmarks to general web performance data, the improved accuracy and deeper industry sector data is helping our customers get actionable insights to improve their ecommerce performance. If you’re interested in using our categorisation API, please contact us for a pilot. And note that Littledata is also recruiting developers, so if you like solving these kind of challenges, think about coming to join us!

2018-10-16

Six challenges in developing a Shopify integration

At the start of 2017 Littledata released its first Shopify app. A year on, here are my observations on the technical challenges we’ve overcome. This week we're at Shopify Unite in Toronto, and it's no surprise that their app ecosystem continues to grow. We chose Shopify as our first platform partner due to their open APIs, quality documentation and enthusiasm from other developers. Much of that has been as expected, but to help all of you looking to build your own Shopify app I’ll share some of our learnings on the hidden challenges. Littledata's Shopify app makes it a one-click process for stores to set up for Enhanced Ecommerce tracking in Google Analytics, and then get actionable insights based on the Google Analytics data. It has to hook into Shopify orders and products, as well and modify the store's theme and process ongoing transactions. 1. Handling re-installs gracefully The great advantage of Shopify’s app store over, say, Magento marketplace, is that any store admin can install and pay for an app with a couple of clicks. The disadvantage is that stores can be as quick to uninstall as install. Store admins may start, realise they don’t have permissions, time or energy to continue and roll back to try again later in the day. Since our app inserts a snippet into the store’s theme layout (see point two below), uninstalling removes the web-hooks we set up but does not remove the inserted snippet. When a store re-installs our app has to work out what state they were in when they uninstalled (audit, test mode or live), whether the script snippet is still there and what settings have been changed in the meantime. It took us a few months to get a handle on all the possible user flows, and we’ve found automated end-to-end tests to really speed up running through the different scenarios. In our Meteor framework we use Chimp [link] to run tests through Selenium on localhost and on our staging server. We've also found it essential to track our own stats of 'installs + activations' (including the date of first install and time to finally uninstall) rather than relying on the Shopify Partner stats of uninstalls and installs, which can hide the detail in between. 2. Working with script tags The other side-effect of making apps easy to install is that you can assume the early-adopter stores who will try your app already have lots of other installs. Shopify recommends using the Script Tag API to handle scripts linked to the app, so that when a store uninstalls your app it also removes any client-side scripts from the store. Unfortunately, in early tests we found the load latency to be unacceptably high: on some stores, only 50% of the page load events were getting fired before the user moved off the page. So plan B was add a snippet to the store theme, and then load this snippet at the top of the <body> element on all the layout templates. This has worked much more predictably, except when theme editors remove the snippet reference without enquiring what the Littledata app does (see our fifth challenge). [subscribe] 3. Charge activation vs authorisation Now a very simple gotcha. In our first month we had around 60 installs at a flat price of $20/month, but apparently no revenue. After investigation we found we had not activated the recurring charges after the store admin had authorised them. Doh! We're still not sure why an app would want to have authorised charges which are not activated -- seems like over-engineering on Shopify's side -- but luckily it was easy to correct without asking for more user permissions. 4. Tracking adds-to-cart The first version of our app tried to run the script when customers got to the ‘/cart’ page of a store. The problem here is that many stores have AJAX or ‘mini’ carts where customers can checkout without every visiting the cart page. We looked to trigger the script before the user got to the cart the page, but this appeared to run too many risks of interfering with the customer actually adding the item. Our final solution has been to poll the Shopify store for the current cart, and see if products have been added (or removed) since we last polled (and stored the previous cart contents in memory). This is somewhat inefficient, as it requires continuous network activity to grab the cart JSON from Shopify, but we’ve reduced the network requests to one every 4 seconds – judging that customers are very unlikely to add a product and checkout in less than 4 seconds. This cart polling has proved more reliable across different store templates. 5. Integrating with other Shopify apps I mentioned that early-adopter stores tend to have lots of other apps: and those apps have loyal customers who push to make Littledata's app to work their chosen app (not just vanilla Shopify). The challenge is that most of these app development companies run a very Agile process, constantly changing how their app works (hopefully to improve the experience for store owners). An integration that worked a few months ago may no longer work. We've found the best solution to be open developer-to-developer communications, via a Slack guest channel. Having the developers implementing the features on each side talk to each other really cuts down the delays caused by a well-meaning project manager slightly misinterpreting the requirement. 6. Handling ongoing updates As tested improved client-side tracking scripts, we needed to update the script in the store theme (see point 2 above). This creates a small risk for the store, as there is no UAT or test environment for most stores to check before going live with the new script. The store theme may also get edited, creating new layout templates where the Littledata snippet is not loaded. In the first version of our app we tried to update and re-insert the latest Littledata snippet automatically on a weekly cycle. However, once we reached hundreds of active installs this became unmanageable and also opaque for the store admins. In the latest version we now allow store admins to UPGRADE to the latest script, and then we check all the correct Google Analytics events are being fired afterwards. Giving the end user control of updates seems a better way of maintaining trust in our brand and also removing risk: if the update goes wrong, it’s quicker for us to alert the store owner on how to fix. Conclusion I’m still sure we made the right choice with Shopify as a platform, as their APIs, partner support and commercial traction are all number one in the ecommerce world. But I hope that by sharing some of the hidden challenges in developing Shopify integrations, we can all build better apps for the community. Have you built something for the Shopify app store? Are there development problems you’ve encountered which I haven’t shared here? PS. Are you a developer interested in joining an innovative analytics company? We're hiring in multiple locations!

2018-05-07

Littledata at Codess

I was proud to be invited by Microsoft to speak at their Codess event in Bucharest last week to encourage women in software. We talked about how Littledata uses Meteor, Node and MongoDB to run scalable web applications; slightly controversial because none of these are Microsoft technologies! The event was well run and well attended, so I hope it inspires some of the attendees to start their own projects...or to join Littledata (we're hiring).

2017-10-17

Cross Domain tracking for Eventbrite using Google Tag Manager (GTM)

Are you using Eventbrite for event registrations? And would you like to see the marketing campaign which drove that event registration correctly attributed in Google Analytics? Then you've come to right place! Here is a simple guide to adding a Google Tag Manager tag to ensure the correct data is sent to Eventbrite to enable cross-domain tracking with your own website. Many thanks to the Lunametrics blog for their detailed solution, which we have adapted here for GTM. Before this will work you need to have: links from your site to Eventbrite (including mysite.eventbrite.com or www.eventbrite.co.uk) the Universal Analytics tracking code on both your site and your Eventbrite pages. only have one GA tracking code on your own site - or else see the Lunametrics article to cope with this 1. Create a new tag in GTM Create a new custom HTML tag in GTM and paste this script: [code language="javascript"] <script> (function(document, window) { //Uses the first GA tracker registered, which is fine for 99.9% of users. //won't work for browsers older than IE8 if (!document.querySelector) return; var gaName = window.GoogleAnalyticsObject || "ga" ; // Safely instantiate our GA queue. window[gaName]=window[gaName]||function(){(window[gaName].q=window[gaName].q||[]).push(arguments)};window[gaName].l=+new Date; window[gaName](function() { // Defer to the back of the queue if no tracker is ready if (!ga.getAll().length) { window[gaName](bindUrls); } else bindUrls(); }); function bindUrls() { var urls = document.querySelectorAll("a"); var eventbrite = /eventbrite\./ var url, i; for (i = 0; i < urls.length; i++) { url = urls[i]; if (eventbrite.test(url.hostname) === true) { //only fetches clientID if this page has Eventbrite links var clientId = getClientId(); var parameter = "_eboga=" + clientId; // If we're in debug mode and can't find a client if (!clientId) { window.console && window.console.error("GTM Eventbrite Cross Domain: Unable to detect Client ID. Verify you are using Universal Analytics."); break; return; } url.search = url.search ? url.search + "&" + parameter : "?" + parameter; } } } function getClientId() { var trackers = window[gaName].getAll(); return trackers[0].get("clientId"); } })(document, window); </script> [/code]   2. Set the tag to fire 'DOM ready' Create a new trigger (if you don't have a suitable one) to fire the tag on every page at the DOM ready stage.  We need to make sure the Google Analytics tracker has loaded first. 3. Test the marketing attribution With the script working you should see pageviews of the Eventbrite pages as a continuation of the same session. You can test this by: Opening the 'real time' reporting tag in Google Analytics, on an unfiltered view Searching for your own site in Google Navigating to the page with the Eventbrite link and clicking on it Looking under the Traffic Sources report and checking you are still listed as organic search after viewing the Eventbrite page Need more help? Comment below or get in touch!   Get Social! Follow us on LinkedIn, Twitter, and Facebook and keep up-to-date with our Google Analytics insights.

2017-02-07

Tracking customers in Google Analytics

If your business relies on customers or subscribers returning to your site, possibly from different devices (laptop, smartphone, etc.) then it’s critical you start tracking unique customers rather than just unique visitors in Google Analytics. By default, Google Analytics tracks your customers by browser cookies. So ‘Bob’ is only counted as the same visitor if he comes to your site from the same browser, but not if he comes from a different computer or device. Worse, if Bob clears his cookies or accesses your site via another mobile app (which won't share cookies with the default browser) then he'll also be counted as a new user. You can fix this by sending a unique customer identifier every time your customer signs in. Then if you send further custom data about the user (what plan he / she is on, or what profile fields they have completed) you can segment any of the visits or goals by these customer attributes. There are 2 possible ways to track registered users: Using Google Analytics’ user ID tracker By storing the clientId from the Google cookie when a new user registers, and writing this back into the tracker every time the same user registers In both cases, we also recommend sending the user ID as a custom dimension. This allows you segment the reports by logged in / not logged in visitors. Let's look at the pros and cons. Session stitching Tracking customers involves stitching together visits from different devices into one view of the customer. Option 1, the standard User ID feature, does session stitching out the box. You can optionally turn ‘session unification’ on which means all the pageviews before they logged in are linked to that user. With option 2 you can stitch the sessions, but you can't unify sessions before the user logs in - because they will be assigned a different clientId. So a slight advantage to option 1 here. Reporting simplicity The big difference here is that with option 1 all of the user-linked data is sent to a separate 'registered users' view, whereas in options 2 it is all on the same view as before. Suppose I want a report of the average number of transactions a month for registered vs non-registered visitors. With both options, I can only do this if I also send the user ID as a custom dimension - so I can segment based on that custom dimension. Additionally, with option 1 I can see cross-device reports - which is a big win for option 1. Reporting consistency Once you start changing the way users are tracked with option 2 you will reduce the overall number of sessions counted. If you have management reports based on unique visitors, this may change. But it will be a one-time shift - and afterwards, your reports should be stable, but with a lower visit count. So option 1 is better for consistency Conclusion Option 1 - using the official user tracking - offers a better route to upgrade your reports. For more technical details on how this tracking is going to work, read Shay Sharon’s excellent customer tracking post. Also, you can watch more about customer tracking versus session tracking in this video. Have any questions? Comment below or get in touch with our team of experts!   Get Social! Follow us on LinkedIn, Twitter, and Facebook and keep up-to-date with our Google Analytics insights.

2016-12-06

Get the Littledata analytics app

Start your free 14-day trial

Learn More