Lazy Loading and the Story of the Mysterious Polyfill

Michael Sandor, Fullstack Engineer

Background

Did you know that between 2011 and 2019, the median resource weight for a desktop, web application increased from around ~100KB to ~400KB?[1] Here at Fieldwire, we have certainly felt that pressure as our web application has increased, and continues to increase, in both scope and complexity. As a result, we have begun an initiative to improve performance within our web application. One aspect of improving performance has been the adoption of lazy loading.

Lazy loading is a strategy that involves identifying non-critical resources and only loading them when needed. A great example of a non-critical resource in the Fieldwire web application is the Google Charts library. It is only used to render the manpower chart within the Gantt view on the Tasks tab.

Therefore, a prudent—albeit slightly more complicated—approach would be to dynamically load the Google Charts library only when a user enters the Gantt view for the first time. This would save us precious time on initial page load and make for a more seamless user experience.

Implementation

In order to lazy load the Google Charts library, we built a nifty utility using Promises and Lodash’s _.memoize that loads our script, allows us to trigger callbacks when the script is done loading, and caches (memoizes) the results of said script loading.

With our script-loader built, there were three things left to do: remove the old loading code from our index.html file, call the script-loader when the user enters the Gantt view, and add a piece of loading UI that would appear while the Google Charts library is being loaded for the first time.

Hello IE 11 My Old Friend

After some basic sanity testing, we merged the changes and prepared to tackle other areas of the codebase in which lazy loading could be applied. It was only a short time later that we received a notification from QA: regression found during test - unable to log in using Internet Explorer 11.

The bug was quickly linked back to our Google Charts lazy loading code. How could this be possible? The code in question was so far away from log in that it seemed implausible. However, sure enough, when attempting to log in to our staging environment in IE 11, the application would freeze after entering in an email.

Debugging

As frontend engineers, there are certain things that we instantly do when we encounter runtime issues like this. Checking the console is one of them. However, in this case, there were no errors or warnings in the console. Perhaps even more perplexing was the fact that this new behavior only occurred in IE 11. The log in freeze did not occur in Chrome, Safari, or Firefox. This meant that it was definitely a frontend issue.

Less than an hour later, the cause of the problem revealed itself. We were encountering an unhandled, and silenced, Array doesn't support property or method 'includes' error when parsing our response from the backend after a user had entered their email. This made sense because IE 11 doesn’t support includes on type of Array. However, it didn’t make sense because we hadn’t encountered this problem before. Was there some package or dependency that had been mysteriously providing a polyfill for includes? We looked into other code that had been checked in around that time and it looked like several packages or dependencies had been changed. Initially, this seemed promising. However, after further investigation, these changes proved to not be the source of the polyfill. Whatever entity was providing a polyfill for includes had to be related to our changes. Then we had our lightbulb moment. What if the Google Charts library was providing a polyfill for includes? It seemed plausible. Google engineers would surely be aware of cross-browser compatibility issues and would derisk the library by providing one or more polyfills. The initial script that loaded Google Charts was the following: https://www.gstatic.com/charts/loader.js.

A quick inspection revealed that the Google Charts library was, in fact, sneakily inserting polyfills for includes and other methods not supported by IE 11.

Reverting

This discovery unfortunately meant that we had to revert our Google Charts lazy loading PR. There were scattered instances of includes in our codebase, beyond just the log in logic. Google’s loader.js had unknowingly lulled our team into using certain, newer JavaScript features without proper polyfills and we now had to retreat. Unfortunately, we would not be able to push the library into lazy loading obscurity.

Looking Ahead

There was, however, a bright spot in this ordeal. We have now set March 31, 2021 as our final day of IE 11 support. From that point on, we will not actively troubleshoot or fix problems that occur as a result of IE 11 cross-compatibility issues. We have also learned that relying on third-party code can be a very slippery slope, and it must be added and removed with extreme care. With that being said, lazy loading remains a worthy cause. There are many instances of lazy loading causing non-trivial, and even significant, improvements in performance.[2] We predict that our application will be no exception and will continue to work towards a faster, more performant Fieldwire.