One day I was looking for a checklist for front end development, something to re-use for improving our job and I found this article on Smashing magazine very clear and useful to measure and monitor performance.

It’s a very long article with a lot of insights and resources, so I’m going to highlight here the most important topics. Of course, we are not the IT team so we can’t have full control, but we have the duty to give to the user the best experience or he will buy our products on other ecommerce websites. Gomez studied online shopper behavior and found that 88% of online consumers are less likely to return to a site after a bad experience. The same study found that “at peak traffic times, more than 75% of online consumers left for a competitor’s site rather than suffer delays.”

I still suggest you to read the entire article on Smashing (for designers). Developer MUST READ IT.

Establish a performance culture.

In many organizations, front-end developers know exactly what common underlying problems are and what loading patterns should be used to fix them. However, as long as there is no alignment between dev/design and marketing teams, performance isn’t going to sustain long-term. Why performance matters? Because it has high impacts on business metrics. Case studies and experiments on wpostats.com can prove that:

Goal: Be at least 20% faster than your fastest competitor.

If you prioritize early on which parts are more critical, and define the order in which they should appear, you will also know what can be delayed. Ideally, that order will also reflect the sequence of your CSS and JavaScript imports, so handling them during the build process will be easier. Also, consider what the visual experience should be in “in-between”-states, while the page is being loaded (e.g. when web fonts aren’t loaded yet).

Lara Hogan’s guide on how to approach designs with a performance budget can provide helpful pointers to designers and both Performance Budget Calculator and Browser Calories can aid in creating budgets.

> FOR DESIGNER: HERE THE ENTIRE BOOK DESIGNING FOR PERFORMANCE OF LARA HOGAN. It seems that all chapters are available for free.

Choose the right metrics.

Not all metrics are equally important. Study what metrics matter most to your application: usually it will be related to how fast you can start render most important pixels (and what they are) and how quickly you can provide input responsiveness for these rendered pixels.

Below are some of the metrics worth considering:

First Meaningful Paint (FMP, when primary content appears on the page),

Hero Rendering Times (when the page’s important content has finished rendering),

Time to Interactive (TTI, the point at which layout has stabilized, key webfonts are visible, and the main thread is available enough to handle user input — basically the time mark when a user can tap on UI and interact with it),

Input responsiveness (how much time it takes for an interface to respond to user’s action),

Perceptual Speed Index (measures how quickly the page contents are visually populated; the lower the score, the better),

Your custom metrics, as defined by your business needs and customer experience.

The slides available here are very useful to understand these metrics.

> For developers: do you know how to measure TTI in the network tab of chrome dev tool?

Gather data on a device representative of your audience.

Facebook introduced the 2g Tuesday to increase visibility and sensitivity of slow connections. This made me thinking about the differences of connection speeds around the world and I found this article about it very interesting. Anyway, the right approach is test, test, test! There are a lot of tools. One very good is Lighthouse and it’s inside the tab Audits in chrome dev tool.

Setting Realistic Goals

100-millisecond response time, 60 fps.

The RAIL, a user-centered performance model gives you healthy targets.

Response is all about input latency: the lag between the finger touching the glass and the resulting pixels hitting the screen. To respond responsively, we would: provide feedback in less than 100 milliseconds after initial input.

after initial input. Ideally, the feedback would show the desired state. But if it’s going to take a long time, then a loading indicator or coloring for the active state will do. The main thing is to acknowledge the user so that they don’t start wondering whether their tap was registered. Animation: each frame of animation should be completed in less than 16 milliseconds, thereby achieving 60 FPS ( 1 second ÷ 60 = 16.6 milliseconds ). How can you measure this? Tab Rendering in the Chrome dev tool, inside the console drawer, then click FPS meter. Idle: To use idle time wisely, the deferring work is grouped into blocks of about 50 milliseconds, so you can still respond to user interactions within the 100-millisecond response window, and not be stuck in the middle of a 2-second template rendering. Load: To load pages quickly, we aim to deliver the first meaningful paint in under 1 second. Beyond this, the user’s attention starts to wander and the perception of dealing with the task at hand is broken. Reaching this goal requires prioritizing the critical rendering path and often deferring subsequent non-essential loads to periods of idle time (or lazy-loading them on demand).

