EXPEDIA GROUP TECH — SOFTWARE

Web Applications: Analyzing Client-Side Performance

A beginner’s guide to the unarguably complex process of analyzing web application performance

Ian White
Expedia Group Technology

--

The strategies suggested in this post are the result of observing Expedia Group™ Staff Software Engineer Joonas Tanner analyze one of our customer facing applications.

Performance analysis ideally should be agnostic with respect to the underlying UI or server framework. End users (including browsers) do not care if the site was made with React, Vue, or if it is a collection of static files.

What is more important is that the site is fast to use and easy to parse. As such, it’s a good idea to analyze the end product: observe the HTML source and the bundled JS/CSS that gets served to the customers by the application while it’s running in production, or in a production-like environment. Ultimately, just treat the site as a document — which it is.

Analyzing the document — Sources of Truth for Reliable App Performance Analysis Results

The first thing to discuss is which environment should be used as the source of truth when analyzing your application’s performance metrics. The following quote is paraphrased from internal Vrbo documentation regarding measuring application performance:

Performance tests on your local machine are not indicative of performance once deployed.

Therefore, the first source of truth that will provide the most accurate performance metrics will be assessing your application in a production environment.

If for some reason you cannot analyze your app in production, the next closest thing would be a stage environment.

If you really need to tweak and test code changes etc. you can also analyze your app while it is running in production mode locally but this is NOT really a reliable source of information, so always make sure you verify all results in production.

Step 1: Analyze The Network Tab

Network tab showing premature and excessive asset downloads.
Are all these assets used? Are they downloading prematurely?

While viewing your page open up the Network tab in the Chrome dev tools. Ensure you perform a hard refresh (or disable the cache in network tab) to accurately analyze the assets served by your application.

Things to look for:

  1. Large assets that can be optimized
  • Say you have an image on your page that is originally 500px wide but shrunk down to 50px with CSS. This is a performance hit both in terms of slower rendering speed and download speed due to the larger file size.
  • If this image does not need to responsively enlarge to 500px then resize and replace it with a 50px version.
  • If you do need the image to responsively resize depending on the viewport, one solution would be to implement a strategy that serves the exact image size necessary for the current screen size rather one large image that scales from mobile up to desktop. Also ensure you are leveraging the maximum amount of image compression and optimum image formats.

2. Long-running requests

3. Assets not downloading in parallel. These will slow down the total page load.

4. Identify large blocking resources and, if possible, defer or load asynchronously, or better yet load lazily or use the idle until urgent strategy.

  • For example, if you are seeing a large number of images being downloaded for a Google Maps instance which is at the bottom of the page outside of the viewport that the user may never even see perhaps the images and/or the relevant javascript should be lazy loaded to avoid the performance hit to the user upon first visiting the page.

5. Ensure assets are being Gzip’ed server-side.

6. Ensure there are no duplicate asset downloads i.e. the same asset is being downloaded more than once.

Step 2: View the Page Source from Top to Bottom

Inspecting the page output using the Chrome DevTools Elements tab can be unreliable since the DOM tree can potentially be dynamically updated by running scripts so instead opt to utilize the “View Page Source” option to analyze resource loading order/strategy.

The key aims here are to identify resource loading order, verify correct usage of rel attributes (deferred, async, preload, etc), and analyze any inline scripts.

Identify large blocking resources that are ahead of critical resources such as the main program bundle and assess whether or not they can be re-ordered or eliminated.

Take note of any inline JavaScript. Determine if there is indeed a necessity for this code to be inline. This code will not work without JS support so consider creating a static asset that can be served asynchronously, if possible.

Copy the <head> of the document, analyze its size in a code editor, and question the importance of included resources as well as their load order.

Additionally:

  • Identify non-critical resources that could be async loaded.
  • Identify resources that aren’t necessary unless an explicit action on the page is initiated and lazy load or defer loading after critical resources.

Identify inline SVG code and optimize or eliminate it. For example, if you see duplicated inline SVG code in the DOM consider adding an ID to the SVG code and leveraging the <use> element.

Step 3: Disable Javascript and Evaluate the Experience

There are several scenarios where JS support may not work including user preference, script errors, and network/loading errors to name a few. Therefore, it is recommended to evaluate your application with JS disabled via the Chrome Dev Tools.

If a certain feature can only work with JS enabled then it is safe to assume it can be lazy loaded with JavaScript.

Ensure you are offering at least the minimum functionality necessary for your application to work without JS. Even if you accept that your page will not work without JS support at least ensure an informative message is presented to the user e.g. “Please enable JavaScript to use {page/app}”.

Step 4: Analyze DOM Node Count

Analyze your application’s DOM node count and try to attain a size less than or equal to 1500. A large DOM can increase memory usage, cause longer style calculations, and produce costly layout reflows. Learn more from an excellent blog post written by fellow Expedia Group software engineer Joonas Tanner and from the Google developer docs.

Step 5: Analyzing Application Bundles

This step probably deserves a post (or several) of its own, but in general:

Check for code that is only included for an A/B test variant is not downloaded for everyone. Also ensure any dead code related to A/B tests is removed.

Leverage the webpack-bundle-analyzer to identify dependencies that accomplish the same goal. For example, if your app is pulling in multiple HTTP clients such as superagent, axios, etc, consider replacing them with fetch. If they are dependencies of dependencies reevaluate the library and search to see if there's an analogous one that uses fetch.

Analyze possible sources of transpilation bloat. An example of transpilation bloat can been seen in React class based vs functional components. Class based components transpile to larger sized files than functional components. Although the file size increase per component is relatively small, those extra bytes can easily add up in a complex web application with dozens of React components. See this excellent blogpost by Drew Walters for details.

In Summary

In conclusion, when conducting performance analysis of your application make sure you keep things simple by focusing strictly on the output that gets served to your customers in a production environment and not the server or UI framework.

Become familiar with and utilize the Chrome Dev Tools to analyze the DOM structure, network resource requests, page source, and UI experience with Javascript disabled to identify performance bottlenecks, many of which could turn out to be low hanging fruit.

Finally, utilize bundle analysis tools to scrutinize the resources that ultimately are downloaded by your users for bloat and superfluous code.

--

--