"I think this is a very underappreciated aspect of SPAs. Stateful software is always more difficult to work with than stateless. The frontend state is added on top of the already-existing backed state. This requires more development time, increases the risk of bugs, and makes troubleshooting more difficult."
But the thing is, almost as soon as you have any kind of significant user input to handle (multipart forms, date pickers, autocomplete elements, conditional input elements) you are instantly dealing with complex frontend state. And then your choice is not "SPA vs plain HTML forms," it becomes "SPA vs (ad hoc jQuery tangle + ad hoc server side endpoints that know to speak to random jQuery UI libraries)."
I find the ad hoc jQuery+endpoint tangle much harder to reason about than something built with a front end framework and a more uniform API.
In my experience, this is false.
In the last two years I've built five very complex forms for the healthcare industry that not only had crazy looking flow-charts, but had to be multi-lingual, predictive, mobile-friendly, and fully accessible.
They use all those little bits that you talk about - multi-part forms, date pickers, autocomplete, and conditional input.
The only JS is for the autocomplete. The rest is all HTML5/CSS3.
What I imagine happened in your tests is, you didn't compare two different implementations of the same UI, you implemented the form via two different UI's and the users preferred the experience of new one to the old one.
I believe that you compared different UI's because otherwise you wouldn't need user testing: you could just measure the latency and code complexity.
The person you are responding to is saying, for a fixed user interface design, the necessary state management will be the same and your only choice is how to implement it. You say you believe this to be false but it doesn't seem like you are responding to the point.
That person had something to contribute that was worthwhile. Sorry it wasn't a double-blind placebo controlled clinical trial, but it was still useful info and very much related to the topic replied to.
You must've missed the "vs plain HTML forms" part.
The point of the comment chain in question is that a given UI has a need for a given amount of state. That is the point that was "[believed] false" and the point I was reiterating. User feedback is not useful at all for determining whether SPA's will require more state than multiple "plain HTML form"s. Moreover, the key assertion of the article is that this is necessarily the case, a point which is asserted without evidence and used as the foundation of the entire article. If you remove that assertion, the whole article collapses.
What I got is that they built different UIs and found one of those UIs was better than the others; that says nothing about single-page vs multi-page. Maybe it was easier for them to build the "winning" UI as a multi-page app, but I didn't see that being said.
Were the designs and layout of these SPA and HTML5/CSS3 website also identical?
If it's intended for business users, particularly if on an intranet site, it's often very easy to limit the number of browsers and versions you need to support to just a handful.
Can you maybe elaborate a little bit more? I've had to do some pages for configuring industrial machinery, some having over 100 parameters, many of which would affect what other parameters could then be used or selected. Used Vue.js which worked pretty good, but I'm curious of other ways...
I'm working on a product that can fill out very complex PDF forms, as well as automatically generating web-based forms for people to fill in, so I would be very interested in hearing some more details.
After spending some years building an SPA webapp, I'm really missing the simplicity of stateless HTML. The front-end framework my colleagues picked really likes to carry state around, like between pages, which has caused numerous hard-to-find bugs. These bugs are triggered when you do certain sequence of actions across multiple pages. That stuff should be impossible. We really ought clear all state between pages, but apparently it's harder to fight the framework to do this. Carrying state around is just fundamental for it.
I may be being dense, but can you provide me with a hint as to how? I guess the answer is "css", but it's not obvious to me how you would do this.
I got the backend prepared to facilitate this. I just call a function with another function that manipulates a datatype representing the form. It parses the submitted data into that, passes it through my function argument, renders it, and responds with it.
In the example I gave, the HTML table holds the inputs for the row data, but initially I thought the items would need more fields than would be pleasant to work with in the table. What I was doing then was displaying a subset of the data in the table, hold the raw data of all items in hidden fields, and have a subform below the table for new items and item edition. This was somewhat more complicated, specially because of the way the backend framework I was using thought of forms. I had one HTML form that I wanted to treat like multiple, validating one or the other depending on what submit button was used. Getting that to work in Yesod was a little troublesome but I imagine it would have been easier (though less safe) in e.g. Rails.
Anyway, I think continuing that way would probably have also been doable, had I not switched to having the inputs in the HTML table for simplicity.
The way that I look at it is this: if you're going to do an SPA, then do it like you're writing a desktop client application: all content is manipulated on the client, the only requests to the back-end are for data (JSON/XML), and the client typically will maintain quite a bit of state. Complex applications that are ports of desktop applications and sit behind authentication dialogs are ideal candidates for SPAs. Here's an example case study of such an application from our company web site:
The one exception to this would possibly be something like an online manual/help browser application, in which you might be able to make an exception and have the content driven by an SPA, thus allowing for more control over advanced content navigation via a tree view or vertical menu.
In general, though, if you find yourself needing to do a lot of history/page management, then that is a good indication that you might be trying to shoehorn a traditional, page-oriented architecture into an SPA.
That feels like a straw man, you can obviously also use modern js and a decent framework (vue react whatever) to make the portions of the page that need state, transitions and complex interactions very user friendly and easy to reason about without pulling the entire site or section of site up into an SPA. I have seen far too many implementations pull whole sites or large sections of sites up to SPA to mitigate one complex form or stateful interaction area. There is obviously a range of implementations that can be done from SPA|--------------------------|Page-Level and none of the areas on that spectrum require you to do anything in a"jquery tangle" let alone with any jquery include at all.
> Third, if you’re implementing a very complicated component then you can use React just for that component. For instance, if you’re building a chat box then there’s really no need to implement your login page in React. The same applies to Vue.js and the rest of the pack.
Actually, thinking of an application as a series of transitions between valid states is why I enjoy using things like Redux. Used properly, it acts as a gatekeeper that makes it hard to move the application from a valid state to an invalid one. It adds a fair amount of boilerplate, though, which I know some people find frustrating.
Maintaining state on the front end and keeping it in sync with the back end is a pain. But I don't think it has to be. it's just an artifact of us being stuck in a certain paradigm of application development that we don't seem to be able to get out of.
It wouldn't be impossible to design a system that runs over a network, but doesn't require the front-line application developer to think about the network. It sounds like something Alan Kay might have worked on at some point. :)
I envision a Smalltalk-ish environment where, in my application running in a browser, I send a message to an object telling it to save itself. And when it receives this message, it transmits itself across the network, validates its data, and persists itself into a database. Maybe an object database like Gemstone/S. But the application developer wouldn't have to worry about all the details most of the time. Just tell the object to save itself, and it'll report back whether it was able to or not, so you can update the application accordingly.
I'm sure I'm overlooking a ton of potential pitfalls, of course. I think we can do a lot better than we're doing right now, but I'm not 100% certain what better looks like, or quite how to get there. Maybe I just need to watch a few Bret Victor talks for inspiration and then get to work trying to build something. :)
We can envision lots of things. The beauty of software engineering/architecture is you can envision almost anything.
The trick is that the more complex the thing is, the harder it is to pull of _well_, and the easier it is to end up with a monster.
The complicated things take more expertise/skill/experience to design, and _also_ take more development time to produce -- and on an ongoing basis to maintain. And it's not always clear where development resources come from on 'shared commons' like platforms (unless they are proprietary and only available for a fee). The current front-end JS environment is already _pretty darn complex_ of course. How well it's pulling it off, well, is a matter of debate central to the OP.
On, and then there's getting different actors to coordinate in thing that require that (like browser's supporting something).
I can envision it too. But I wouldn't wager much on it happening in a way that actually results in an improved development environment (instead of a monster).
But if we're comparing to crufty tech, I'd say what I was talking about in that sentence was more like DCOM or CORBA. Both of which were nice in concept but a bit of a PITA in practice.
It's not the best user experience ever (the whole page has to reload after each click, and the state is persisted by serializing structs in <input type="hidden"> fields), but I had a pleasant experience when I developed with it.
ASP.NET Webforms components were the best thing until we finally had React/Vue!
The only problem was that it was way too easy to make a mess in your backend code.
Disclaimer: my company sells a web development product that creates SPAs that work like desktop applications.
This makes it very easy to test and reason about.
Does it not access any data?
Here's a thread I posted about some FOSS I've been working on that relies entirely on the server for rendering and using VDOM diff/patching on the server for updates. I'm definitely biased, but in my opinion this approach is vastly more user friendly because it enables realtime data updates as easily as request/response style updates.
here's the thread https://news.ycombinator.com/item?id=18318415
if you need some click bait, here's a gif of me in seattle over cell phone internet interacting with a SSR realtime app with the server in cali. https://imgur.com/z8pKt8t - all operations going round trip, with no application level JS written
You have multiple scopes in the same document. You have the jsp scopes which gets rendered once, when the client requests the page, and the jsf scope which lives longer. Initially when rendering these scopes seem to share variables, but after the request is rendered, the jsp scope gets removed
> Third, if you’re implementing a very complicated component then you can use React just for that component.
There are components that are complex enough to warrant a view library, embedded in an application that's not. In fact, it doesn't take much complexity at all, in my experience. At which point loading a view library a la carte is your only real option.
What doesn't make sense about that?
The complexity involved in using two different libraries that handle the rendering in very different ways (one doesn't even do the rendering, so now you have two rendering solutions in addition to two view libraries. Yay!) in the name of simplifying your code is an absolute joke.
Of course single-page apps generally require far more development than multi-page. Of course they require more state, more testing, are slower to load, etc.
But they also do a million things multipage "apps" can't and, and therefore have the potential to provide a vastly better user experience at the end of the day.
In the end it's same tradeoff as literally every other business decision: will the resulting benefits be greater than the costs they incur?
My dentist doesn't need a single-page app for their site. Anything complex enough to intuitively be called an "app"... there's a good chance it does.
I will add that I think "vastly better user experience" is very situation-dependent. Many applications put on the web (be they client-side rendered, server-side rendered, or a mix) are really boring simple things: fill in a simple form and get some basic information. A lot of users and businesses really don't value that much what an SPA can do. They don't think they are "vastly better" - sorry. In many cases an SPA doesn't necessarily give you the benefits worth the cost, especially since there are many costs. In other cases an SPA is obviously the right way to do a job, and it'd be a poor decision to try to do otherwise.
I wish people would focus on understanding "there are X ways to do this, and here are the pros and cons of each approach". Trade-offs, trade-offs, trade-offs. If we focused more on understanding the alternatives and their trade-offs, the field would be a saner place.
Consider a choose your own adventure book.
Both a single page application and a multiple page application can implement this entirely through the url and I don't see how I have any state other than the url.
If that is too facile, there were choose your own adventure books where you had hit points and could fight monsters. So now I store hit points in the session or in the url or in the single page application's state.
Single page application doesn't mean single url. I don't exactly understand what additional state would be required to implement any particular user experience via a single page application or multiple page application. The only difference I see in either case is where the state gets managed and mutated.
TBH, given the state of modern tooling I'm not sure that SPAs really do require more development (once you have actually bought into the process of using them, which is nontrivial). I do, however, think they generally scale (developer-scale, not performance-scale) better than any multi-page system I've ever worked on.
I like your division of "site" and "app", and it's the same one I use. The boundaries are definitely fuzzy, but if you're working on it--I think you know.
I've never understood this opinion. To me it seems like you're forcing such complexity that you _need_ focused developers. That by definition scales far worse than having all cross functional team of developers that can work on the view layer and the data layer (by virtue of the view layer being easy enough all developers can understand it).
It's touted quite frequently ime that it's better to have front-end engineers and back-end engineers and that to me just feels flat on its face wrong.
I'm not claiming there are no benefits to SPAs, of course. I'm just saying I don't understand the opinion that claims it scales better with development cycles.
1. SPA's still require some back end to render the initial page / payload.
2. SPA's cannot be trusted, so you need to duplicate things like validations.
2a. "But you can just do an ajax call to validate records on the back end, and get a json response!" — how's that any more performant than just doing a traditional page load? With pjax or turbolinks, the performance difference between these options is even closer.
We eventually did some minor optimisations - returning smaller parts of the page, and replacing more precise sub-parts of the DOM. We had some other small pieces of JS on the pages too, but for the most part just having the server render everything replacing large chunks was good enough.
In a way, it's the microservices argument. You don't make microservices because of performance, you make them because it forces less skilled developers to do (more of) the right thing and allows those less skilled developers to parallelize better. So it is with tooling like React in an SPA and an API-driven backend. But, and unlike microservice-all-the-things, I find myself able to crank out more involved features, faster, using React and talking to a backend API in something like Swagger than dealing with postbacks and string-manipulating my way to HTML output. So I think there are wins on both ends.
I don't agree with the microservice comparison because the separation of view/data layers could more easily exist with a back-end rendered application. It only exists more readily with front-end rendering because the complexity is so much higher that you find specialists in that field who can only work with that stack. The separation is purely artificial and driven by increased complexity.
As with stacks, this is highly team-dependent. I for one do not crave for the time of constant back and forth between backend-frontend mindsets and stack lock-in because of rigid query/rendering requirements. This may not work for the author's team of mostly "backend-mindend" devs who wish for more control over output, but is much preferred for a more diverse set of members (or especially departments).
The experience was wonderful and highly recommended. I think it's perfect for small teams who are more productive with traditional/backend tech.
You presented the actual valid argument for the OP's position. If you are a small team of largely backend engineers, using tech you aren't familiar with is probably the wrong solution. Unfortunately, the blog post decided to take a more aggressive stance and made the author just look like a fool.
Making my login page driven by HTML and postbacks, rather than a straightforward REST(-ish) API talked to by an SPA, makes it nontrivially more difficult to establish the kind of separation of concerns necessary to then build a mobile application out of it (and as it happens, React is pretty useful there with React Native).
SPA's also benefit from static caching and serving. You do not always need to have the pages being served from your origin, but can be served from a much cheaper source like Cloudflare or even a different origin.
Is that supposed to be a downside? Just showing a 500 page isn't really a great UX, the SPA experience is probably a lot better (think preserving of unsaved data for example).
Is the better UX during 5xx worth the extra investment?
Does it directly drive more revenue than what it costs in developer time?
I would argue that in most situations the business is better off using 10 hours of engineer time working on some source of 5xx, than 10 hours making sure the error has good UX.
Problem with SPA made here is simply that there isn't an easy "good enough" solution like you have for MPA, it is either good ux or nothing. (I don't know if that is true or if it is just that SPA devs will always insist in good ux even if it doesn't improve company's bottom line)
Whether users return, and how that hurts your business, depends very much on your site and your users.
I will disagree about UX not being about bottom line though (in the usual context of a for-profit company of course). In a common setting, whoever pays developers and designers are doing so as an investment that they think will be profitable. If you can cut costs without hurting (long term) revenue by cheaping out on UX then of course you should (!). But often the idea is good UX will drive revenue...
Or if you want the full power of React, Redux, AND the simplicity of Turbolinks/PJAX. You can use https://jho406.gitbook.io/breezy/ which is Turbolinks for React Redux Rails, you can build SPA without APIs, just by treating JSON as a navigational format.
Full disclosure, I'm the author and I've been trying to get some feedback on the project.
The SPA approach also helps with statefulness, because it allows you to keep the UI state on the client where it belongs. The UI becomes much easier to reason about because you're not trying to keep state of the server and client in sync. Meanwhile, the server can just be a set of service calls the UI queries when it needs data, and doesn't need to store any state about it. This directly helps with horizontal scaling where you can spin up more servers and balance across them.
While SPAs can have a larger initial Js download, they can be a lot more responsive overall because you don't have page repaints, and you fetch data as you need it.
Testing actually becomes easier because your front-end is decoupled from the backend. You can test it in isolation, and the amount of statefulness is dependent on the complexity of the UI as opposed to whether its implemented SPA style or not. An equally complex UI implemented using traditional server-side rendering will likely be harder to test in practice.
Also not sure why authentication needs to be any different with SPAs. You can authenticate exactly the same way as if you did server-side rendering.
>Let's illustrate this with an example: we're building an e-commerce site that has a list of categories. We need to update the list from time to time. In an MPA, the list is updated on every page load. That's not the case in an SPA though.
Why wouldn't it be?
>In an MPA, we can simply pass models to views and render attributes we need. This isn't the case in an SPA. We need to define data formats and implement serialization.
Not really sure what that's about either. JSON exists, and I'm pretty sure nobody is inventing serialization formats in SPAs.
>Single-page apps are much more expensive to build than multi-page apps. In most cases, there is no business reason to choose this architecture.
Perhaps this is just a problem with the particular stack the author is using, and they're extrapolating this to be a general problem?
>Not really sure what that's about either. JSON exists, and I'm pretty sure nobody is inventing serialization formats in SPAs.
I think the author means serialization of models into JSON for your API, which is non-trivial in frameworks like Django - you have to pick which model fields to use, how to represent foreign keys, etc. In Django you can just pass that model object into a HTML template processor (view) in a MPA.
What that means in practice is that:
1. Routing is always handled server side. The less frontend state (IMO/IME) the better. There will be the odd bit of pushState where appropriate, but no actual full-on routes.
2. Whether to render content server or client side is determined on a case by case basis. It's often easier to build HTML using whatever templating system your backend tooling provides than build a REST/GraphQL endpoint for a component to consume.
3. You can introduce React/React-style programming piecemeal to a team with varying levels of experience. Everyone on a team (with varying degrees of frontend skill/experience) can be productive from day one.
Does anyone else do this?
Should huge wizards and complex forms be constantly sent back to the server and rendered as each bit is filled in? Of course not.
But if you have a basic CRUD app with a big dashboard, create route, edit route, view route, etc., it's a hell of a lot cleaner to break up each one of these main features into mini SPAs that cleanly refresh the state on the server when moving from one function to another.
Otherwise, you're constantly making duplicate calls to ensure you've got the data you want as you really can't be sure if some previous click from one of dozens of in-memory widgets has stale or incomplete data.
At least that's been my experience working on a few legacy apps written in AngularJS and, God forbid, JQuery. At that time, an Asp.Net or GWT app would have probably been easier to maintain as the duct tape increased over the years.
Maybe React and Vue allow for cleaner architectures, but older apps were a pain.
On the other hand, there are plenty of times where a multi-page app is just fine. If that's the tool that works best for you, then by all means use it. The vast majority of users don't even perceive things like page reloading, etc. Build something useful, whatever the means are.
This is the correct response, but among developers you will have zealotry on this topic. I personally find MPAs easier, but that is based on my work experience and my perception of the learning curved involved in things like React and Angular (I found VueJS to be far easier to learn). I do like the idea of thinking about an API from the get-go with an SPA, but do all applications need an API? Nope. The amount of applications in the business world that will require both a web implementation and an iphone/android app is actually quite small, isn't it?
Would the user have to chose "32 GB Ram", hit a submit button and wait for the page to reload with the matching laptops?
What I mean: This is an example where a single-page approach is better.
I agree that there are many single-page sites that would be better if they were multipage. But I'm not sure the "almost always" is justified.
The title of the article is even stronger "The Architecture No One Needs". But you and I clearly can bring up examples where it's an architecture that does make sense.
Also, with this SPA, you can't bookmark or a share a URL to a particular hardware search, so information and usability is lost. The URLs could be fixed, but the SPA has to explicitly manage the page history. An MFA gets that for free.
Regardless, I think this is a superior experience. It does require some getting use to because of the screen/price chart, but just because it's different than anything everybody has done in the past, doesn't mean it's worse. I think someone getting used to this reactive approach might find it a bit hard to go back to a more traditional click/wait cycle.
If I have a team of apes slapping keyboards that know Perl and don't understand finer points of OS/servers/state-management very much and see JS as form of evil, then yeah... I'm going to have an MPA where everything complicated happens in a CGI Perl backend that gets wiped clean between every request and probably hand-roll a little bit of turbolinks for them so they don't have to in the spots that will benefit from that.
If I have a team consisting of a single perfect crystalline entity that exists out of time and can pluck the correct solution out of the set of all possible solutions and just start banging out source files in alphabetical order, but BA is staffed by two-headed barbarian ettins that change their mind mid-sentence and can't produce static documentation then we're going to have to account for that least common denominator when it comes to our project management style -- super-iterative... maybe kanban, crystal boy banging out a set of feature branches and staging environments for them with big demos meetings of "so.. kinda like this?"
I hate SPAs, so much more complexity and work. In MPAs you can still use JS and AJAX for a better experience, but SPAs just have so much needless extra work and are harder to maintain. They are also frustrating to use. They can be better to use when done well, but you don't often see that unless it's from someone quite big.
I, personally, place blame on WC3 for this state of affairs, as Web components should have solved the UI interoperability problem, observed objects were dropped which regardless of what one thinks of them (I think a trie is better) would have unified state management clearly (replaced by Proxy? yeah, I see everyone using that string-only thing for state), and various other specs should have solved most of these issues, but I guess they were too busy making people leave their org over DRM, rather than making a useful, consistent, easily-produced internet. Who needs standards when all of the for-profit board members have their own un-interoperable libraries?
The complexity of state management must exist within an app. Either it's stateful server side or stateful client side.
Going away from the SPA pattern doesn't save anything in this regard.
The only unique complexity SPA brings to the table is route management, which can get confusing, and a non-trivial learning curve.
I think we could make a nice color coded calendar or almanac to celebrate these more. Maybe even create more formal holidays. I’m inspired by the Giant Man radio interview I heard on NPR this last weekend. Highly recommend it, by Hillary Frank.
I'm no gilded dev, but 5 years back when PHP wasn't avoided like the black plague we would still need to update user interface handling these internal server errors. It's judt good design practice.
I more or less agreed/understood with the writer up to that point. Everything else past this just sounded like a argument for argument's sake.
It's understood that this can also happen in an MPA, but I think the blast radius is much smaller.
Most of the problems are solved by using Haskell+Elm+Generating the API types (sharing the code for their encoding/decoding). Testing the backend is way easier through API, and testing UI is better done with hands/eyes.
Without that second part, this is just a trite rant against SPAs.
Wrestling with this idea. Been using create react app for most of my projects that arguably do not need it. But the development is so much faster and easier to manage than traditonal html/css webpack
There are plenty of MPA's with slow first time loads for instance.
I don't agree with this article. I think its author has a significant misunderstanding of SPAs and overstates some of the potential issues. Having built both type of applications at varied level of complexity I absolutely don't agree with the notion that SPAs are necessarily more expensive than MPAs. I'll address a few of his points, but most, if not all, of his claims can similarly be refuted.
Statefulness: Few people would build an SPA on top of a stateful backend? Most SPAs query an API which nowadays will more than likely be hypertext driven (read stateless). The user authenticates, gets an access key, then serves that access key along with each subsequent request to the API that needs it. The server does not keep a session around. It receives the query, does its job in the confines of what is requested and serves back a response (likely in JSON). That's more or less it. There's very little state involved here.
Testing: Vague unsubstantiated claims. Why make backend and frontend talk to each other when mocking would do just fine.
Performance and network latency: Has anyone been using an SPA recently and had this as their main problem? If your client is incessantly polling your API, then it's a sign that either your API is not designed well enough to address the problem domain, or you need to employ a caching mechanism on the front. There's no cookie cutter way to architect an API. If your client is more likely to query and use 10 items from a collection, don't force it to make 10 requests, provide a facility to bundle them up into one. On the front-end most frameworks have satisfactory libraries to handle state and cache management.
Slow first time load, multiple times per day? Stop updating your live bundle multiple times in a single day.
Authentication with JWTs: JWTs are not an SPA requirement, they're just one of many approaches to authentication. I don't use them and don't see the point of them in most apps that I've built. If you do use them and need to keep track of them (maybe because you'd like to be able to invalidate them), store them in something fast like Redis on the server.
Error Handling: You catch the error from the http response and call your handler. That's practically like muscle memory whenever one does an http request.
// handle response
The rest of the article can similarly be addressed.
My conclusion is simply that if you want to maximize your cost efficiency, build a team that is familiar with their tools. If all your developers have done and are familiar with are MPAs, then yes, it might be more costly to build an SPA for them at first. I should know. I went through that phase at the beginning of my recent JS foray. Doing anything was slow and would've taken me a fraction of the time by simply playing around with Jinja on the backend. But fast-forward a few months, after immersing myself a bit more with the ecosystem, I cannot imagine reverting to that approach to build anything more complex than a simple data driven app. Emphasis on the simple.
The quality of an end product doesn't have as much to do with tools as people would like to believe.