SpeedIndex < 1250, TTI < 5s on 3G, Critical file size budget < 170Kb.

Speaking about Time To Interactive, it’s a good idea to distinguish between First Interactive and Consistency Interactive to avoid misunderstandings down the line. The former is the earliest point after the main content has rendered (where there is at least a 5-second window where the page is responsive). The latter is the point where the page can be expected to always be responsive to input. Tools such as Calibre, SpeedCurve and Bundlesize can help you keep your budgets in check, and can be integrated into your build process.



Defining The Environment

Progressive enhancement.

Keeping progressive enhancement as the guiding principle of your front-end architecture and deployment is a safe bet. Design and build the core experience first, and then enhance the experience with advanced features for capable browsers, creating resilient experiences.

Choose a strong performance baseline.

JavaScript has the heaviest cost of the experience, next to web fonts blocking rendering by default and images often consuming too much memory. With the performance bottlenecks moving away from the server to the client, as developers, we have to consider all of these unknowns in much more detail.

Keep in mind that on a mobile device, you should be expecting a 4×–5× slowdown compared to desktop machines. Mobile devices have different GPUs, CPU, different memory, different battery characteristics. Parse times on mobile are 36% higher than on desktop. So always test on an average device — a device that is most representative of your audience.

Build Optimizations

Set your priorities straight.

Run an inventory of all of your assets (JavaScript, images, fonts, third-party scripts and “expensive” modules on the page, such as carousels, complex infographics and multimedia content), and break them down in groups.

> Set up a spreadsheet. Define the basic core experience for legacy browsers (i.e. fully accessible core content), the enhanced experience for capable browsers (i.e. the enriched, full experience) and the extras (assets that aren’t absolutely required and can be lazy-loaded, such as web fonts, unnecessary styles, carousel scripts, video players, social media buttons, large images). We published an article on “Improving Smashing Magazine’s Performance,” which describes this approach in detail.

Consider using the “cutting-the-mustard” pattern.

Albeit quite old, we can still use the cutting-the-mustard technique to send the core experience to legacy browsers and an enhanced experience to modern browsers. Be strict in loading your assets: Load the core experience immediately, then enhancements, and then the extras. Note that the technique deduces device capability from browser version, which is no longer something we can do these days.

For example, cheap Android phones in developing countries mostly run Chrome and will cut the mustard despite their limited memory and CPU capabilities. That’s where PRPL patterncould serve as a good alternative. Eventually, using the Device Memory Client Hints Header, we’ll be able to target low-end devices more reliably. At the moment of writing, the header is supported only in Blink (it goes for client hints in general).

> Since Device Memory also has a JavaScript API which is already available in Chrome, one option could be to feature detect based on the API, and fallback to “cutting the mustard” technique only if it’s not supported

Parsing JavaScript is expensive, so keep it small (This is more about web apps but always interesting).

When dealing with single-page applications, you might need some time to initialize the app before you can render the page. Look for modules and techniques to speed up the initial rendering time (for example, here’s how to debug React performance, and here’s how to improve performance in Angular), because most performance issues come from the initial parsing time to bootstrap the app.

JavaScript has a cost, but it’s not necessarily the file size that drains on performance. Parsing and executing times vary significantly depending on the hardware of a device. On an average phone (Moto G4), a parsing time alone for 1MB of (uncompressed) JavaScript will be around 1.3–1.4s, with 15–20% of all time on mobile spent on parsing. With compiling in play, just prep work on JavaScript takes 4s on average, with around 11s before First Meaningful Paint on mobile. Reason: parse and execution times can easily be 2–5x times higher on low-end mobile devices.

