I would like to begin with citing my mentors & team-mates who helped me to understand the concepts & enhance my knowledge on performance improvements of Website. It was a collaborative effort across teams from the planning stage till taking things to Production. The list of people whom I would want to thank is quite long but would want to cite a few notable people who had a larger influence in making this happen:
- Hidayat Febiansyah (https://www.linkedin.com/in/hidayat-febiansyah-9a3641140/, working at Blibli.com)
- Friska Nataniel (https://www.linkedin.com/in/friskanataniel/, working at Blibli.com)
- Anand Kumar Tiwari, (https://www.linkedin.com/in/anand-tiwari-30455b105/, working at Quinbay)
Let me give a brief Heads-Up:
UI Rendering was slow on Search Listing pages in our site as we had multiple cross-cutting components being loaded during the rendering of the page. There were also these redundant & sub-optimal recursive implementations which eventually built up over a period of time.
The listing page though owned by one part of the team; it had several modules that were managed by different teams.
The framework and the supporting tools we use are as below:
- https://www.npmjs.com/package/intersection-observer-polyfill (used for lazy loading of components)
- https://developers.google.com/web/tools/lighthouse (UI testing tool)
Diving into the waters right away …. how we swam through
Some of the BIG things we identified to be addressed immediately were as below:
- Changing Image format from JPEG to WEBP
Until recently we were using JPEG format while rendering images on site. JPEG are heavier and for some odd reason we had been reliant on using jpg though WebP which is a far more improved version of image formatting techniques which supports both lossy & lossless compression was published 10 years ago.
WebP is an image formatting technique which uses both lossy and lossless compression. For more details you can refer to : https://developers.google.com/speed/webp/faq
As on the day of this writing, neither Firefox nor Safari had native support for WebP. This wasn’t a big deal for us to handle, because in Safari and FireFox users will still be rendered with JPEG version of the images. But the major chunk of users we have are luckily on Chrome or Edge. So, we had the support of Users intuitively.
- Create Webpack chunk names
Webpack as the name suggests bundles modules of the project by building a dependency graph. This allows you to split your code into various bundles which can then be loaded on demand or in parallel. It can be used to achieve smaller bundles and control resource load prioritization which, if used correctly, can have a major impact on load time.
But for these bundles we give name to identify which bundle comprises of which components and node modules. So, by this we can identify the unnecessary modules loaded in that bundle and remove that.
You can use ‘npm run build –report’ command to get details of the kind of bundling one has in their UI modules. An example report would be as depicted below:
- Single page application constraints
If your web-application comprises of multiple modules handled across multiple teams and each render part of the single page application, it therefore requires Code review from time to time across teams to avoid any Internal URL redirections etc, that would break the principle functioning of SPA.
We had a scenario where the Search pages of our E-Commerce site (blibli.com) was redirected to different url paths based on different types of requirement like SEO, brand or merchant pages.
As search pages are rendered along with other components that are not purely under the purview of Search, the url redirections caused us to re-load the page instead of just refreshing the result portion of page. We identified these simple concerns in the tech-debt part of a sprint to improve performance of the site and fixed the point of concerns.
- Lazy loading of components & Images
One needs to identify the parts of the site / app that can be loaded lazily. Meaning request for the resource only when the user demands to view those sections explicitly and not to load the entire page on the first interaction.
This way we can reduce the network bandwidth used, number of resources loaded in the site, number of calls to the site or static resources. All in all you can find lot of benefits from this behavioral change of site. One can find enough tech articles describing the implementation of such a pattern.
- Code Refactoring
Let’s take an example of a tree-structure component of E-Commerce site as shown below:
As and when user chooses an option an api call is made to reflect the changes caused by this user action. An api call if it is not fast enough the end users would see the screen frozen for a moment before reflecting the changes (even the selection status of the option).
Below is an example where the api call + the data structure processing + sending out UI trackers + drawing the items on UI entirely took lot of time.
The solution we identified was as below:
- Not to manipulate the DOM
- Show the tick mark when user clicks in the check box
- Reduce recursions in tree-view component that are unnecessary usage of computed properties
- Call trackers only after successful execution of the call
- Have loaders if the api calls take longer time.
We observed the change in experience for users and thus engagement on these tree structured UI interactions too improved.
What we finally saw …
Our overall web-site performance improved and thus User engagement increased. As per the lighthouse reports we could see that improvements were justified and measurable through these actions.
Our site performance before the above upgrades:
And improved to this after the changes:
This is not to say that we are done with the performance improvements and we are at the pinnacle of it. Rather this is just a start and there are still major upgrades happening on the performance as I write this article. I will share once we see its light. Till then…Adios 👍