Website performance guidelines

Abstract

When I optimize for frontend performance I always keep in mind that the user, his expectations and his experience must always be the focus of anything I do. Every little optimization should be advantageous to the user at one point or the other or have otherwise useful beneficial effects. This guide will not try to scholarly tell you how to write your code but instead give you some ideas on what is possible and how to achieve it.

There are numerous steps you can take to optimize your site and most of the time people will more or less list you the things Google PageSpeed recommends, things you most likely are already familiar with. I do for sure not ignore these totally valid points but divided them into two tiers to which I add my own tiers for further optimization.

I would really recommend to plan ahead in case of performance optimizations. It is far easier to implement all possible measures when developing a site than afterwards!

Tier 1: The basics

So here you go with your usual list of very basic Google PageSpeed recommendations:

  • reduce server response time
  • leverage browser caching
  • enable transfer compression
  • avoid redirects
  • optimize/minify image assets
  • optimize/minify/reduce HTML
  • minify/concat/reduce CSS
  • minify/concat/reduce JavaScript

I try to achieve as many of these measures as possible with one exception: I did test HTML minification only to find out that it does not help as much as I would have expected. Beside that it can become problematic because sometimes whitespace in code actually has an influence on browser rendering which might lead to unexpected results when minified.

One thing on which I would like to go a bit into details is "leverage browser caching". Often enough you will read that you should configure your server to send long expiration headers for all static assets. Although this is totally valid it is missing an important point: Whenever you change a static asset you really want the browser to refresh it - which it will not do without clearing the cache if you only follow this directive blindly. So you really want some type of versioning within your asset URLs to ensure they get refreshed by the browser.

What I usually do is to include a semver compatible version numbers within my URLs which get removed from the URL via .htaccess. So the URL will change, the browser will reload the asset but you do not have to work with new directories for each and every version or revision.

Tier 2: Code reordering

Most of your JavaScript should not affect the rendering of the page in the browser but only add additional non-critical function to it. If you put this kind of JavaScript in the head of your document you need to know that it will, although not having any optical influence, still block the rendering of your page for the user.

So the simple recommendation is to move any such JavaScript from the head of the document towards the end of the body. Another nice little side effect is that any JavaScript included at the end of the body will not have to be wrapped in something like jQuery's "$(document).ready".

Tier 3: Lazyload images

Although greatly contributing to the overall look and the impression of your site, images do not have a greater influence on what I call "perceived loading time". Simply put, the user is used to wait a bit longer for images to load and the number of concurrent requests is limited depending on your browser anyway. So why not benefit from this trained perception?

Tier 4: Defer loading of modules

A module might be any combination of JavaScript, CSS and HTML that represents an independent, self-contained section within your page that is in its very nature not critical for the user. A good example would be a box containing the latest GitHub activities as shown on my personal site. Although this module contributes to the page it is definitely not critical for the user to be present at load.

By using a script loader like require.js you can easily separate that functionality from your code and load it in a non-blocking fashion after the page is already rendered. Beside being beneficial for the perceived loading time this step will most likely greatly help you to keep your code modular, dry and easily maintainable which is why I greatly recommend it!

Tier 5: Prioritize above the fold content

This is also a recommendation by Google PageSpeed but definitely one of the more advanced. Its main aim is to enable us to inline CSS and JavaScript that is critical for everything the user will see when he initially loads a page so that anything required for rendering is contained in a single request. This single request should ideally stay below 14kb to allow a page rendering time of 1 second on mobile devices and connections.

There are tools available that you can include in your hopefully existing Grunt or Gulp build process which automate this task but I would recommend to manually plan your CSS and JavaScript accordingly. This will perhaps be a bit bigger than the automated per page results but it will also help you personally, to improve your thinking and the results will be far more versatile.

Tier 6: Further tinkering

In addition to the other tiers there are still more ways you might think about. So tier 6 is your as well as my personal tier to think out of the box and be creative :)

Just to give you an example of what you could put here personally I will list two things I already did in the past:

Back in 2011 I did develop a microsite for an online raffle of one of our customers. The site contained many alpha transparent packshots of their products. So many, in fact, that one of the pages weighted 4mb in the end which I thought was totally intolerable. The images related all shared one thing: If they had no alpha transparency they would all be way smaller as JPG than PNG. So I developed a solution (Qoopido.js shrinkimage1 module) which loads color and alpha information separately via JavaScript and combines both via HTML5 canvas without increasing the number of requests. I will not go into details further but it reduced the total weight by about 70% and got used in many projects I developed afterwards.

Just a couple of days ago I implemented localStorage caching (Qoopido.js asset2 module) on my own site for above the fold CSS, JavaScript as well as some module CSS. The above the fold CSS and JavaScript get inlined by the server on first request. The asset module will afterwards request the original files via XHR, store them in localStorage and set corresponding cookies for each cached asset. On the next load the server will read these cookies and only output empty containers into which the localStorage content will get injected. This reduced the load time of repeated requests by another 250ms (which equates to 1/4 of the total loading time before), a big amount for mobile connections.

Verdict

Whatever you pick up from this guideline, I can only recommend you to be creative, think out of the box and finally: Always put the user in the focus of your optimizations. Not only the user will profit, your customer and you will as well. A clear win-win-win situation so to speak :)