An interesting way of avoiding parsing costs is to use binary templates that Ember has recently introduced for experimentation. These templates don’t need to be parsed.

That’s why it’s critical to examine every single JavaScript dependency, and tools like webpack-bundle-analyzer, Source Map Explorer and Bundle Buddy can help you achieve just that. Measure JavaScript parse and compile times. Etsy’s DeviceTiming, a little tool allowing you to instruct your JavaScript to measure parse and execution time on any device or browser. Bottom line: while size matters, it isn’t everything. Parse and compiling times don’t necessarily increase linearly when the script size increases.

Are you using tree-shaking, scope hoisting and code-splitting?

Tree-shaking is a way to clean up your build process by only including code that is actually used in production and eliminate unused exports in Webpack. Also, you might want to consider learning how to write efficient CSS selectors as well as how to avoid bloat and expensive styles. Feeling like going beyond that? You can also use Webpack to shorten the class names and use scope isolation to rename CSS class names dynamically at the compilation time.

Code-splitting is another Webpack feature that splits your code base into “chunks” that are loaded on demand. Also, consider using preload-webpack-plugin that takes routes you code-split and then prompts browser to preload them using <link rel="preload"> or <link rel="prefetch">, attributes that we can also apply without using webpack.

> Where to define split points? By tracking which chunks of CSS/JavaScript are used, and which aren’t used. Umar Hansa explains how you can use Code Coverage from Devtools to achieve it.

Take advantage of optimizations for your target JavaScript engine.

Study what JavaScript engines dominate in your user base, then explore ways of optimizing for them.

Make use of script streaming for monolithic scripts. It allows async or defer scripts to be parsed on a separate background thread once downloading begins, hence in some cases improving page loading times by up to 10%. Practically, use <script defer> in the <head> , so that the browsers can discover the resource early and then parse it on the background thread.

Caveat: Opera Mini doesn’t support script deferment, so if you are developing for India or Africa, defer will be ignored, resulting in blocking rendering until the script has been evaluated

Client-side rendering or server-side rendering?

In both scenarios, our goal should be to set up progressive booting: Use server-side rendering to get a quick first meaningful paint, but also include some minimal necessary JavaScript to keep the time-to-interactive close to the first meaningful paint.

Do you constrain the impact of third-party scripts?



With all performance optimizations in place, often we can’t control third-party scripts coming from business requirements. Third-party-scripts metrics aren’t influenced by end user experience, so too often one single script ends up calling a long tail of obnoxious third-party scripts, hence ruining a dedicated performance effort. To contain and mitigate performance penalties that these scripts bring along, it’s not enough to just load them asynchronously (probably via defer) and accelerate them via resource hints such as dns-prefetch or preconnect .

As Yoav Weiss explained in his must-watch talk on third-party scripts, in many cases these scripts download resources that are dynamic. The resources change between page loads, so we don’t necessarily know which hosts the resources will be downloaded from and what resources they would be.

What options do we have then? Consider using service workers by racing the resource download with a timeoutand if the resource hasn’t responded within a certain timeout, return an empty response to tell the browser to carry on with parsing of the page. You can also log or block third-party requests that aren’t successful or don’t fulfill certain criteria.

Another option is to establish a Content Security Policy (CSP)to restrict the impact of third-party scripts, e.g. disallowing the download of audio or video. The best option is to embed scripts via <iframe> so that the scripts are running in the context of the iframe and hence don’t have access to the DOM of the page, and can’t run arbitrary code on your domain. Iframes can be further constrained using the sandbox attribute, so you can disable any functionality that iframe may do, e.g. prevent scripts from running, prevent alerts, form submission, plugins, access to the top navigation, and so on.

For example, it’s probably going to be necessary to allow scripts to run with <iframe sandbox="allow-scripts"> . Each of the limitations can be lifted via various allow values on the sandbox attribute (supported almost everywhere), so constrain them to the bare minimum of what they should be allowed to do. Consider using Safeframe and Intersection Observer; that would enable ads to be iframed while still dispatching events or getting the information that they need from the DOM (e.g. ad visibility).

