Core Web Vitals | How to optimize website Performance.

Amit Kumar
redbus India Blog
Published in
10 min readAug 4, 2020

--

redBus Mobile web

Background : Website Performance

What is performance? One will consider performance in everything in day to day life for eg: an automobile, a smart phone, an electronic device etc… In similar terms, we talk about our web pages as well. There are several metrics to measure the performance of a webpage, we will explore them one by one as we go through this article.

Let’s understand that why is this important, is it worth spending time and effort?

The answer to this question is Yes and here are the reasons, why:

  1. Better user experience,
  2. More conversions, and
  3. Improved SEO performance.

Now we know why this is important, but we still need to figure out what are the metrics that can be used to define a good performing site. As per studies, any site which becomes ready for user interaction in 3 seconds are very good in performance. We call this metric TTI (Time to Interactive).

As per Google, best practices are 3 seconds but majority of sites fail to meet this criteria — for more details,

How redBus improved mobile web page performance ?

We approached this step by step and these have been incorporated in our recent Mobile web revamp for

  • better user experience
  • improved SEO performance.

As you can see in the above graph, on 4G network our performance score as per chrome audit was 65/100, on Fast 3G network page performance was 50/100 and on slow 3G it was around 34/100.

NOTE:- During this complete process our best friend is Google Chrome Lighthouse tab

It will recommend the best practices and a resource material for better understanding.

Before we start with the implementation details, Let’s check on different metrics for performance.

  1. Time to first Byte (TTFB)
  2. First Contentful Paint (FCP)
  3. First meaningful Paint (FMP) (deprecated from Lighthouse 6.0)
  4. Time to interactive (TTI)

TTFB → This metric tells us about our server response time for the page. It means how much time the server is taking to respond with the page document to the browser. As per google this audit fails if our browser waits for more than 600 ms for the server to respond to the main document.

FCP → This metric is basically a measure of time from when the page starts the loading process to when any section of the page is rendered and visible to the user.

TTI → This metric tells us how much time the page is taking to be ready for the user to interact. It is very important for every developer to monitor these numbers for their pages who are working on browser side apps.

Let’s get started:-

1) Code splitting.

a) For every developer working on web technologies, we have to write a lot of code in Javascript, use a lot of css in designing UI, use multiple font-files (woff2) etc…

In order to ship these codes we create bundles out of it — CSS bundles , JS bundles, Font files etc. There are multiple options which are there in the market for performing this activity, in redBus we use webpack.

We use webpack for multiple reasons, I would like to highlight the flexibility aspect of it in terms of bundling, performance budgeting etc... There are many plugins with webpack which is very much configurable.

In the below snapshot, you can see that we had created bundles from webpack but it was one for css, one for js and so on.

CSS bundle downloaded to browser

JS Bundle downloaded to browser

With this setup, the website will work perfectly fine but it will take more time to load because browser have to download all our code in a single network call.

Instead of doing this, we can split the same bundle and ask for small chunks of code in multiple calls on load. This is supported by webpack through splitChunks.

This will give some improvement in load times and TTI, Please refer the snapshot below:-

These are the dynamic bundles which we create through webpack splitting.

b) On demand bundle calls: — only download what is needed and when it is required. (On demand Loading)

This is very important for us to understand and determine the list of features on our page that is needed on load. e.g., we have menu items on the home page which can be loaded on demand. With this approach, we will reduce the total size of data that is needed to load the page. Hence better TTI.

There are multiple ways to achieve this but in our system we created a dynamic important process with a custom setup but there are lib available to do so eg: react-lazy lib,, etc

2) Compression Audit

If you have reached till this part of the read — that’s great ! This section talks about compression. We are doing this to compress our data transfer over the network (http calls). Here, we first wanted to do an audit on our existing compression technique. Till now we were using gzip compression which is good but there are better solutions — one such solution is Brotli.

As per Wikipedia:-

Brotli is a data format specification for data streams compressed with a specific combination of the general-purpose LZ77 lossless compression algorithm, Huffman coding and 2nd order context modelling. Brotli is a compression algorithm developed by Google and serves best for text compression.

We upgraded our webpack to start generating bundles separately in brotli and gzip formats. You may have a question. Why gzip if we have started using brotli compression?

The answer to this question is, In terms of browser support we had to look back to our user base, it means there are many users of redBus who are still using browsers which do not support brotli compression. Please refer to the snapshot below for more details on browser support for brotli

We did below setup in our startup file

With this approach we were able to configure our system to respond with the file which is relevant to the browser from where we receive a request.

After having the above set steps completed and we released our new setup in production, we found the below changes.

Based on this analysis, we saw that there is around 15% increase in score in 4G network, around 20% increase in Fast 3G and approx 27% in slow 3G.

