In 2003, to normalize a
document.getElementById, gracefully degraded to check for the non-standard
document.all, and used UA sniffing to check for old Netscape. I used it to do only one thing: change class names. It was ugly, but necessary. By today’s standards, at 885 bytes unminified, it was also tiny. Popular libraries and frameworks are much larger as they are written to be more robust than mine, but what is their impact on performance?
Libraries enable “code once, work everywhere.” Normalizing event listening methods
attachEvent was a common requirement and a big reason why jQuery was so quickly adopted. jQuery, currently at 252 KiB unminified (32 KiB minified and compressed), includes
toggleClass or similar function, a library isn’t needed as classList is natively supported in all modern browsers.
While frameworks and plugins can provide for faster, optimized, readable code, with function chaining, implicit iteration behavior, and other nice-to-haves, they’re not always beneficial or necessary. We should be focused on the user experience, not the developer experience. Think of libraries as scaffolding: they can help you quickly prototype and even build your application, but you don’t leave unnecessary scaffolding in place when the building, bridge or application is complete: you remove what you don’t need. Why are we sending unused functions with each request? Why are we creating web components which replicate the features of a native element, with or without a simple event handler, that are often only used once? Libraries, frameworks, and other dependencies should never be included out of laziness or for resume padding.
Using a framework or library to simplify a coding job means every site visitor will have to wait for that dependency to download. Every additional plugin contributes to bloat. In 2010, when we started keeping records about web app sizes, the average website was around 700 KiB, the size of ReactJS (145 KiB minified and compressed). ReactJS is now a starting point for many sites. That’s a lot of bytes! Does is it benefit the user? Or was it included to benefit the developer either in terms of time saved or in terms of added professional experience? What are the costs associated of including each dependency? It’s not just bloat that’s an issue, but also relying on 3rd-party dependencies that can be points of failure. For example, keeping left-pad as an external dependency caused major issues with React, Babel and many, many web applications when a similar, basic function could have been rolled into a site’s script without breaking the web!
Today the average size of a web site or application is around 2.5MB, with 403 KiB of generally minified scripts (versus 118 KiB of scripts, both minified and not, in 2010). 403 KiB may not seem that bad, being only 16% of the bloat of a modern web application. Indeed, optimizing images and other media continue to be the “low-hanging fruit” of web application bandwidth consumption. However, it’s not just the download time of application assets that matters. Performance isn’t just about download time: it’s about time to interactive and sustaining 60 frames per second (fps). When you have a site that is fully built client-side, the download time may be reasonable, but 60 fps may become a pipe dream.
The HTML request is small. Over fast 3G, it only takes 18ms to download the 2.6 KiB HTML file, after a TTFB of 1424ms, which includes the DNS lookup, redirection to HTTPS, SSL negotiation (this is an area that can definitely be improved). The problem is, the HTML draws only one empty DOM node to the page: the body contains only an empty div with an ID to create a hook for content created client-side. It calls a few scripts that, in turn, create all the content for this client-side application, with the main components of the site downloading in 8,204 ms. While the CSS puts the background image on the body element at the 6.92 s first paint, the rest of the application doesn’t render until DOM Content Loaded at the 13.62 s mark: it takes more than 5 seconds after the content creating assets are downloaded for the client-side script to render the content to the page.
DOMContentLoaded event. Yes, you read that correctly: a
DOMContentLoaded event that lasts longer than three seconds.
We get a beautiful pink bar in our waterfall timeline — a sight we never want to see.
During this time the CPU is at capacity, the main thread is occupied, and 1 to 2MB of garbage gets collected at regular intervals.
With cached assets, the browsers still spends 4.24 seconds processing script. The content, generated client-side, doesn’t get painted to the page for 5.7 seconds. The time it takes to render the cached view is longer than I would expect such a site to take to deliver everything from the server and render the first view!
Companies can spend hundred of developer hours and hundreds of thousands of dollars making download time faster, within the recommended 1 to 2 seconds. However, if they aren’t focusing on the time it takes to parse and execute client-side site-generating scripts, they are not going to improve user experience.
Good engineering involves finding simple solutions to sometimes complex problems. Repurposing code may make development easier, but ease of development is not the end goal.
Sometimes you really do need a framework, and a 3-second