Assets Optimizations (FOR DESIGNERS AND DEVELOPERS)

Are images properly optimized?

As far as possible, use responsive images with srcset , sizes and the <picture> element. While you’re at it, you could also make use of the WebP format (supported in Chrome, Opera, Firefox soon) by serving WebP images with the <picture> element and a JPEG fallback (see Andreas Bovens’ code snippet) or by using content negotiation (using Accept headers).

> Sketch natively supports WebP, and WebP images can be exported from Photoshop using a WebP plugin for Photoshop. Other options are available, too.

> Use the Responsive Image Breakpoints Generator.

On Smashing Magazine, they use the postfix -opt for image names — for example, brotli-compression-opt.png ; whenever an image contains that postfix, everybody on the team knows that the image has already been optimized.

Take image optimization to the next level.

When you’re working on a landing page on which it’s critical that a particular image loads blazingly fast, make sure that JPEGs are progressive and compressed with Adept, mozJPEG (which improves the start rendering time by manipulating scan levels) or Guetzli, Google’s new open source encoder focusing on perceptual performance, and utilizing learnings from Zopfli and WebP. The only downside: slow processing times (a minute of CPU per megapixel). For PNG, we can use Pingo, and SVGO or SVGOMG for SVG.

Every single image optimization article would state it, but keeping vector assets clean and tight is always worth reminding. Make sure to clean up unused assets, remove unnecessary metadata and reduces the amount of path points in artwork (and thus SVG code).

These optimizations so far cover just the basics. Addy Osmani has published a very detailed guide on Essential Image Optimization that goes very deep into details of image compression and color management. For example, you could blur out unnecessary parts of the image (by applying a Gaussian blur filter to them) to reduce the file size, and eventually you might even start removing colors or turn the picture into black and white to reduce the size even further. For background images, exporting photos from Photoshop with 0 to 10% quality can be absolutely acceptable as well.

What about GIFs? Well, instead of loading heavy animated GIFs which impact both rendering performance and bandwidth, we could potentially use looping HTML5 videos, yet browser performance is slow with <video> and, unlike with images, browsers do not preload <video> content. At least we can add lossy compression to GIFs with Lossy GIF, gifsicleor giflossy.

Good news: hopefully soon we’ll be able to use <img src=".mp4"> to load videos, and early tests show that img tags display 20× faster and decode 7× faster than the GIF equivalent, in addition to being a fraction in file size.

Not good enough? Well, you can also improve perceived performance for images with the multiple background imagestechnique. Keep in mind that playing with contrast and blurring out unnecessary details (or removing colors) can reduce file size as well. Ah, you need to enlarge a small photo without losing quality? Consider using Letsenhance.io.

> This article is the bible. It explain how the browsers load images, how to optimize them (for designers) and how to use them (for developers).

Delivery Optimizations

Do you limit the impact of JavaScript libraries, and load them asynchronously?

When the user requests a page, the browser fetches the HTML and constructs the DOM, then fetches the CSS and constructs the CSSOM, and then generates a rendering tree by matching the DOM and CSSOM. If any JavaScript needs to be resolved, the browser won’t start rendering the page until it’s resolved, thus delaying rendering. As developers, we have to explicitly tell the browser not to wait and to start rendering the page. The way to do this for scripts is with the defer and async attributes in HTML.

In practice, it turns out we should prefer defer to async (at a cost to users of Internet Explorer up to and including version 9, because you’re likely to break scripts for them). Also, as mentioned above, limit the impact of third-party libraries and scripts, especially with social sharing buttons and <iframe> embeds (such as maps). Size Limit helps you prevent JavaScript libraries bloat: If you accidentally add a large dependency, the tool will inform you and throw an error. You can use static social sharing buttons (such as by SSBG) and static links to interactive maps instead.

Are you lazy-loading expensive scripts with Intersection Observer?

