5 things where Server Rendered apps beat SPA
I’m a huge proponent of server rendered apps. As someone that started with cluttered desktop apps close to two decades ago, and did his best to push as much of the processing to the database at that time, I know a thing or two about processing stuff on the client. Alongside the desktop stint (that took a better part of a decade), I was searching for (and finding) a better way of delivering apps to our customers. I didn’t have much say as a junior/mid person in that company at the time, but I fought my way into introducing something that in its core was a server rendered web application. This abomination was done in Oracle Apex, which was impossible to version, very hard to deploy to other databases, and induced full body headaches in every step of the process. But it was a start. After the Apex abomination, we played a little with .NET, and then I managed to sell Ruby on Rails to the management, which luckily stuck.
Now this isn’t gonna be a post about Ruby on Rails, or any technological choice in particular. You can probably render HTML from the server in any possible language you can imagine. There are frameworks in some, you have to raw dog it in others, but it can be done. There is a very well hidden secret that I’m gonna uncover now, you can even do server rendered HTML using JavaScript.
Let’s start with the list shall we?
-
You are using a single language/framework to render it Now, I’m not saying you can’t do your frontend and backend code in JS if you wish to do so. But usually we choose a language for our backend, and JS for our frontend code. And it’s not only the language, it’s the whole framework on the backend and another whole framework on the frontend. I sincerely hope you are not doing your own bespoke SPA solution (and even more for the backend), because if you are, the technical debt you are adding increases exponentially with every line of code you write. If you are doing this, you are setting yourself for a nightmare scenario. More importantly, if you suffer from any success in the future, you will have to grow your engineering team. You surely don’t want to train people for 6 months before they are able to contribute to the project because that would be insane. You want them to hit the ground running and have their code in production in less than a week. The only way to accomplish this is by using well-established frameworks. I said single for a reason. Although there are multiple people skilled in any framework you imagine, when it comes to a combination of them, it’s not that easy. Sometimes you’ll be able to find people quickly, most often you’ll have to train them in the skill they are lacking. I.e. if you are looking for a senior Laravel engineer, I believe you can find a 1000 of them available on the job market, but if you are looking for the same skill set, but both in Laravel and Vue.js, you’ll quickly realize that available people with those exact skill sets are in the single digits, and maybe don’t fit your budget.
-
It’s dead easy to deploy the app Established backend frameworks already have the most common ways of deploying them (serving the production package to customers). There are platform as a service (PAAS) solutions for a lot of programming languages nowadays. Heroku supports 7 languages officially, but you can run multiple more using build-packs. You can always dockerize your app and serve it yourself from any of the multiple providers that offer containers as a service. Since you are rendering HTML from the server, you don’t have that much frontend code that you need to serve somewhere else, setting up a CDN to make it load super fast for users around the world. Now I’m not saying you won’t have to do this in the future if your app is successful, but it’s definitely not a thing you should think about from the start. You have to get your solution in front of users as fast as possible, so you can reduce their pains or save them money (or hopefully both).
-
Latency matters Back in the day when I developed desktop apps, there was one thing that always caused a lot of issues. It was latency, combined with poorly written code of course. You can work a lot on the code quality, but there is nothing you can do to combat latency. Although desktop apps were more akin to web servers nowadays, since they connected to the database directly, there were a lot of places for improvement there. Later I learned that some of those issues were N+1 queries, but a lot of it was just hard processing that needed to fetch a lot of data from the server, crunch it, and then push it back to the database. In a “modern” web application, you will be fetching a lot of data you don’t need just to decide whether to render something on a page. This will make sense in the start, since you’ll have only a couple of pages consuming the same api endpoint, but soon you’ll find out that your assumptions about how customers will use the application (and how their data looks) are wrong in multiple dimensions. One way to combat latency issues in a SPA is side-loading in a JSON response. But before you know it, you’ll be side-loading data to render one specific page, then reuse the endpoint and crash the server trying to render a million records on another page. Of course you can go around this using parameterized serializers for different frontend page, which adds technical debt, slows down the development process and subsequently the application itself.
-
Doing things multiple times sucks Imagine having to write every piece of logic twice. This is exactly what’s going to happen when you go the SPA path. You have to write validations in the frontend, so it works fast and doesn’t send faulty data to the API, but you also have to watch out for any malicious person and write the same (and sometimes additional) validation in backend code. If you were just using the backend framework to render HTML, you’d do the validation once and be done with it. Writing new features or perfecting old ones, making your customers happier and earning revenue. Validations are just one example, security is another. There are a lot of things you don’t have to think about when you are the one rendering the HTML and controlling what data ends up in the user’s browser. Authorisation is much easier to do as well, you can scope the permissions to the current page you are rendering in HTML, with API it’s less so. There are methods to secure the SPA in the same way you can secure SRE apps, but it’s hard and gruesome work, while with a SRE it takes a bit of common sense and that’s it.
-
Server rendered HTML is faster Yes, yes, we all know how slick those transitions in dashboard apps look, and everyone wants shiny and new and fast. What if I told you that you can achieve the same slick transitions using some CSS magic, and sprinkle some vanilla JS here and there to achieve the same dynamic feel. If you are writing a business app, your main competitor is MS Excel. I’m not saying you should make your app obnoxious to look at, it’s just that it doesn’t have to have the same ‘feel’ that Instagram has. In my previous job we used to stress a lot if the whole html page wasn’t rendered under 100 ms, and 50 ms was the norm for us. Nowadays if the API server returns its load under a second, it’s considered good enough. Now imagine a customer is opening your app for the first time (which will be after every deployment since they need to reload the code that changed). They have to download a couple of MB of your app’s JS code, then the same amount if not more of the JS dependencies, and then the data itself from the server. This can take a lot of time to do on a slow connection. If it takes too long, they will give up and find something that will solve their problem faster. Clicking on a link and having the page render in the blink of an eye saves your customers’ time and money, letting them get in, do what they want and get out as soon as possible. No one likes using your app, it’s a tool that helps them achieve a goal, fix some pain they are having or save them money. While we are on the speed topic, you can very easily cache HTML fragments. There are frameworks that allow you to cache nested fragments, with a very simple cache invalidation strategy that bubbles up. This way you will make less trips to the database, and use less processing/rendering power on the server, just reading the fragment from your lightning-fast cache store.
Comments