We all like fast websites, and doing website optimisation work is fun. This blog post will give you five principles that you can use to build blazingly fast websites.
Why optimise?
But before we begin we need to consider: why optimise in the first place? Or rather, how do you convince your boss/product-owner/client/spouse that you need to spend the time/money to make your website faster?
Take a look at some research Walmart did to convince themselves to speed up their website. The research shows: the slower your site, the lower your sales. Customers simply get bored waiting for your slow site to load and go elsewhere.
Also, Google now takes website speed into account when calculating your site’s search engine ranking. An analysis clearly shows, all other things being equal, the slower your website speed, the lower its ranking in the Google search results.
Finally, Facebook, frustrated with the slowness of the mobile web, has implemented Facebook Instant Articles. This feature makes certain supported articles in the Facebook news feed load instantly, giving a much better user experience, and getting users to spend (even) more of their time in the Facebook app.
So, without further ado, here are five principles you can use to increase sales on your site, boost your search ranking and increase user engagement.
1. Measure everything
It isn’t useful to micro-optimising something that was really fast to begin with. When optimising anything, it’s important to focus on the slowest part of the application first. Clearing such bottlenecks gives the most speed benefit for the least amount of work. There are a bunch of tools available to help you find the bottlenecks.
Analyse and profile
An easy way to measure how long a website takes to load is to look at the red “Load” measure at the bottom of the Chrome Network tab. That tells you how long it takes for your page to fully load.
If you want to go a bit deeper, the Chrome dev tools also have an excellent timeline profiling tool available.
You can also run YSlow and PageSpeed Insights on your website to get a report of optimisations you can make.
The JSPerf website is a useful tool to benchmark variations in Javascript code snippets and across different browsers.
Facebook’s XHProf is a great tool for profiling PHP, and it can create nice graphs of the call stack showing bottlenecks highlighted in red and yellow. XHProf also gives you the ability to compare multiple runs, so you can see if and how your optimisation efforts are helping make the code run faster.
Log slow requests
Configuring your web server to show you slow requests is a great way to zero-in on performance problems to focus your optimisation efforts. Here are some things you can do.
Log the request speed by adding the “%D” parameter to your Apache access log. Logging request speed adds a tiny bit of performance overhead, but gives you great visibility on what aspects of your website are running really slowly. Add this to your Apache configuration:
/usr/local/apache/conf/httpd.conf
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D" combined
Then you can, for example, get a real-time view of all http requests taking longer than 0.5 seconds by running:
tail -f /var/log/apache/access.log | awk '$(NF) > 500000'
You can also enable the slow query log in your MySQL/MariaDB database to gain some insight into performance problems stemming from slow database queries.
Load test
A website might run fine when doing single requests, but fall over as soon you hit it with multiple concurrent requests. That’s why it is important to load test your website. Load testing tools give you a nice report of how your website performs under load.
Two load testing tools I like are Siege (guide) and Apache Bench (guide). Siege is better at simulating real-world requests patterns while Apache Bench is easy-to-use and comes pre-installed on almost any server.
2. Ignore your job title
Minimalist content and design
Are you a content author, information architect, or designer? Some of the biggest performance gains or losses result from decisions made by those disciplines. So, what to do if that’s not your job title? Easy, insert yourself into those discussions anyway and try to make a difference.
For example, try loading these two websites: Google and Dick Smith Electronics. Which takes longer to load? Oh, it’s not a fair comparison you say? The Dick Smith website has a whole bunch of huge banners, images, annoying pop-overs, lots of CSS and slow JS while the Google website is just one image and a search box. Yeah, my point exactly.
Scale horizontally
Are you an Infrastructure Architect? If you have a high-traffic website, then ignore your lack of title, put on your Infrastructure Architecture hardhat and make some changes. You can gain huge performance under load by horizontally scaling out your server stack.
Use a load balancer, introduce multiple web servers, and create multiple database read replicas. It takes some doing to get right and costs a bit more money, but, in circumstances where a single server is struggling and you’ve done all you can to optimise your code, it’s time to scale horizontally and add more servers.
Hint: adding more servers is so much easier if you are in a cloud hosting environment like Amazon Web Services (AWS).
Spend money, not time
Are you a CEO? Do you have budget approval to purchase third-party software that costs thousands of dollars?
Because most of us don’t have such budget approval, we hesitate to use high-quality commercial software components. For example, you might spend months implementing a charting solution for your website, or you could buy Highcharts. A month of your salary almost certainly adds up to more than the price of Highcharts, and yet I’ve seen developers struggle for months to implement a home-grown charting solution.
Don’t be a penny wise and pound foolish developer. Make use of paid software components and make your project come in early and under-budget.
3. Push to the left
Cache database queries
Take a look at this five-tier website architecture.
The database is the slowest component and it is way on the right. You can get massive speed-gains by pushing content from the right to the left of the architecture. The fastest possible database request is a request that is cached by the application server and never actually reaches the database.
Use Nginx (other reverse proxy)
Using a reverse proxy server such as Nginx allows you to cache your static content at the reverse-proxy tier. Nginx is really good at efficiently serving static content (and yes, Apache can be almost as fast if you disable all the features that make Apache so good at serving dynamic content, such as switching Apache from prefork-mode to worker-mode and turning off htaccess file parsing; but doing those things defeats its purpose). Let Nginx handle the static requests and use Apache to handle your complex dynamic request, and you get the best of both worlds.
Use a CDN
A Content Delivery Network (CDN) takes your content and distributes it to end-points all over the world. It does this because the speed of light is too slow. Or rather, light is pretty fast, but light doesn’t travel through optical fibre at the actual speed of light, and there are a bunch of routers, repeaters and stuff that slow the light down. The end result is that a request from New Zealand to Europe has a latency of over half a second and there is nothing you can do to speed that up. Well, if you’re Elon Musk you can launch your own fleet of 4000 Low Earth Orbit satellites but, chances are, you’re not Elon Musk, so use a CDN instead (if you are Elon Musk, please give me Tesla).
Some CDNs that have endpoints in New Zealand are: Fastly, Akamai, Incapsula, CloudFlare.
If I were choosing a CDN right now, I’d choose CloudFlare. But your experience and requirements may vary. So, please do your own research.
Let the browser do the work
Taking the principle to the extreme, you can push your content to the far left. Push it all the way into the browser.
You can set caching headers so the browser caches content appropriately and doesn't ask for the same content again. Use a cachebuster GET parameter to invalidate content, rather than trying to invalidate using ETags, content expiry settings, etc. You can even use this technique for image references within CSS.
Do as much processing as possible in JavaScript. For example, an online calculator that does its processing in JavaScript is so much faster than one that needs to ask the server for the result. Doing stuff in the browser takes a load off your server and gives a better user experience to boot.
4. Deliver fast content
Pre-compute
A static request is much faster than a dynamic request. If you can pre-compute the result of a dynamic request and save it as static content, that greatly improves performance. You might generate a JSON file onAfterWrite of a SilverStripe DataObject, rather than generating it dynamically on every request. You might statically cache page HTML, so you can serve a page without invoking any PHP. Or you might de-normalise some database tables when joins are too slow.
Minify and combined
Take your JavaScript and CSS and remove all the whitespace, comments and line breaks. Then rewrite all your variable names to make them shorter. Now take all your files and combined them into a single file. A single http request for a large file is quicker than multiple requests for several smaller files, so combining files makes your site load faster.
Luckily, you don’t need to do any of these optimisation by hand. There are tools to help you. SilverStripe has the built-in Requirements::combine_files feature. If you want to do more fine-grained optimisation, then you should look into using a build system such as Grunt, Gulp or Webpack.
Compress
Compressing textual content is an easy way to speed things up. All browsers support compressed content (even IE6 with SP2+). Test your site to see if it is already using compressing. If not, then you can easily enable gzip compression by vomiting up some punctuation into (adjusting) your Apache configuration.
Inline content
Consider the following scenario: you have a web page that displays a list of a few thousand items from a server database by loading in a JSON file (a file that you have cleverly pre-generated using the “Pre-compute” principle). You load your page, then the document.ready event fires off an AJAX request to load the JSON file and display it on the front-end, using JavaScript to page through it (cleverly using the “Let the browser do the work” principle). All is well, right? No!
You can speed this page up by having that data already loaded into the HTML of the page. That way the browser doesn’t need to wait for any events and doesn’t need to do any more HTTP requests. The data is right there so that as soon as the JavaScript is loaded, it can display the content.
You can do this by inlining the content directly into the HTML of the page. Be careful not to do this with libraries that might already be cached in the browser, or with files that you are reusing across multiple pages of the site. But, if you have content that sits in a single file and isn’t used anywhere else on the site, and you can efficiently write the file’s content into the HTML of your page, that gives you a nice optimisation. Be sure to json_encode your inlined content to protect against cross-site scripting attacks.
Know your framework
Spend some time getting to know your framework. You need to know your tools like the back of your hand. Know all the little tricks and implementation details that let you make smart decisions and write efficient code.
For example: know that jQuery.find() is faster than jQuery.children(), because it is a native browser method; know that AngularJS ng-if is faster that ng-show, because it completely removes elements from the DOM instead of just hiding them; and know that SilverStripe <% cached %> partial caching is essential for efficiently generating a menu.
5. Cheat
Great performance isn’t so much about actual raw speed, but more about the perception of speed. If you can make something seem really fast without doing the hard work of actually making it fast, then, by all means, do that. I promise I won’t tell anyone you cheated.
Load in the background
Do you know how Facebook Instant Articles work? They simply pre-load the article before you actually click on it, using up all your bandwidth in the process. You can do that, too. If you have a single-page app, you can use analytics to measure what your user is most likely to click next and load that content before they actually click. The result: instant UI, without any actual increase in speed.
Similarly, you can load additional content as a user scrolls down a long webpage, resulting in so-called infinite scrolling. The old-fashion way would be to display a “next” button. But users typically don’t click onwards to the next page, so infinite scrolling can improve your user-experience in certain cases.
Queue
Sometimes you can respond to the user instantly while doing the actual processing in the background.
This happens, for example, when you send an email in Gmail, or try to order an iPhone on a launch day. Gmail tells you “message sent”, but actually just queues the message for sending at some later time. Similarly, Apple takes your credit card details when you order, puts them into a giant message queue, processes them later, then sends you a message if your payment validation fails, asking you to try again.
Both examples show how you can achieve the illusion of speed by using a queue to immediately respond to the user and delay some slow processing until later. That’s great user experience.
Run less code
Sometimes you don’t need that massive framework. Sometimes a few lines of PHP are all you need. You don’t need to include thousands of PHP files just to increment a counter, give an autocomplete result, or redirect to a different URL.
When you find yourself writing a feature that doesn’t need all the power of SilverStripe, cheat a little: betray your beloved framework, go behind its back and write a cute little stand-alone PHP file.
Generalise the optimisation
You’ve done all this work, applying all these principles to create a perfectly optimised website. What are you going to do for your next project? Do it all again? Please, don’t do that. Instead, figure out a way to generalise all the great optimisation work you do, so you can take advantage of it for all future projects.
With SilverStripe Platform, we’ve done just that. We have taken a bunch of these principles and generalised them into a Platform service. You get the benefit of all our hard work for an affordable monthly subscription.
Post your comment
Comments
Posted by Hammad, 03/08/2016 5:17pm (8 years ago)
Posted by Hammad, 31/07/2016 3:27am (8 years ago)
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/
Posted by Dan Hensby, 04/11/2015 11:08pm (9 years ago)
No one has commented on this page yet.
RSS feed for comments on this page | RSS feed for all comments