1. 40
  1. 33

    A title describing the same problem from a different angle would be “The mess we’ve gotten ourselves into with single-page-applications

    1. 6

      How about “The proliferation of JavaScript and our failure to prevent servers from acquiring vast stockpiles of such code

      1. 4

        Can you elaborate? Classic SPAs don’t have this problem because all their functions are “client colored” (to borrow the terminology of the post).

        1. 7

          I guess the answer is that Classic SPAs are good until you need some SEO which is probably very common. Hence SSR. Although technically speaking SPA per se don’t need SSR (maybe for performance but shouldn’t be an issue if things were developped correctly by default I’d say).

          1. 15

            I was thinking the same thing. The title could easily be “The mess spawned by organizing the web economy around a search monopoly”.

            1. 9

              IMO, this is the wrong intuition. Categorically, pages that need to be SEO-opitimized are those that are informational. You don’t need SEO for a desktop app, nor would you need that a web app because a web app is the same thing but distributed through the browser (but sandboxed, not making users require a download executables, and on a highly portable platform available on almost every OS and architecture). These two concepts are not the same thing despite both being delivered through the browser; you shouldn’t use a SPAs tech stack for a basic page because information category pages don’t require the same shared state management and usage of device feature APIs that an application might. I can use Wikipedia from a TUI browser because it’s 95% information. It was the exact same issue in the Flash days of not using the right tech and society has permanently lost some content from its internet archive.

              So it’s not “when you need SEO”, but SEO should be a requirement from the get-go in helping you choose a static site or dynamic, multipage application where the server always did the rendering.

              The problem is the tooling. The NPM community instead of having an intuition about the right tool for the job and stating “do not use this tool for your static content”, we have tools that try to solve everything and hide the mountains of complexity that should have scared devs away from the complex solution into the simple one. It should be hard to make a complex pipeline like that of server-side rendering for a SPA. And that easy tooling is riddled with bugs, megabytes of node_modules, and might invite you to start involving more complexity with tech such as ‘cloud workers’, but people don’t find out until they are way too deep in the Kool-Aid. Many don’t seem to see this issue because influencers are pushing this stuff to get GitHub stars and have, ironically, gotten all of the top ranks when searching for a solution (or people were asking the wrong questions without knowing).

            2. 3

              Not the poster you’re responding to but it might be because SSR is a fairly natural leap from SPA-style apps. They might also be implying that it’s my fault, which would be nice, but unfortunately isn’t the case.

          2. 16

            You know, one could capture these “colors” in a type system. One could see the different contexts in which the functions run as, I dunno, “side effects” separate from the intended action of the function. If you had these “wrapper types,” you’d be unable to inadvertently run a function that interacts with Server contexts in a Client context. In cases where either context could provide the same functionality – possibly implemented in different ways – you could implement a kind of “class” for those context types and make your functions generic across their “colors.”

            They’d need some kind of catchy, short name, though. Something that would definitely not scare people.

            1. 9

              Thank you for emphasizing exactly how much the word “monad” has become a fnord. You are describing codensity monads.

              1. 7

                Abilities? :-)

                1. 2

                  Nice – I hadn’t looked at Unison before. That’s definitely an interesting way to encode and name these mysterious container types! Thanks for the link.

                  1. 2

                    Completely agree. I think you could actually have a wonderful experience with this sort of architecture if you had the type system and ecosystem to back it up.

                  2. 13

                    Title of the article needs “JavaScript” in it. SSR in other languages isn’t an issue, this is specific to Node.

                    1. 5

                      I would like a clarification though, there are perfectly nice “SSR” templating languages that have none of these problems. This post is mostly about server-rendering client-side code.

                      1. 2

                        Agree the title is confusing. I don’t think I can change it here but I will before sharing this more broadly.

                      2. 31

                        My initial, empathy-lacking response to this is “if you’re looking for sympathy, you can find it in the dictionary between shit and syphilis.”

                        Alternatively, “play stupid games, win stupid prizes.”

                        The title’s just wrong, though. Server side rendering was easy. PHP, JSP, jinja, django are all examples of that. The special kind of hell this post describes is just what happens when you try to run javascript on the server, which anyone who thought very much about it knew was a bad idea in the first place.

                        The absurd complexity this post is moaning about is 100% opt-in.

                        1. 8

                          Not looking for sympathy, just pointing out a problem I rarely see discussed.

                          I’ve also never built a serious app using these technologies and would do my best to discourage their usage in companies I work for, but that doesn’t prevent our clients (other dev teams) from doing so.

                          1. 9

                            FWIW, based on the “soapy taste” and and “while some would decry” commentary, I was not assuming that you were the one who’d personally opted in to this problem. But I stand by my assertion that SSR is the traditional, easy way to do things and that it requires opting into bad ideas to enjoy the pain your post describes.

                            1. 1

                              I see this discussed regularly under the name hydration. It’s the piece of web development that I don’t feel like I know an elegant approach to.

                          2. 6

                            What the gist calls “SSR” is really better described as the problems of combining isomorphic templates and resumable frameworks. The problems that Node is a weird environment and some code only works there or doesn’t work there is real, but temporary. Eventually that will be straightened out as Deno leads to a more consistent environment between browser and server. (For example, Node finally added atob a million years after they should have.) The hard problem is that people want to use React, Vue, etc. because they are nice templating languages, but they really aren’t designed for rehydration. I think an approach like Astro of separating SSR from CSR with an islands architecture makes more sense, but that cuts against using isomorphic templates.

                            1. 5

                              It’s hard enough to ensure that you’re only using the correctly coloured function in the right place, until you consider that one of the main advantages of this sort of framework is sharing code across the server and the client.

                              Hmm, I’m not sure this is the only benefit with SSR frameworks. A huge benefit is collocation of server colored functions next to the coupled client color function. Take remix, for example: the corresponding controller function (the Loader) is in the same file as the view function (the Page react component that generates html for the route). The route is generated automatically based on the path and file name.

                              The bundler (esbuild) “smartly” figures out what should be run on the server (route, controller, view), the client (route and view), and then creates separate bundles as well as adds logic to automatically call the controller when a user navigates to the route.

                              It’s true that sharing the view on both client and server is important here, but the real benefit of this framework is collocation and automatically linking a route with a controller and view.

                              1. 4

                                Hmm… what if server-side functions required a “IsServer” token to be passed into them, sort of like a capability system with a single “server” capability, which is only available to your application when it’s being bundled for the server (e.g., when Next.js is bundling your app for the server, it adds a global token: IsServer, and token: IsClient otherwise). Then Typescript + appropriate type narrowing in functions shared between client and server would have your back with this.

                                1. 3

                                  The issue with colors happens because of distributed computation; remote continuations and local continuations have differing underlying codensity monads, so they must have different colors. It is unrelated to server-side rendering; any distributed system has to deal with this when they implement promises/futures/deferreds. I go into more detail in my note about colored functions and monadic effects.

                                  1. 2

                                    Deno should help solve a lot of that, its API is close to the Web API, which encourages code sharing and reuse between client and server.

                                    1. 6

                                      The degree to which this is appealing to people has always puzzled me a bit. What code are you looking to share across client and server?

                                      With the caveat that I personally do very little frontend development, nearly all the web-based systems I’ve worked on had only a tiny number of things that I could see would be valuable to share between client and server. Wire formats and validation logic are the two categories that come to mind.

                                      For wire formats, you can use OpenAPI or gRPC or Thrift or whatever to share across client and server without even requiring the two sides to be written in the same language, and some of those tools will give you API versioning and other features too.

                                      Validation logic totally makes sense to want to share, but it’s usually not a lot of code and optimizing for sharing it seems like it has limited benefit.

                                      What else?

                                      1. 3

                                        if you have an existing SPA you can render most of it on the server-side then shuffle it back to the browser for display. then the browser loads the rendered state from the server and starts pulling in any other dynamic bits from your API in the background.

                                        the biggest appeal seems to be the ability to re-use your existing SPA without rearchitecting everything from scratch. that said, I agree with OP in that this makes things pretty complicated.

                                        1. 1

                                          My personal use case, for instance, is that I do a lot of prototyping in Observable, a JavaScript Web-based notebook editor. Over the years I accumulated quite a bit of code (see for instance my personal standard library https://observablehq.com/@sebastien/boilerplate), and would like to run it outside of the just the browser. I really like the fact that the code can run from any browser, but that with tools like Deno and a bit of glue code I can run my most my notebooks standalone. It gives a lot of freedom and the ability to better reuse code across application domains