SPA vs. MPA, FIGHT!Getting realistic about Single-Page vs. Multi-Page applications.
SPA vs. MPA, FIGHT!
Whenever you see two factions warring in the technology world the answer is almost always: "Both." The endless debates about SPA vs. MPA are very much this kind of situation, but because everyone is deeply invested in one or another technology their arguments are mostly unrealistic. This blog post is an attempt to drip a tiny amount of realism into the debate by giving a kind of oral history of web development in the 90s to explain the power structures in place today.
To understand the subtext of the arguments between MPA and SPA fans you have to understand the history of web development leading up to the invention of SPAs. Once you know what came before you can begin to understand why some people hate SPAs, and it's not any of the reasons they typically give.
My course currently teaches Svelte, which is solidly an SPA. I am planning on adding Alpine.js because I also teach classic Model-View-Controller MPAs, and I need a "bridge" from MPAs to SPAs. Alpine.js seems like the perfect in-between technology being a framework for simple reactive UIs, but not trying to be an entire SPA system like Svelte.
I view my job as an educator to be one that prepares people for surviving through all these obnoxious arguments technology people constantly have. The most capable student is one who knows how to make both kinds of applications, so that's what I teach. I'm mentioning all of this just in case someone thinks that because I have Svelte in my course that I'm somehow biased against MPAs.
I'm not biased against MPAs, but through a lot of research for this course I've found Svelte to an easier starting technology for beginners. I'll cover why this is later in the post, but let's just say I only need to make 1 file to get started with Svelte.
Remembering the 90s
I was there. I was writing web apps using CGI taking credit card payments shortly after the internet was deregulated in the 90’s. The irony was before you got paid for web apps you had to get your clients a T1 line and set it up and also set up their network and mail servers! The knowledge was still pretty arcane. I’m about to have a trauma flashback now lol.
At the dawn of the web every computer sucked. Servers sucked. Desktops sucked. There were no phones with web browsers. It was all junk desktops with tiny CRTs over slow AOL 56k internet talking to fairly slow servers with web servers that would tank if more than 100 people connected at once.
You would make your design in PS, cut it all up and align it as background images in tables. Lots of WYSIWYG HTML code all over the place, rendering differently in different browsers. I remember making invisible text of really small sizes to get smaller row/cell heights.
<h1> tag in Netscape with CSS, and since Netscape was king we all had to use
<div class="h1"> instead. IE got padding "wrong", but weirdly, right, and still lost. It was a mess.
I remember doing a website using java or flash or something to take an image, and make it look like it was a video with waves. That was the penultima of my web career. One thing I remember is how user un-friendly everything was, especially documentation. Setting up a working web cart in those days was magic.
The situation was so bad inside browsers that there was a market for software that ran inside the browser just to get a decent GUI. Flash and Java Applets were the two biggest examples, and Microsoft had Active-X. NPAPI was Netscape's answer to…Java Applets? Active-X? I actually have no idea what NPAPI did. Then you had all the editors that tried to help like DreamWeaver and Front Page.
The Ridiculousness of the 90s
We would receive the requests from the user--every single click went to the server--to process their UI events, talk to the databases for them, take the data and push it through raw nasty string templates, and then pass that back to the client. If you think about that really hard, this is ridiculous. Doing the rendering for the browser?! But, as you saw from people's quotes, it really was necessary to avoid all the difficulties inherent in the browsers. Every other week some new company had some new thing that was going to "solve" the web, and some Pointy Haired Boss would walk in and slap their box of software--yes, a real physical box with CD ROMs in it--on your desk telling you to use it because they paid for it.
It didn't feel awful back then at all though. And that's actually the interesting thing about this looking back. For me it just was the way it was and I tried to get along with what I had and knew.
The ridiculousness of the situation is that every user interaction involved usually pointless network traffic to the server. Every tiny little change to the UI required this, but it wasn't a bad idea back then. If someone is using a Compaq Presario over an AOL connection then this is what you want because asking their computer to change a button's color might set their house on fire.
Even more ridiculous is how tightly coupled all of this was. If you wanted to change the color of a button you almost always had to completely redeploy the entire server. That's because the server generated all of the HTML, so if you change the HTML then you change the server, and that might break everything.
Dreamweaver and its Version Control System. Yeah the locking files type of version control, yeah integrated directly with production. Yeah and the manager could unlock files and screw your work up, and PHP of course, and Apache... and PHPMyAdmin (MySQL and friends)
The Basic Operators From Hell
Running everything in the servers gave the programmers who worked on those servers a lot of control, and many people resented this control. Every change you made required begging a group of developers with total control over the code. You want to change a button? Gotta talk to the Database Admin, the System Admin, the Server Admin, the EJB Manager, the Java Developer, the Template Developer, and then finally the Designer got their button change.
There was a lot of job security and power in being the person in charge of the servers, because the entire business went through the servers. Not many designers could grasp all this code to make their own changes, and even if they could they wouldn't be able to deploy the change without going through these gatekeepers.
The Current Situation
Even if Quirks Mode and doctype switching were a thing, I feel as if learning web development was easier in the late 90s. There were less parts to learn, they were simpler and learning them was done incrementally. At least, compared to the modern webpack/react/js stack.
Users also expect their UIs to react in incidental asynchronous ways, not because of direct interaction. If they scroll to the bottom of a bunch of videos on TikTok they aren't going to click "next page", they expect the next videos to just load. If they make a mistake in a form they don't expect to wait for their browser to talk to the server, they expect immediate feedback. If they chat with someone, they don't expect the page to reload every time they send a message.
Today the user base on the internet is also far larger than it was in the 1990s or even early 2000s. Because of this far larger internet population we also have far higher costs on the servers. More traffic means more CPU spent making HTML for the browser that could be spent calculating the best ad to show them. Meanwhile, the browser is far better at rending HTML than a server ever will be. It's literally the browsers' primary job.
In this current environment the 1990s style of making servers spend CPU time rendering HTML for browsers seems even more ridiculous, but is the MPA entirely unnecessary now? It may be ridiculous, but there are very valid and specific reason to render the HTML on the server even today, which I'll get into later. Remember, if you're practical about this, you'll realize that it's ridiculous to do work for the browser, but there are definitely situations where that's exactly what you want.
I should also mention that many of the people advocating for MPAs also work at companies that love to track your every click. With an SPA the network traffic that you can track is reduced, which most likely cuts into the bottom line of the click tracking industry.
The reason I'm talking about how terrible the web was in the past, and how it's not like that anymore, is to bring up the real motivations for the arguments on both sides. The vast majority of programmers working in web development today are either from the 90s limitations, or directly descend from that era without realizing it. Every framework until maybe 2016 assumed that it was still the 90s, and they were designed to do everything in the server.
The emergence of SPAs threatens to undermine this control. With an SPA you can actually change the entire UI without doing anything to the backend. I frequently rework whole UIs without ever redeploying my little JSON server. If people are able to work on the whole UI and deploy it without needing the people working on the backend then the power dynamic has changed dramatically.
This is why you see people who work on Ruby on Rails, Django, Pheonix, and other classic MVC web frameworks criticize SPAs with FUD. That's also why they produce their own answers to SPAs that still require redeploying a server on every change to the UI. They're desperate to maintain the control they have over the entire stack.
The people pushing SPAs however seem to be motivated by capturing control of the UI since that's the thing which is viewed as most valuable by the people in charge. To the people who run businesses, the UI is what makes them money, so the people who control the UI are the most important people in your company.
Keep in mind I'm not making any ethical judgement on this behavior, just pointing out how I see the real motivations playing out in the drama of MPA vs SPA. If you want to actually use these technologies it helps to understand these motivations so you can take what these people say with a large grain of NaCL.
Control vs Interactivity
The real issues that matter in the decision of MPA vs SPA comes down to Control vs. Interactivity. It's not performance. It's not FOMO. It's not bloat. It's not accessibility. Those issues are important but they exist in all software.
It's whether the UI on your site's interaction needs to be controlled vs how interactive it is. These two needs compete with each other because to control the page you have to move the rendering into the server where you can actually lock it down, but doing that removes your ability to make it highly interactive because you're rending it remotely. If the page needs to be highly interactive you have to do the rending in the browser, but that removes your server as a point of control.
The "second computer logout" issue is a good example of this control vs. interactivity dynamic. The SPA transfers the control of the UI to the browser, so when someone logs out on a different computer most SPA apps have no idea it happened. They continue to operate but the user is logged out at the server, so it takes a few attempted requests before the SPA realizes the situation. With an MPA this isn't a problem, because the very next action will entirely go through the server, and the server will simply return the login page instead of the next information.
A similar issue from the other side is form validation. You always need to validate form input at the server, but if you want to provide instant feedback to the user you can't do that. Validating the form with an HTTP request on every keypress is stupid when you have a perfectly functioning advanced virtual machine on the user's computer. It's better to provide feedback validation to the user in an SPA, but then final validation in the server where the real control needs to be.
When to Use MPAs
After all that, here are my recommendations when an MPA is going to be a better choice than an SPA:
When control over the UI interaction is important.
In all of these situations the key component is you have to transmit every user interaction to the server so that the server can decide if the user is allowed to do that. This means if they left their session open and the server logged them out, then the next interaction will catch that and boot them to the login.
When the time spent on the content is long.
If all someone does is visit one page and read it for about an hour, then an SPA is pointless. Same with videos. If they just land on the page and watch a video then an SPA is stupid. This blog is static files for this very reason, because you'll come here and spend some time reading, then probably get back to your other interests. Making your computer download my entire website just to give you one article is stupid in this situation.
When the interaction needs are low.
If the content has no access restrictions, and it doesn't change much, and the user doesn't interact with it, then you really should make it static. There's no reason to have that generated by a slow Ruby on Rails stack.
However, if the content does change, and you have to restrict access, but the user doesn't really interact with it much while they're using it, then an MPA will be better.
Weak client computers.
If the target audience has very weak computing power or internet, then you'll be better off with an MPA and finely tuned low bandwidth HTML. Effectively this situation is much like the 90s when everyone's computers were terrible and we had to do their work for them. An SPA in this situation is incredibly dumb unless you can somehow preload it onto the phone and make it persistent.
When to Use SPAs
For an SPA the criteria is mostly reversed:
When control over the UI interaction is not as important.
If the interactions the user makes are do not need to be controlled--just the data they need for each one--then having an MPA hand craft HTML for the browser is stupid. The browser is a far superior HTML renderer than any server side programming language will ever be, so stop making servers do it. If there's no reason to control every link and button click then simply have the browser do the rendering and the server send the data.
When time spent interacting with the site is long.
Notice I say "time spent interacting" and not "time spent on the site." This is a key distinction I think people don't make. If I'm reading a blog and I want to explore everything this person wrote, I might make 1 click of a link every hour. This is not a long time spent interacting with that blog. A site like Facebook, TikTok, Twitter, or an application used by data entry workers are examples of things where the person has to interact with the UI frequently for long periods of time.
When the interaction frequency is high.
Just like with the interaction time spent on the site, if the user also has to interact very frequently with the site then an SPA is superior. If they will sit there watching a video for 30 minutes and then maybe click one link, then either an MPA or SPA is fine. But if they're clicking buttons for a solid 30 minutes, then the SPA is better. A good example of this is a dating site where people get on, swipe for about 30 minutes very fast, then get off.
Finally, SPAs Are Easier
After all of this I do want to say one thing about SPAs vs. MPAs: SPAs are far easier to use when developing the UI, but only because MPAs don't bother making this easy. When I develop a new UI with Svelte I can create the entire UI, all interactions, the whole design, and mock out the server with one file. I get started by generating that file in bandolier, add it to a routing file, and then done. No need for models, view, controllers, forms, views that are actually controllers, migrations, nothing but one
As I work I can then slowly add the controller, then when the controller is working I can add the models I need, and then I can bring in some queues if I need. The point is I can grow my stack in consistent logical steps from one file to a working UI, instead of dealing with the entire stack right away.
By contrast, whenever I start working on a new UI in most MVC frameworks I have to lay down anywhere from 4-8 files that all need touching and configuring before I can even get a "Hello World" to show up in the browser. I'm forced to interact with every single piece of the framework even if I don't need to right now, and that's a huge burden when I just want to start with the UI.
In addition to this, when I make changes to the UI with Svelte I only need to run a build that regenerates static files and push it to the server. I don't need to go through huge redeployment scenarios that reboot 20 services just to change the color of a button. I just push the new UI, and there's zero down time because I'm not taking anything down. If I change the JSON server then I only redeploy the JSON server. If I change the queue server then I only redeploy the queue server.
There's no reason a classic MVC framework can't also do this. There's no reason Phoenix needs me to layout 8 files just to get "Hello World". I think if MPA frameworks want to compete with SPAs they need to start making it possible to start with just one file and then progressively grow the rest of the required features. They also need to make changes to the UI not require complete redeployments of the server.
That's primarily why I had to create The Bandolier the way I did. It has MPA elements, static file rendering, but primarily I can use Svelte as the primary teaching method because it is easier to get started and learn each piece of the stack in order, rather than all of the stack all at once.