The js ecosystem is frustrating. I’m a backend/DevOps/platform engineer so I don’t write frontends, but I’ve spent many hours working with and optimizing the compiled JavaScript in CI/CD pipelines, deployment systems, and homebrew bespoke SSR Rube Goldberg micro services that a backend calls out to, waits for the js to comeback, throws in a redis cache, then sends down the pipe to the user. The frustrating part is that often times the clusterfuck required to spit out some scripts that make Ajax calls to a backend, shuffle divs around, bind dom events, and push browser state, is so much more complicated than the backend systems that are actually doing the core work for the app.

SSR was a solved problem before SPA/huge frameworks became the often unnecessary de facto, standard. Why am I dealing with a backend app server that is deadlocked and exhausted its connection pool causing cascading failure because its (wrongly) making in-band calls to a micro service someone wrote which is a single threaded node app running a virtual dom and spitting out js that shouldn’t be required to serve up the app’s presentation layer, yet now is queuing up an ever increasing buffer of connections from the backend trying to get the page assembled to send to the user who does not care about any of the js so long as when they click shit, it clicks.

Js is a simple and small language. The runtime it executes in is designed for a very specific deployment target and even there it’s at its limits. Why did we let a tiny scripting language become this beast of complexity and obnoxiousness? Why can I put bundle optimization and tree shaking on my resume? Or that I inherited and maintained a not invented here fueled SSR system that would rehydrate Apollo stores and glamorous styles, for a site that would have looked the same and provided the same FE functionality using bootstrap css and js? Why am I adding extra headers to response bodies to debug if SSR is being cached by the backend properly, or if the caches are being busted properly and sweeping down the Russian doll layers at the react component level? Why, if it was known from the start that search crawlers wouldn’t render JavaScript, and as a result wouldn’t render the content you want indexed, did you not use a friggin backend templating engine that every MVC framework has a million options for, and include a damn partial that adds your js for a signup modal and some Apollo gql queries? All this complexity is for what? Bash is a tiny scripting language with very specific deployment targets and handles mostly presentation and in the what, 60 years it’s existed, shipping a shell script has been as simple as scp’ing a file to a server, which is how easily you used to be able to “build” and ship js. If the Linux OS devs didn’t need that kind of complexity, your hot or not site doesn’t either (: Is your job as a front end developer significantly improved from the days of using jquery or some other tiny no nonsense utility lib? I see the js being written and I don’t see a huge quality of life improvement to justify the chaos these systems introduced to the entire stack, build system, CI system, deployment orchestration, bundle optimization, library bloat and security upkeep, dependency hell for package upgrades, CVE shit show and supply chain security. What is your app doing that all of this is needed, for presentation? Modern web apps are enormous, render slow as hell, and often break expectations set in stone 20+ years ago, like the back button doing what it says on the tin, or inspecting a dom element and being able to actually see the content, not off in some store somewhere and bound to some property event that injects it and removes it as needed.

People have just accepted that this is how FE dev is done now, with jr devs knowing only this insanity and not anything else. I try to delude myself to think that this is fine, it’s better, somehow, and yes I realize that modern frameworks are in some ways better, react components are modular, reusable, transpilers are able to do chunking, lazy loading, tree shaking, gzipping, and compile time performance optimization as it writes the file, but why is this needed for a single threaded language primarily doing network IO and string replaces? Nobody on the FE team at that place with this insanity could grok how it worked, and didn’t understand when things rendered with SSR would be different, or how to debug any of at all. They didn’t run SSR locally to confirm, they didn’t use any tests, and trying to force them to model the request flow between backend micro services, the added complexity of any caching solution, to constantly be mindful of not having access to the window element when running in node server side without special consideration for shims or null objects, is not fair to them, but it’s not fair for people familiar with deploying complex backend SOA systems to be force fed a maintenance and stability nightmare either. There has to be a common ground.

I know my rant is exaggerated and overly critical, but god damn y’all, reel it in a bit, will you? I’m hopeful that things get slimmed down and less complex as the JS community corrects for the over correction that caused all of this and got us here, and I see things that tell me it is happening, so hopefully soon my job won’t have to mentally model and reason about these moving pieces that result in static files on a cdn.

If I can ever finish it, my solution to end the need for SSR at the lib level, which isn’t original by any means, was a background queue, and a pool of headless chrome docker containers that handled it all. Send it a url, the page is rendered in real chrome, guaranteeing runtime compatibility, dev tools api calls will execute some js in chrome console to inline styles and prepare the js, and finally spit out a full html page complete with bundled inline styles and JavaScript, along with any rehydration on load events. No need to have special js packages or servers. No need to think about it at all as a FE dev. If it works locally in chrome for you, it’ll be the same thing from SSR. Some middleware in the backend app to route crawler/logged out/whatever you want requests back to the SSR system if not in cache, serve from cache if in cache, or pass down the call chain and let the backend serve up html with client rendered js if it’s a request you don’t want SSR’d. Still complex but the core of it works for any and all websites, not just one with a special SSR js package and config. For shops with many apps and frontends, it could all run through the same chrome ssr render pool, because it’s just a bunch of sandboxes chrome instances.

Note: it’s been a minute since I’ve been in engineering at all (out of work) so things may be much better now. I also am fully aware that the SSR system of insanity I described above was overly complex from the day it was built, and nowadays many frameworks ship with bundled SSR functionality.