Why I hate your Single Page App
Stefan Tilkov
84551

First off, thank you for sharing your thoughts about SPAs, but I have to ask, what year did you write this article? Not only do you group the idea of all SPAs in with uber frameworks like Angular and Ember (neither of which are the only way to build apps in JS, you should follow the Unix philosophy and build your apps from small composable pieces, which is the Node way), but you also gripe about a lot of issues that are non-issues anymore. Unlike your article, I will provide examples of what I am talking about.

Maybe your single page app is different, but the ones that I know break most of my browser’s features, such as the back and forward buttons, page refresh, bookmarking, sending a link, or opening a link in a new window or tab. They offer no way to link to something that I look at.

The issues surrounding history management and deep linking have been worked out for awhile. As mentioned in other comments not only do the big frameworks have routing libs now, but there are standalone routing libs as well that let you structure your application in route based components which makes it very easy to navigate forward and backward as well as share app state with deep links.

They are bloated and slow to load, even though the actual information they display and the interaction they offer is very simple.

While this may be true about the SPAs you tend to use, this is by developer creation, not a problem with JavaScript or single page apps at all. Not to mention that server side rendering fixes this in no way. You may have to wait to load the app, but then it is cached until there is a change. Next time visiting you should be greeted by a UI rather quickly (again, unless the app is just clearly badly written, but I have seen plenty of server side apps that are horrible as well). Not to mention that server side rendering requires a refresh EVERY SINGLE TIME I CHANGE THE STATE OF THE APP! That is just horribly inefficient in most cases. At least with single page apps a good developer (or team of developers) will create a skeleton layout and then populate over that once data hits the page. I have yet to see an SPA that displayed a white screen while waiting for the page to load like server side apps.

I can almost hear you say: “But I don’t care about the web. I’m building an application, not a website.” To which I say: Go ahead then, build an application, using your desktop’s or mobile operating systems native toolkit, or build an applet, or a Flash or Silverlight application. That’s all fine by me. I don’t even object to you building the same thing using the browser as an application runtime. Just be aware that when you do things this way, your application is on the web just as much as if you had built it as a Java applet. Be aware of what you’re sacrificing.

Honestly, I don’t even know how to respond to this. I have to ask again, what year are you living in? Even if an SPA is overkill and you need some type of embeddable widget, you should not pick a single technology you mentioned. Flash is being stripped from every runtime as we speak. Silverlight is not in Microsoft Edge and with IE 8,9, and 10 no longer supported that means Silverlight will soon be gone. I won’t even talk about Java Applets, WTF? If you don’t need to build an entire app you should be looking at something like Web Components, React components, or jQuery plugins.

But more importantly, they miss out on the benefits of not choosing an SPA. They’re not aware of the benefits of the alternative. What is this alternative, then? It is to build a classic web application, including rendering server-side HTML, and use JavaScript only sparingly, to enhance browser functionality where possible. In this architectural approach, it is absolutely clear that the responsibility for actual business logic resides completely on the server. This includes the server-side state machine that governs the transitions between pages. And again, this is not a bug, but a feature: It is what enables a quick change on the server side to take effect immediately, everywhere — including the other kinds of clients that you might end up building in addition to your web UI. Business logic does not belong in the client, unless you like having to redundantly maintain the same logic in every kind of client you support (in addition to maintaining it on the server, of course — remember that you can never trust any client). From a server-side viewpoint, the best architecture you can have is the one you could (and should) have built a decade ago: following REST principles, including stateless communication and identification of resources.

Telling developers to build web apps the way we did ten years ago is about the worst advice you can give anyone. Times have changes, technologies have changed. Concerns are not what they once were. The ability to reach people through a multitude of clients has changed the way we need to think about how we store/provide/display the information that users want from us. It also means that we need to change the way we build our applications to better fill those new needs. You say that the server should be a state machine and that you should follow REST principles, “including stateless communication”. How do you accomplish stateless communication when the server is the state manager?

