← Back to context

Comment by onion2k

4 years ago

SPAs are a pattern that's been applied too broadly IMO, but it's going a bit far to call them a mistake. The aims of an SPA are pretty noble - the idea of essentially removing the network round trip when a user clicks on something is not a bad one. It means things are faster, they work if your connection is flaky, they can do things like offline support. Those are good features.

They might not be actual requirements for a blog or a brochure site but it's not totally unreasonable for someone to want their website to work that way. The question is not whether or not an SPA is a good or a bad thing, but whether or not the cost (more code, more complexity, potentially a series of background requests after the initial page load) is worth paying in order to get the benefits.

When either an SPA or a multi-page site is done well most users can't tell which sort of site they're looking at. That should be the goal. Make stuff where the user forgets about the tech. Make a website that just works, that's fast enough for users not to think 'this is a bit slow', and that's coded in a way you can maintain and work on for a long time. If you get those things right then no one can reasonably criticise what you've made no matter how you've made it.

> the idea of essentially removing the network round trip when a user clicks on something is not a bad one.

Practical SPA's have many more network roundtrips than the equivalent server-rendered web interface. Every AJAX request is an extra roundtrip, unless it can be handled in parallel with others in which case you're still dependent on the slowest request to complete. With SSR, you can take care of everything with a single GET and a single POST no matter what. Images load in parallel of course, but those are non-critical.

  • I pretty strongly disagree with this.

    The distinct advantage of an SPA is that, done correctly, cached data lets you render pages instantly.

    Who CARES if the SPA had to make 3xRTT in the background, if it can serve the next page up instantly because that data is already present and cached, it's a huge win.

    The server rendered app will ALWAYS have to wait at least 1xRTT for every new render. The SPA does not.

    Still doesn't make an SPA the right fit for everything, but on bad networks, we get incredibly improvements in performance by rendering from cache, and occasionally handling cache updates in the background. User's fucking love it compared to waiting 2-5 seconds for every page load, even if they were JUST on the page a second ago.

    • > The distinct advantage of an SPA is that, done correctly, cached data lets you render pages instantly.

      Yeah, but full page caching is a thing, and in my experience, teams writing traditional server-rendered pages are much more aggressive in their use of response caching than are teams writing JSON APIs. Honestly, a Rails/Django/Laravel app with smart caching headers and a Varnish instance in front feels more reliably instantaneous than the bespoke caching solutions each SPA seems to invent for itself.

      4 replies →

    • Have you tried to use a heavily SPAed site from a slow, distant (high latency), or metered connection? A SPA that works and feels great from a big city quickly becomes unbearable when internet access isn't as ideal. There are ways to handle this nicely, but maybe 5% of devs actually think about and test that, and no PM will allocate sprint time for it.

      17 replies →

    • > Who CARES if the SPA had to make 3xRTT in the background, if it can serve the next page up instantly because that data is already present and cached, it's a huge win.

      The data is never in the cache. It is for a developer because they forget to clear their caches when testing. For most users the data will not be cached unless they're sitting on the SPA all day.

      So when the user doesn't have data cached that 3xRTT on their spotty 4G signal the SPA pattern is of no help. Not everyone is sitting still on great WiFi. Someone's cellular reception can go from great to unusable just walking down the street.

      2 replies →

    • > done correctly

      Big assumption. This is what the whole discussion is about. Doing "correctly" an SPA is incredibly expensive.

      I can also assure you that when an MVC application is done correctly you can have an equally good user experience.

      > The server rendered app will ALWAYS have to wait at least 1xRTT for every new render. The SPA does not.

      This is an outdated idea of how server rendered apps work. See Unpoly, HTMX, LiveWire, Hotwire, etc.

      I'm personally using Livewire. I make server request in only 2 situations. 1) When going to different pages (I'd need that anyway with an SPA given I need "SSR") and 2) When I'd need to write or read data from the server, which with an SPA would mean I need an API call anyway. Every other interaction is done with Alpine, 100% client side.

    • Unless their cache is cold and then they navigate back before your first render completes. Often we are not the sole source of a piece of information and first load time counts.

      7 replies →

    • > The server rendered app will ALWAYS have to wait at least 1xRTT for every new render. The SPA does not.

      Not necessarily. I think you're conflating caching/fetching and rendering here. You can cache rendered views in the same way you'd cache the data for those views.

      Turbo does this, for example. When you navigate to a cached page it will display the page from the cache first, then make a request to update the cache in the background. So it's similar to what you describe, but with server-rendered content.

      2 replies →

    • Preloading is a browser-native feature, you don't need a SPA for that. And just as often you can't know in advance what data will be requested by the user.

      6 replies →

  • > Practical SPA's have many more network roundtrips than the equivalent server-rendered web interface. Every AJAX request is an extra roundtrip, unless it can be handled in parallel with others in which case you're still dependent on the slowest request to complete.

    This is largely solved with innovations like GraphQL (which you don't need a SPA to use). Pages that require multiple API calls can show their UIs progressively with appropriate loading indicators. For SPAs that have ~long sessions, it's arguably a good thing to have multiple API calls, because each can be cached individually: the fastest API call is the one you've already cached the response for. This is stuff we were doing at Mozilla in 2012, it's nothing new.

    There's also nothing stopping you from making purpose-built endpoints that return all the information you need, too. Your proposed solution (SSR) is literally just that, but it returns HTML instead of structured data.

    • What they're meaning is that the total time is longer for the SPA if you don't go all out (and nobody does).

      SPA:

          1. get html from server (1 roundtrip)
      
          2. get resources (js/jpg/movies/gifs/favicon/...) from server (1 roundtrip)
      
          3. get ajax calls from server (1 roundtrip)
      
          4. process answer from server + update page (not a roundtrip, but not zero)
      

      Vs traditional:

          1. get html from server (1 roundtrip)
      
          2. get resources from server (1 roundtrip)
      

      Also if there are sequential ajax calls required to build the page (like a list -> detail view), it goes up a lot without speed oriented (very bad code style) API design. For instance you need a separate "GetListOfUsersAndDetailsOfFirstUser()" (or more general "getEverythingForPageX" calls). You can't do "GetListOfUsers()" and "GetUserDetail()" separately.

      So to match traditional webpage total load time you cannot do any ajax calls in your SPA until after the first user action. And even then, you only match traditional website performance, you don't exceed it.

      Time until the first thing is on screen however, is faster in SPA's. So it's easy to present "a faster website" in management/client meetings, despite the SPA version actually being slower.

      You can make SPAs faster than traditional websites ... but, for example, you cannot use javascript build tools. Since you need to do the first calls server-side and have javascript process them, and only send the result of the preprocessing, and the resulting dom to the client, after optimization and compression. After that you need to then do image inlining, style inlining, etc. I know react can do it, but does anyone other than facebook actually do that?

      6 replies →

  • SSR isn't a one-size-fits-all solution. Blogs? Sure. Corporate marketing site? Absolutely. Wikipedia? What else.

    A webapp where state can exist in various components is a perfect fit for SPA-s. Clicking a button in a widget and submitting the page with every other state, updating them and refreshing the page makes no sense.

    SPA-s also help with separating frontend and backend development, they are different beasts. I know many backend devs who wouldn't touch frontend with a 10m pole and not because of JavaScript, but because they don't like design and UX in general.

  • People generally have a very bad estimate of just how engaged users are with their sites.

    These people have lives and other shit to do besides spend all day in my web app. There’s a lot more accidental and exploratory clicks than you think and all of the background requests run even though the user is only on that page for half a second.

  • > Every AJAX request is an extra roundtrip

    But not every AJAX request is blocking or necessary for the site to be usable.

  • If you can do it in a single GET/POST in an SSR, you can do it in a single XHR call in an SPA. There's no reason it has to be split up in separate XHR calls.

    You just have to want to do it.

> When either an SPA or a multi-page site is done well most users can't tell which sort of site they're looking at.

And therein lies the problem. In the _vast_ majority of SPA sites I've been to, they are not "well done" by this definition. It is commonplace for things to break, like the back button as the quintessential example, because the developer(s) didn't spend the time to make sure things work correctly. With a non-SPA site, you generally have to go out of your way _to_ break those same things.

I like SPAs for some things (gmail being a good example), but they should not be the default implementation; there should be a _very_ strong argument before the SPA architecture is used.

  • You're just saying that bad software is bad. That's tautological.

    Without SPA you have horrific 10page forms and the challenge of maintaining state as you go back and forth to make edits.

    The hard part is managing state, and there is no way to avoid it if your product is not purely readonly. SPA is one strategy, and a pretty good one.

    • If one style of writing applications is far more prone to having “bad software”, consider that this is a valuable signal about the difficulty of using that style effectively. SPAs require you to take on ownership of more hard to debug challenges and that's an important factor to consider when selecting tools.

    • The GP was not saying "bad software is bad"; they pointed out a lot of footguns that are introduced by going the SPA route. "More code -> more (opportunities for) bugs" is the truism at play here.

>It means things are faster, they work if your connection is flaky, they can do things like offline support. Those are good features.

Yet the reality seems to be the opposite. If my connection is flaky (which it often is) then SPA's seem to fail more often and it isn't obvious what is going wrong. They don't seem to be faster either, long pauses between pages is common.

  • Right. The typical SPA doesn't recover at all from network failures, and you end up poking random buttons hoping that it will reach some sort of consistent state. Then you find that doesn't help and you need to refresh anyway, which is a reboot for the whole 'app'. So much for saving page loads.

I think it's exciting to see frameworks like https://remix.run/ trying to temper the disadvantages of SPAs by relying on web standards and server-side rendering. It's pretty cool that this is a JavaScript framework that can work without any (client-side) JavaScript.

  • Honest question: why is remix the primary example for this now? Next.js has been doing this for years and is really amazing. It seems like a ton of people had no idea this was even a problem they had until remix came along, and now remix is the savior.

    Am I missing something with remix? Is there anything really novel that it does/introduces?

> they work if your connection is flaky,

Not saying it can't be done, but I haven't seen many SPAs that handles well flaky connections. Most stall with no indication to the end user, endless spinners or just broken in some random way.

Those are very good points - I wonder how the author would see, for example, Google Sheets implemented as not a SPA