If you need to lazy-load images, videos, ad scripts, A/B testing scripts or any other resources, you can use the shiny new Intersection Observer API that provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport. Basically, you need to create a new IntersectionObserver object, which receives a callback function and a set of options. Then we add a target to observe.

You could even take it to the next level by adding progressive image loading to your pages. Similarly to Facebook, Pinterest and Medium, you could load low quality or even blurry images first, and then as the page continues to load, replace them with the full quality versions, using the LQIP (Low Quality Image Placeholders) technique proposed by Guy Podjarny.

Opinions differ if the technique improves user experience or not, but it definitely improves time to first meaningful paint. We can even automate it by using SQIP that creates a low quality version of an image as an SVG placeholder. These placeholders could be embedded within HTML as they naturally compress well with text compression methods. In his article, Dean Hume has described how this technique can be implemented using Intersection Observer.

Browser support? Decent, with Chrome, Firefox, Edge and Samsung Internet being on board. WebKit status is currently in development. Fallback? If we don’t have support for intersection observer, we can still lazy load a polyfill or load the images immediately. And there is even a library for it.

> In general, it’s a good idea to lazy-load all expensive components, such as fonts, JavaScript, carousels, videos and iframes. You could even adapt content serving based on effective network quality. Network Information API and specifically navigator.connection.effectiveType (Chrome 62+) use RTT and downlink values to provide a slightly more accurate representation of the connection and the data that users can handle. You can use it to remove video autoplay, background images or web fonts entirely for connections that are too slow.

Do you push critical CSS quickly?

To ensure that browsers start rendering your page as quickly as possible, it’s become a common practice to collect all of the CSS required to start rendering the first visible portion of the page (known as “critical CSS” or “above-the-fold CSS”) and add it inline in the <head> of the page, thus reducing roundtrips. Due to the limited size of packages exchanged during the slow start phase, your budget for critical CSS is around 14 KB.

If you go beyond that, the browser will need additional roundtrips to fetch more styles. CriticalCSS and Critical enable you to do just that. You might need to do it for every template you’re using. If possible, consider using the conditional inlining approach used by the Filament Group.

Are you saving data with Save-Data ?

Especially when working in emerging markets, you might need to consider optimizing experience for users who choose to opt into data savings. The Save-Data client hint request headerallows us to customize the application and the payload to cost- and performance-constrained users. In fact, you could rewrite requests for high DPI images to low DPI images, remove web fonts and fancy parallax effects, turn off video autoplay, server pushes or even change how you deliver markup.

The header is currently supported only in Chromium, on the Android version of Chrome or via the Data Saver extension on a desktop device.

> Finally, you can also use service workers and the Network Information API to deliver low/high resolution images based on the network type.

Do you warm up the connection to speed up delivery?

Use resource hints to save time on dns-prefetch (which performs a DNS lookup in the background), preconnect (which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background), prefetch (which asks the browser to request a resource) and preload (which prefetches resources without executing them, among other things).

Most of the time these days, we’ll be using at least preconnect and dns-prefetch , and we’ll be cautious with using prefetch and preload ; the former should only be used if you are very confident about what assets the user will need next (for example, in a purchasing funnel). Notice that prerender has been deprecated and is no longer supported.

Note that even with preconnect and dns-prefetch , the browser has a limit on the number of hosts it will look up/connect to in parallel, so it’s a safe bet to order them based on priority (thanks Philip!).

In fact, using resource hints is probably the easiest way to boost performance, and it works well indeed. When to use what? As Addy Osmani has explained, we should preload resources that we have high-confidence will be used in the current page. Prefetch resources likely to be used for future navigations across multiple navigation boundaries, e.g. Webpack bundles needed for pages the user hasn’t visited yet.

Addy’s article on Loading Priorities in Chrome shows how exactly Chrome interprets resource hints, so once you’ve decided which assets are critical for rendering, you can assign high priority to them. To see how your requests are prioritized, you can enable a “priority” column in the Chrome DevTools network request table (as well as Safari Technology Preview).

