Zachariaharticle

SPAs are not goto, nor should they be a go-to

Single Page Applications (SPAs) are all the rage if by all the rage you mean DOMINATE THE WEB DEV PRACTICE. There are a handful of good reasons for this, and a handful of causal reasons, but I have no interest in treating the latter. I only have interest in identifying the good and bad reasons for using SPAs, or, more specifically, the effective uses of this important item in the modern developer’s toolbox.

Necessary semantic accord work

When I’m talking about SPAs, for the purpose of this article, I’m describing a JavaScript-driven (React, Vue, Elm, homespun, etc) web application that populates a single page with different content, generally from XHR requests, based on user interaction, often with a simulated navigation system.

With that having created only additional questions of definition, let’s move on.

The good bits

If the user never loads a page, but only data, we can save bandwidth on the transmission of markup, styling and scripting. We can also save render cycles with efficient DOM diffing.

If we can control the transitioning between different views, we can use our application’s internal idiomatic design to convey these transitions (our pleas for user patience), rather than relying on the browser’s loading bar, or its method of rendering which may in fact be perceived as the page freezing.

If our backend is primarily a data-delivery service, we can easily develop multiple user interfaces (web, mobile, CLI, headless) in parallel with this data source.

If we are poorly trained in web technologies, especially those such as the DOM and the HTML and CSS interacting with it, we can rely on expertise in one tool (JavaScript or some other language and a web framework) to compensate for these failings. Team composition is simpler and cheaper when expertise in many technologies can be reduced to one framework.

What it means

SPAs are particularly beneficial for:

The bad bits

There’s little that is actually bad about SPAs. Mostly, there are problems to avoid, problems that I’ve seen solved in many inefficient ways.

Code organization

Single Page Application is often sometimes interpreted as site-in-a-page. This is a natural consequence of domains evolving together, especially in an iterative development workflow. Using the same user structure in a user preferences application, an administration application, a user-user communication application and the primary data/entertainment-interaction application is quite easy, even if perhaps some of these applications don’t require the same data about a user, for example, email preferences are unlikely to be relevant to the application that allows the user to interact with their media queue. The easy slide into a monoapp monorepo becomes self-reinforcing.

Here, certain clean code guidelines, and the mistaken premise that something within the repo is not a dependency, can not lead to dependency hell, tend to prevent developers from adopting the following strategies to simplify and limit complexity:

The biggest reason for avoiding this is that it prevents us from reducing the size of our script to just that which is necessary for the application. But this also requires defining application boundaries, which can be difficult. Also very worth it to get that optimized bandwidth good-feeling.

Authentication

Reduplication of authentication code is a problem especially for SPAs that tread the path of the site-in-a-page code organization scheme above. Now we’re not merely authenticating on the backend, but also on the frontend, checking access permissions for every page. This tends to kill one of the benefits of SPAs: small data loads.

Breaking pages into applications helps to a degree with this. For example, if the internal (i.e. staff) administrative pages are a separate app from the user pages, then the check for admin privileges need only be done on the server.

Reinventing the wheel

Trying to cram things into an existing application or fine tune every aspect of the navigational experience tends to create demands to define (redefine) user experience, usually at the cost of a lot of work reinventing the wheel.

Here’s an example I run into everytime I use the Elm Packages website: if I enter a search query, click on a package and then press back because it wasn’t what I actually wanted, I have to reenter my search query. The common solution before SPAs was that any search query would be stuffed into the URL as GET params so a user could return to a specific search result set. With an SPA one must rewrite history because a round-trip from the server is not part of the navigation experience anymore, or one must carry those search results into the new page under the assumption the user may return, and decide how to invalidate those results if they return by different interapp means (eg, a hero link vs the back button).

A more intricate verion of this issue I ran into in a codebase: product didn’t want the back button to take users back to the form, but to the page whence they entered the form. The reason was because browsers would leave the data previously entered in the form, and it was seen as confusing. It might look like an edit page to the user who had made a mistake in the form. The problem was there were ways into the page that were redirections themselves, which left us having to decide where to deposit the user in such a case.

Unfortunately that problem is not easily solved. One option would have been to rewrite the history to take the user to an edit page if they pressed back. Another would have entailed a complete reimagining of the user flow up to that point. With great power comes great responsibility: building an SPA puts your users' experience completely in your hand, and the way you observe or subvert their deeply-rooted expectations of how the web works will impact business and developer success.

Dependency hell

This is inescapable no matter what you do. Your own code is a dependency just as much as external code. The only difference is you don’t control external code, so writing hacks to get things evolving at different speeds playing nice together is harder unless you’re in the habit of frequent forking.

You do, however, expose yourself to more vulnerabilities by relying on external code. Supply chain attacks are on the rise, and protestware has suddenly jumped into the news. You never know when the latest version of your CSS library is going to delete all the files on your computer because your president announced a broadly-condemned war. You might never have though it a valid risk, either, but Pandora’s box is open.

What is there then to be done?

Use SPAs! They’re a great tool too keep on a web developer’s belt. But use them judiciously. Consider the tradeoffs. There are pros and cons I didn’t list here, ones I’ve never had reason to suffer or profit from. The best thing to do is to evaluate every case as it comes.