Well it’s good to see that there is some progress in our user experience and page load time(TTI)

What’s Next?

This is something difficult to plan now….

We again went back to our best friend “Lighthouse”. We found that there are some issues in the number of network calls. The number of calls which goes from client (browser) to our servers were high…(approx. 55) It includes css, js, font files, offer images, icons, banners, third party tracking lib, GTM, marketing lib, service worker etc…..

Let’s cut down third party lib from these calls: -55–22 = 33 calls.

Now we have to see how to optimize these calls.

4) Reducing Http calls, less browser round trip.

  1. After this analysis we start combining the images to sprites.
  2. Created icon fonts for most of the assets.
  3. We stopped calling real time data for static assets like offers on load. To do this we added a thin caching layer on top of these calls with auto expiry x hours.
  4. We stopped downloading fonts lib from external dependency on every page load. Instead download them and keep it at our server.

5) Enable browser caching.

What is browser caching?

Basically when you visit a site the browser caches the static assets calls and on the next load it does not call the server to download those assets again, hence it loads faster. To enable this we have to define proper cache policy information in all those requests which does not change frequently.

Response from cache

6) Reducing TTFB.

Let me give background about it. TTFB stands for Time to first byte. This is very important for us because this is the time which browser is waiting for the first byte to start the loading process. As per google recommendations this should be within 200 milliseconds.

We identified all items in our codebase that can cause delay in TTFB, we added cache layers to reduce this and also removed the logic or Operations which are not needed on first load.

You can see in the above image that server response time is high, this can be due to many reasons(multiple api calls , complex logical operations etc…) , We need to see if these are needed at initial load, if not then let’s find the ways to call it after the page is loaded.

To solve this problem for our use-case we added a caching layer on api calls which were giving static response for certain amount of time.

After doing this change we save improvement as below:-

As we can see there is a good amount of improvement in our TTFB.

By doing the above 3 steps, we were able to bring down our TTI to 5–6 seconds and performance was as below.

Based on this analysis, we saw that there is around 13% increase in score in 4G network, around 21% increase in Fast 3G and approx 34% in slow 3G.

Good going so far.. But What’s Next?

7) PRPL setup

  1. Push (or preload) the most important resources.
  2. Render the initial route as soon as possible.
  3. Pre-cache remaining assets.
  4. Lazy load other routes and non-critical assets.

This is a pattern which tells us how to manage our resources efficiently which are needed on our pages. Prefetch tells the browser to request a certain resource as soon as possible. Important resources can be preloaded by adding a tag with rel=”preload” to the head of your HTML document.

We did this setup in our master layout file which is shared among all our pages. Please see the snapshot below:-

Let’s describe them individually:

Push:- This process tells us to push the initial resources needed by the webpage to render, to achieve this we performed,

  • Preload:-There are many assets like js, css fonts, etc which are needed in a page, with this approach we ask the browser to download the assets before they are being called in our code. This reduces our rendering time.
  • Preconnect:- we download different assets and files from some or the other domain which we are already aware of, so we ask the browser to preconnect those domains.
  • Prefetch:- preload and prefetch are resource hints, it helps to overcome the render blocking nature of resources.

Render Initial route as soon as possible.

As it is very clear from the title itself that we should try to reduce our first Paint for the webpage. In order to understand this. Let’s understand First Paint.

What is First Contentful Paint(FCP)?

This is a measure which tells us the time from the start of the webpage load to when the page is loaded.

Now we should try to make this duration as low as possible. In order to do so we need to load the basic assets (JS, CSS,etc) which are needed in the initial load. All other JS etc. can be deferred. With this approach your FCP will be less.

Pre-Cache assets:-

This is not something which will help us in initial load but it will definitely help us in subsequent requests which the browser does for later operations on the webpage (this is done after the page is loaded).

We use service worker for this purpose. With service worker, we get much better experience as a user on our site on repeat visits.

Lazy Load:-

As the name suggests, we need to do this activity on all assets. Basically, we need to divide which chunk is needed on initial load and which one can be deferred. Based on this analysis we can start lazy load to all those assets.

In redBus, we used our custom setup as well as webpack for doing the same.

8)Try to load all the images on page in a progressive way. There are multiple ways of doing this. We have described one of the ways:-

In this approach you need to add image url in data-src instead of src. The script will add the value of data-src to src after the page is loaded in its lifecycle. Hence your images will load after the page is loaded.

Summary

  1. Load all your third party calls after the window is loaded and the page is ready.
  2. Maintain your DOM size.
  3. Control network data transfer. (Suggested range would be between 200 to 300 KB)

FINAL MOBILE WEB NUMBERS

Finally, as we can see we have made an increment of around 40% on 4G network from our very initial state.

Thanks for reading, have a great day!

Have a specific question? Drop us a tweet.

--

--