Designing for actual performance

This is how it goes. We put a load of shit into a single web page. This makes the page slow. Slow to load, slow to render. Slow.

Instead of getting rid of the shit, we blame the page refresh. There’s only one way to avoid the page refresh and that’s AJAX.

However, AJAX still needs to render new (parts of) screens. More crucially it still has to make a request to the server. That’s not all—there are penalties in using AJAX and it doesn’t necessarily result in faster experiences.

Firstly, making requests; handling different responses; traversing the document tree; and injecting HTML requires more code to be sent initally. This also needs to be evaluated and executed on the client.

Secondly, AJAX engineers away progressive rendering—which by the way—browsers do for free. To reinstate this functionality we resort to hacks that need yet more code. (Plus nobody uses the hack anyway.)

Thirdly, using AJAX means we have to give users another way of knowing that something is being loaded. Again, browsers give us a really good loading indicator for free.

With AJAX, we need to design and build a custom one. Not only is this more work and more code, but they are an inferior replacement for those provided by browsers.

This is because a browser’s indicator displays progress. That is, a user can tell how long until the request finishes. Custom loading indicators or spinners don’t display progress, so users get frustrated and click again causing further delays.

And custom indicators are unfamiliar as the look and placement differs from site to site. Conversely, the browser’s indicator appears in the same place and behaves the same for every website. This creates a familiar and informative experience that we should be loathed to forgo.

This doesn’t mean AJAX is bad. AJAX is useful depending on the situation as it avoids requesting the same assets over and over and may well render faster if updating a small segment of the page. But it’s not a solution to slow-loading pages. At best it patches over the problems that lie beneath.

The real problem is that we’ve designed something that can never be fast. Therefore the question really is how do we design for performance?

1. Simplify the interface

Do we need background videos, modal dialogs and social media buttons plastered everywhere? The answer from the people is a resounding no. The fastest feature is one we never build.

What about the way we design components? Hamburger menus, tabs, carousels, accordions, image galleries and expanding panels. All these things have one thing in common. They hide stuff.

Designers are obsessed with patterns that save space and look clean. A clean interface is good but not at the cost of clarity. If pages only contain the essential, then there should be little and maybe even nothing to hide.

Effort aside, designing fully responsive and inclusive components results in more code. More code that users rarely appreciate. After all, it slows the page down and requires the user to exert energy revealing the hidden content.

Heydon Pickering coined the seemingly satirical term Unprogressive Non-enhancement. This is how he explains it:

You take some structured content, which follows the vertical flow of the document in a way that everyone understands.

Which people traverse easily by either dragging their scroll bar with their mouse, or operating the keyboard using the up and down keys, or using the spacebar.

Or if they’re using a touch device, simply flicking backwards and forwards in that easy way that we’ve all become used to. What you do is you take that, and you fucking well leave it alone.

Letting things stack naturally is a good start. Not only does this embrace the way the web works—it makes for a remarkably inclusive and fast experience.

A fast experience, by the way, is a vital aspect of designing inclusive experiences. Some people don’t have fast connections and this shouldn’t cause exclusion.

Letting things stack isn’t our only option. We can chunk stuff across multiple pages. Once pages have little on them the page refresh ‘problem’ is no longer a problem. Pages are fast by design. Sometimes to the point where the page refresh is unnoticeable.

With regards to longish complex forms (or even shortish ones) there is One Thing Per Page which I’ve spoken and written about before.

But this pattern isn’t reserved for forms. A typical product page contains an image carousel, description, add to basket form, other details, shipping information, related products, ratings and comments. We can split this up too.

Most users don’t read every single aspect of a product, every time they visit the page. Instead give users a lightweight page and the choice to drill down further.

Give users one high-definition image without a carousel. Then let users click show all which would show all the images in a page of its own. No Javascript is needed either, saving yet more code. Let the content flow.

This uses the natural building blocks of the web as a form of progressive disclosure. Ultimately, this speeds things up drastically.

People on expensive data contracts benefit too. They can choose to see all the images by following the link or they can wait until they are connected to WI-FI.

It’s easier to share page content or imagery this way too. Sending users to a page where most of the stuff is hidden is problematic.

Tabs: If stuff is hidden by default, how important is its existence on this page? Or if the tab contains a small amount of content just show it. Or consider putting it on a separate page.

Modal dialogs are often misused. All too often they contain too much content that would be better off as a new page anyway. This improves performance and doesn’t break the back button (like a dialog often does).

2. Reduce code

By simplifying the interface we’ve already reduced the code by literally not writing any. But coding the remaining features brings forth a different opportunity—to write less code.

Create lean HTML

Sometimes developers use the wrong element. A <button> is half the size of <div role="button" tabinidex="0"> and doesn’t require script to make it accessible again.

Divitus is an antiquated buzzword but its still prevalent today. We often use additional elements unncessarily. These make the request longer to make, parse and render. We should be able to justify the existence of every element.

Classitus is the use of extra classes. Extra classes may decrease the size of CSS but they signifcantly increase the size of HTML.

Ensuring HTML is small is important. Unlike CSS, it can’t be easily cached. This is because it’s likely to contain personalised and dynamic content. This in turn encourages the misuse of AJAX.

Simplify your design system

Government Digital Services design simple websites. Most things are left aligned and stack naturally. In this case we may be able to avoid CSS classes altogether which further proves that a simple interface is a performant one.

4. Backend stuff

Use Command Query Responsibility Segregation to make database queries fast (good for sites that have more reads than writes).

Use a Content Delivery Network for your static resources. And cache HTML and AJAX responses too.

Cache assets with long expiry dates so that users don’t have to download assets again.

Place scripts at the bottom and use async and defer attributes. Async is good for completley independent scripts that can run later like analytics.

Use HTTPS over HTTP2 with Gzip compression. Gzip, by the way, works better with a well-designed and consistent design system—the more HTML is repeated the better the compression.

Use preload and prefetch where appropriate. Addy Osmani says preload resources you have high-confidence will be used in the current page. Prefetch resources likely to be used for future navigations across multiple navigation boundaries.