You also said, “Business logic does not belong in the client”, this is correct, that is what APIs are for. What it should say is “state management does not belong in your API”. The server application backing your clients is there to provide data and allow interaction with that data, that is it. Why should another application manage the state of the client? In your Utopia you have one server side app that renders content for all devices. How is that in any way less complex than multiple client apps. I have seen the affects of using a server side app to power mobile and web browsers, it wasn’t very pretty. Ton’s of the gorilla/banana problem starts to pop up when one client needs a feature that is slightly different from other clients. While the gorilla/banana problem focuses mainly on OOP, that same situation comes with inheriting unwanted behavior from extending views and templates.

It is completely fine for clients to handle their own state. In fact they should in most cases. Another issue with your architecture (which you give no details about) is that your build/release process is going to suck big time. Let’s take a look at a real life example of this scenario.

  • Design team says button needs new text and color. (5 minute change)
  • Developer makes changes (maybe an hour of work, maybe)
  • Developer commits and pushes change
  • The entire build is run including all tests for all clients and the server application (who knows how long this takes)
  • The release can’t go out yet because there is a bug in another client that needs to be fixed found by QA. (A few days)
  • The bug is fixed but it produces another unforeseen bug because of all the complex rendering logic for multiple clients is starting to get too confusing. (Another few days)

Your button and text roll out to production two weeks later.

That is a real example that I have seen first hand. Separation of concerns is key, and you will never convince me that my API should be concerned about the state of any of it’s clients. The benefits you portray in your post don’t really give the whole picture. You say not to focus on developer friendliness and instead focus on customer happiness. I think you need to find a spot in the middle. Having great customer focus is awesome, unless you can’t get your product to them quickly, then they will most likely find it somewhere else.

You also mentioned that making a change once on the server allows you to propagate that change everywhere without updating multiple codebases. I say that you must not really understand JavaScript these days. If you are doing JS right your code is broken in to small packages that are shared across your applications (since it’s all JS we can reuse it anywhere). Make a fix to your module and use services like greenkeeper.io to alleviate having to manage those dependencies for you.

Some people object to this because they believe that SPA gives you a better architecture. I disagree: With an SPA, your architecture typically is the one suggested by your framework because from a web perspective, your SPA is a single thing the web is concerned with. With a non-SPA approach (which I’m going to call ROCA from now on), the architecture governing your applications is the one of the web itself.

I’m not sure what you are trying to say, sounds like you’re saying that SPA frameworks like Angular and Ember are opinionated and have a pre-defined architecture that they promote. Oh, you mean like Wordpress, Drupal, Laravel, Django, RoR, and all the other opinionated server side frameworks?

It’s funny because you are accidentally half way right! You should avoid those frameworks but not because you should let the server render your UI, but because JavaScript combined with NPM modules makes it very easy to pick the individual parts you need to build the right framework to suite the needs of your application. There are more JavaScript modules than any other language with a package manager, USE THEM!

A fantastic example of the problems created by the SPA approach is parallelization of work. If you have a team of multiple people, or God forbid, multiple teams working on the same SPA, you need to come up with a good way to support this. Instead, you can just have each of those teams build their own web application. Each of those applications can be connected to every other one built at the same time by the same organization (as well as to every other web application residing anywhere, if you want to) — in fact relying on the core strength of the web.

Again, Node modules solves this problem. Not to mention that that logic would just live on the server and not the client, how does having it be a server-side application do anything more than have those teams work on a different code base together?

In almost every case I’m aware of, your SPA has zero benefit for the user, and there are only positive sides to embracing browser features instead.

This is quite the bold statement to make, without a single example of it’s merit to boot!

I have to say, it sounds like you have actually very little experience using JavaScript at scale. I currently work with JavaScript in a multitude of runtimes including SPAs and I can’t imagine building our application or site, or whatever you prefer to call it in any other manner. Now there are times when an SPA application is not warranted but with the way technology is changing, I’m actually having a harder time thinking of when it’s a good idea NOT to use an SPA.

Moving to an SPA has actually helped us at many previous jobs as well. If you have had such bad experiences with it, maybe elaborating on what was so bad other than solved problems (routing, SEO, etc) and providing concrete solutions would be much more viable than just complaining about it. JavaScript is exploding right now and with it’s ease of use and low barrier to entry, it’s no wonder that some apps are popping up that may give you the impression that JS is not good for building apps. Don’t judge the entirety of an architecture on a few examples. (Speaking of, I would love if you could link to some of these “horrible” SPAs that have you so worked up about the subject.)

Cheers and happy JavaScripting!