One of the great joys of working at Google is seeing the big trends in web development. It’s genuinely awesome to see all the innovation and excitement around new ways of crafting experiences. Though, if I’m honest, there are other trends I see, too: developers still feel overwhelmed at the vast array of approaches and tooling, struggle with performance, and fight almost daily to create compelling apps & sites that work across devices and browsers.
This post is the first in a series where we look at some of these challenges, and attempt to suggest possible solutions and strategies. So what to begin with? Let’s start with where we are.
It’s 8.30am on a Monday morning. You look out of your car or train window and you see something that looks like this. Rush hour.
The worst time of day to travel. For many it’s not possible to travel at any other time of day because they need to get to work by 9am.
This is exactly what a lot of web code looks like today: everything runs on a single thread, the main thread, and the traffic is bad. In fact, it’s even more extreme than that: there’s one lane all from the city center to the outskirts, and quite literally everyone is on the road, even if they don’t need to be at the office by 9am. It really is one of the trends I see, and you don’t normally have to go far to find stats (Look at HTTP Archive or one of Alex Russell’s many tweets) that show that there’s more code than ever on the main thread and it’s looking to increase over time.
Okay, what can we do about it? Taking the case of the roads there are a few options, which range in effectiveness and pragmatism:
What would these three look like if we applied these concepts to the web?
Year on year companies ship better phones, that is true. However, it’s also true that the lower bar has actually gone down, such that the gap between entry level and flagship devices has never been wider. In essence, then, relying on increased capacity would be similar to telling someone that they need a supercar to drive on a given road, or, in our case, ‘please upgrade your phone or computer to something “good enough”’. I personally can’t imagine telling someone to do that, especially if what they had was all they could afford, and I don’t think we should ever bank on increased resources to mask capacity issues. True of transport, true of code.
We actually have the option to move non-UI traffic today: Workers. Since options like React / Preact use virtualized copies of the DOM that don’t technically need to interact directly with the real DOM until patches are applied, this is something that we should look into more thoughtfully.
There are very few notable cases of people making good use of Workers, which is a shame. (I always think of Nolan Lawson’s Pokedex app, which did a great job here!) True enough, it can be painful to navigate postMessage with Workers, and without doubt most of the abstractions many of us rely on day to day are not built with Workers in mind, but if we accept the premise that the main thread is often in a ‘traffic jam’ and overworked, then by extension we need to seek ways to alleviate the traffic. The Worker is our primitive for doing that.
Unlike the real world, where people work collaboratively in businesses and need to be collocated at 9am to 5.30pm in the same office (generally speaking), there is rarely a case where code must all be run at the same time.
Recently Philip Walton wrote a must-read article on a strategy he called Idle-Until-Urgent, which treats UI work as non-essential by default and only upgrades render priority when certain criteria are met. As I mentioned above, my own personal preference is “it’s on the screen right now”, but even within that single criterion some components are likely to be more important than others; most likely the ones with which the user is interacting. If, for example, they want to compose an email, that’s the most important component bar none.
What does this all point to? For me, a yet-to-exist scheduler that I think we need on the web platform. Whether you use requestAnimationFrame, requestIdleCallback, setTimeout, or setInterval, you will eventually hit the case where you create something scheduler-like, attempting to orchestrate a queue of discrete tasks. Or you won’t and you will run single long-running tasks that just updates everything in one go. And why wouldn’t you? It’s the default on the web today. Just like driving on the roads at rush hour.
We need better defaults. We need more organized traffic.
While I have written this article, ideas and thoughts don’t happen in a vacuum. Specifically, in this case, Surma can be thought of as the co-author of this series, since most of it has come from our many conversations together.
But there are others, too, who deserve to be mentioned because they have contributed, either knowingly, or just by saying or doing the right thing at the appropriate time: Doug Fritz, Paul Irish, The Chrome DevTools team, and Nolan Lawson.
There are probably others, too.
Standing on the shoulders of giants here, people.