While I’ve always been bothered by the downsides of SPAs, I always thought the gap would be bridged sooner or later, and that performance concerns would eventually vanish thanks to things like code splitting, tree shaking, or SSR. But ten years later, many of these issues remain. Many SPA bundles are still bloated with too many dependencies, hydration is still slow, and content is still duplicated in memory on the client even if it already lives in the DOM.
Yet something might be changing: for whatever reason, it feels like people are finally starting to take note and ask why things have to be this way.
Interesting to see a decade-long perspective. I especially like how Sacha revisits and reasseses design principles from ten years ago:
Data on the Wire. Don’t send HTML over the network. Send data and let the client decide how to render it.
It’s since become apparent that you often do need to send HTML over the network, and things seem to be moving back towards handling as much as possible of your HTML compilation on the server, not on the client.
I believe that we haven’t figured out when and how to give a developer access to an abstraction or how to evaluate when an abstraction is worth using. Abstractions are usually designed for a set of specific use-cases. The problems, however, start when a developer wants to do something that the abstraction did not anticipate.
Smart thoughts from Surma on the design of libraries, frameworks, and other abstractions:
Abstractions that take work off of developers are valuable! Of course, they are. The problems only occur when a developer feels chained to the abstractions in a situation where they’d rather do something differently. The important part is to not force patterns onto them.
There’s a lot of emphasis put on decision-making: making sure you’re making the right decision; evaluating all the right factors before making a decision. But we rarely talk about revisiting decisions.
I think perhaps there’s a human tendency to treat past decisions as fixed. That’s certainly true when it comes to evaluating technology.
I’ve been guilty of this. I remember once chatting with Mark about something written in PHP—probably something I had written—and I made some remark to the effect of “I know PHP isn’t a great language…” Mark rightly called me on that. The language wasn’t great in the past but it has come on in leaps and bounds. My perception of the language, however, had not updated accordingly.
I try to keep that lesson in mind whenever I’m thinking about languages, tools and frameworks that I’ve investigated in the past but haven’t revisited in a while.
The carousel is like one of those on a game show that shows the prizes that can be won. The tool will sit on there until I think it’s gone through enough maturing to actually be a viable tool for me, the team I’m working with and the clients I’m working for.
Crucially a carousel is circular: tools and technologies come back around for re-evaluation. It’s all too easy to treat technologies as being on a one-way conveyer belt—once they’ve past in front of your eyes and you’ve weighed them up, that’s it; you never return to re-evaluate your decision.
This doesn’t need to be a never-ending process. At some point it becomes clear that some technologies really aren’t worth returning to:
It’s a really useful strategy because some tools stay on the carousel and then I take them off because they did in fact, turn out to be useless after all.
See, for example, anything related to cryptobollocks. It’s been well over a decade and blockchains remain a solution in search of problems. As Molly White put it, it’s not still the early days:
How long can it possibly be “early days”? How long do we need to wait before someone comes up with an actual application of blockchain technologies that isn’t a transparent attempt to retroactively justify a technology that is inefficient in every sense of the word? How much pollution must we justify pumping into our atmosphere while we wait to get out of the “early days” of proof-of-work blockchains?
Back to the web (the actual un-numbered World Wide Web)…
Nolan Lawson wrote an insightful article recently about how he senses that the balance has shifted away from single page apps. I’ve been sensing the same shift in the zeitgeist. That said, both Nolan and I keep an eye on how browsers are evolving and getting better all the time. If you weren’t aware of changes over the past few years, it would be easy to still think that single page apps offer some unique advantages that in fact no longer hold true. As Nolan wrote in a follow-up post:
My main point was: if the only reason you’re using an SPA is because “it makes navigations faster,” then maybe it’s time to re-evaluate that.
Perhaps the best example of a technology that warrants regular re-evaluation is the World Wide Web itself. Over the course of its existence it has been seemingly bettered by other more proprietary technologies.
Flash was better than the web. It had vector graphics, smooth animations, and streaming video when the web had nothing like it. But over time, the web caught up. Flash was the hare. The World Wide Web was the tortoise.
In more recent memory, the role of the hare has been played by native apps.
I remember talking to someone on the Twitter design team who was designing and building for multiple platforms. They were frustrated by the web. It just didn’t feel as fully-featured as iOS or Android. Their frustration was entirely justified …at the time. I wonder if they’ve revisited their judgement since then though.
Just the other day I was chatting with one of my colleagues about an online service that’s available on the web and also as a native app. He was showing me the native app on his phone and said it’s not a great app.
“Why don’t you add the website to your phone?” I asked.
“You know,” he said. “The website’s going to be slow.”
He hadn’t tested this. But years of dealing with crappy websites on his phone in the past had trained him to think of the web as being inherently worse than native apps (even though there was nothing this particular service was doing that required any native functionality).
It has become a truism now. Native apps are better than the web.
And you know what? Once upon a time, that would’ve been true. But it hasn’t been true for quite some time …at least from a technical perspective.
But even if the technologies in browsers have reached parity with native apps, that won’t matter unless we can convince people to revisit their previously-formed beliefs.
The technologies are the easy bit. Getting people to re-evaluate their opinions about technologies? That’s the hard part.
This is a great case study of switching from a framework mindset to native browser technologies.
Though this is quite specific to Jack’s own situation, I do feel like there’s something in the air here. The native browser features are now powerful and stable enough to make the framework approach feel outdated.
And if you do want to use third-party dependencies, Jack makes a great case for choosing smaller single-responsibility helpers rather than monolithic frameworks.
Replacing lit-html would be an undertaking but much less so than replacing React: it’s used in our codebase purely for having our components (re)-render HTML. Replacing lit-html would still mean that we can keep our business logic, ultimately maintaining the value they provide to end-users. Lit-Html is one small Lego brick in our system, React (or Angular, or similar) is the entire box.
We noticed a trend: students who pick a UI framework like Bootstrap or Material UI get off the ground quickly and make rapid progress in the first few days. But as time goes on, they get bogged down. The daylight grows between what they need, and what the component library provides. And they wind up spending so much time trying to bend the components into the right shape.
I remember one student spent a whole afternoon trying to modify the masthead from a CSS framework to support their navigation. In the end, they decided to scrap the third-party component, and they built an alternative themselves in 10 minutes.
This tracks with my experience. These kinds of frameworks don’t save time; they defer it.
The one situation where that works well, as Josh also points out, is prototyping.
If the goal is to quickly get something up and running, and you don’t need the UI to be 100% professional, I do think it can be a bit of a time-saver to quickly drop in a bunch of third-party components.
Very true! jQuery is the canonical example of a library smoothing over the bumpy landscape of browser compatibilities. But jQuery is also the canonical example of a library we no longer need because the browsers have caught up …and those browsers support standards directly influenced by jQuery. That’s a library success story!
Browser compatibility is one of the underlying promises that libraries—especially the big ones that Jeremy references, like React and Bootstrap—make to developers.
So again, it’s browser incompatibilities that made libraries attractive.
We distrust the browser because we’ve been trained to. Years of fighting browser deficiencies where libraries filled the gaps. Browser enemy; library friend.
For example: jQuery did wonders to normalize working across browsers. Write code once, run it in any browser — confidently.
Three for three. My question has been answered: people gravitated towards libraries because browsers had inconsistent implementations.
I’m deliberately using the past tense there. I think Jim is onto something when he says that we’ve been trained not to trust browsers to have parity when it comes to supporting standards. But that has changed.
This approach isn’t a sustainable practice, and I’m trying to do as little of it as I can. Jeremy is right to be suspicious of third-party code. Cross-browser compatibility has gotten a lot better, and campaigns like Interop 2022 are doing a lot to reduce the burden. It’s getting better, but the exasperated I-just-want-it-to-work mindset is tough to uninstall.
I agree. Inertia is a powerful force. No matter how good cross-browser compatibility gets, it’s going to take a long time for developers to shed their suspicion.
Jim is glass-half-full kind of guy:
I’m optimistic that trust in browser-native features and APIs is being restored.
He also points to a very sensible mindset when it comes to third-party libraries and frameworks:
In this sense, third-party code and abstractions can be wonderful polyfills for the web platform. The idea being that the default posture should be: leverage as much of the web platform as possible, then where there are gaps to creating great user experiences, fill them in with exploratory library or framework features (features which, conceivably, could one day become native in browsers).
Yes! A kind of progressive enhancement approach to using third-party code makes a lot of sense. I’ve always maintained that you should treat libraries and frameworks like cattle, not pets. Don’t get too attached. If the library is solving a genuine need, it will be replaced by stable web standards in browsers (again, see jQuery).
I think that third-party libraries and frameworks work best as polyfills. But the whole point of polyfills is that you only use them when the browsers don’t supply features natively (and you also go back and remove the polyfill later when browsers do support the feature). But that’s not how people are using libraries and frameworks today. Developers are reaching for them by default instead of treating them as a last resort.
I like Jim’s proposed design princple:
Where available, default to browser-native features over third party code, abstractions, or idioms.
(P.S. It’s kind of lovely to see this kind of thoughtful blog-to-blog conversation happening. Right at a time when Twitter is about to go down the tubes, this is a demonstration of an actual public square with more nuanced discussion. Make your own website and join the conversation!)
I’ve noticed a strange mindset amongst front-end/full-stack developers. At least it seems strange to me. But maybe I’m the one with the strange mindset and everyone else knows something I don’t.
It’s to do with trust and suspicion.
I’ve made no secret of the fact that I’m suspicious of third-party code and dependencies in general. Every dependency you add to a project is one more potential single point of failure. You have to trust that the strangers who wrote that code knew what they were doing. I’m still somewhat flabbergasted that developers regularly add dependencies—via npm or yarn or whatever—that then pull in even more dependencies, all while assuming good faith and competence on the part of every person involved.
It’s a touching expression of faith in your fellow humans, but I’m not keen on the idea of faith-based development.
And yet, the mindset I’ve noticed is that many developers are suspicious of browser features but trusting of third-party libraries.
When I write and talk about using service workers, I often come across scepticism from developers about writing the service worker code. “Is there a library I can use?” they ask. “Well, yes” I reply, “but then you’ve got to understand the library, and the time it takes you to do that could be spent understanding the native code.” So even though a library might not offer any new functionality—just a different idion—many developers are more likely to trust the third-party library than they are to trust the underlying code that the third-party library is abstracting!
Developers are more likely to trust, say, Bootstrap than they are to trust CSS grid or custom properties. Developers are more likely to trust React than they are to trust web components.
On the one hand, I get it. Bootstrap and React are very popular. That popularity speaks volumes. If lots of people use a technology, it must be a safe bet, right?
But if we’re talking about popularity, every single browser today ships with support for features like grid, custom properties, service workers and web components. No third-party framework can even come close to that install base.
And the fact that these technologies have shipped in stable browsers means they’re vetted. They’ve been through a rigourous testing phase. They’ve effectively got a seal of approval from each individual browser maker. To me, that seems like a much bigger signal of trustworthiness than the popularity of a third-party library or framework.
So I’m kind of confused by this prevalent mindset of trusting third-party code more than built-in browser features.
Is it because of the job market? When recruiters are looking for developers, their laundry list is usually third-party technologies: React, Vue, Bootstrap, etc. It’s rare to find a job ad that lists native browser technologies: flexbox, grid, service workers, web components.
I would love it if someone could explain why they avoid native browser features but use third-party code.
Just like jQuery dominated the front end yesterday, React dominates it today. There will be something new that dominates it tomorrow. Your design system team will continue doing the same work and incurring more and more costs to keep up with framework churn. And let’s not forget the cost of updating tomorrow’s legacy apps, who are consumers of your soon to be legacy design system.
This is a terrific analysis of why frameworks exist, with nods to David Hume’s is-ought problem: the native features are what is, and the framework features are what somebody thinks ought to be.
I’ve been saying at conferences for years now that if you choose to use a framework, you need to understand that you are also taking on the philosophy and worldview of the creators of that framework. This post does a great job of explaining that.
Our mental model for how we build for the web is too reliant on canned solutions to unique problems.
This is very perceptive indeed.
A thoroughly researched look at what our baseline criteria should be for making websites today:
The baseline for web development in 2022 is:
low-spec Android devices in terms of performance,
Safari from two years before in terms of Web Standards,
and 4G in terms of networks.
It’s somewhat damning to Safari to see it as a baseline browser, but with Internet Explorer out of the picture, something has to be the lowest common denominator. In this case, Safari is quite literally the new IE.
Prompted by the rhetorical question at the end of my post Today, the distant future, Jim casts his gaze into a crystal ball. I like what he sees.
However, I also think it’s possible—and dare I predict—to say we are peaking in our divergence and are now facing a convergence back towards building with the grain of the web and its native primitives.
Why do I say that? In our quest for progress, we explored so far beyond the standards-based platform that we came to appreciate the modesty of the approach “use the platform”.
He also makes one prediction that lies within his control:
This blog post will still be accessible via its originally published URL in 2036.
I’d recommend going in the order HTML, CSS, JS. That way, you can build something in HTML, add CSS to it as you learn it, and finally soup it up with your new-found JS knowledge.
Excellent advice for anyone new to web develoment.