For example, since fonts usually are important assets on a page, it’s always a good idea to request the browser to download fonts with preload . You could also load JavaScript dynamically, effectively lazy-loading execution. Also, since <link rel="preload"> accepts a media attribute, you could choose to selectively prioritize resources based on @media query rules.

A few gotchas to keep in mind: preload is good for moving the start download time of an asset closer to the initial request, but preloaded assets land in the memory cache which is tied to the page making the request. It means that preloaded requests cannot be shared across pages. Also, preload plays well with the HTTP cache: a network request is never sent if the item is already there in the HTTP cache.

Hence, it’s useful for late-discovered resources, a hero image loaded via background-image, inlining critical CSS (or JavaScript) and pre-loading the rest of the CSS (or JavaScript). Also, a preload tag can initiate a preload only after the browser has received the HTML from the server and the lookahead parser has found the preload tag. Preloading via the HTTP header is a bit faster since we don’t to wait for the browser to parse the HTML to start the request. Early Hintswill help even further, enabling preload to kick in even before the response headers for the HTML are sent.

Beware: if you’re using preload , as must be defined or nothing loads, plus Preloaded fonts without the crossorigin attribute will double fetch.

Have you optimized rendering performance?

Isolate expensive components with CSS containment — for example, to limit the scope of the browser’s styles, of layout and paint work for off-canvas navigation, or of third-party widgets. Make sure that there is no lag when scrolling the page or when an element is animated, and that you’re consistently hitting 60 frames per second. If that’s not possible, then at least making the frames per second consistent is preferable to a mixed range of 60 to 15. Use CSS’ will-change to inform the browser of which elements and properties will change.

Also, measure runtime rendering performance (for example, in DevTools). To get started, check Paul Lewis’ free Udacity course on browser-rendering optimization and Emily Hayman’s article on Performant Web Animations and Interactions.

We also have a lil’ article by Sergey Chikuyonok on how to get GPU animation right. Quick note: changes to GPU-composited layers are the least expensive, so if you can get away by triggering only compositing via opacity and transform , you’ll be on the right track.

Have you optimized rendering experience?

While the sequence of how components appear on the page, and the strategy of how we serve assets to the browser matter, we shouldn’t underestimate the role of perceived performance, too. The concept deals with psychological aspects of waiting, basically keeping customers busy or engaged while something else is happening. That’s where perception management, preemptive start, early completion and tolerance managementcome into play.

What does it all mean? While loading assets, we can try to always be one step ahead of the customer, so the experience feels swift while there is quite a lot happening in the background. To keep the customer engaged, we can use skeleton screens(implementation demo) instead of loading indicators, add transitions/animations and basically cheat the UX when there is nothing more to optimize.

Testing And Monitoring

Have you tested in proxy browsers and legacy browsers?

Testing in Chrome and Firefox is not enough. Look into how your website works in proxy browsers and legacy browsers. UC Browser and Opera Mini, for instance, have a significant market share in Asia (up to 35% in Asia). Measure average Internet speedin your countries of interest to avoid big surprises down the road. Test with network throttling, and emulate a high-DPI device. BrowserStack is fantastic, but test on real devices as well.

Is continuous monitoring set up?

Having a private instance of WebPagetest is always beneficial for quick and unlimited tests. However, a continuous monitoring tool with automatic alerts will give you a more detailed picture of your performance. Set your own user-timing marks to measure and monitor business-specific metrics. Also, consider adding automated performance regression alerts to monitor changes over time.

Look into using RUM-solutions to monitor changes in performance over time. For automated unit-test-alike load testing tools, you can use k6 with its scripting API. > Also, look into SpeedTracker, Lighthouse and Calibre.

Quick Wins

This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let’s boil it all down to 10 low-hanging fruits. Obviously, before you start and once you finish, measure results, including start rendering time and SpeedIndex on a 3G and cable connection.