Hacker News new | ask | show | jobs
by diek 1569 days ago
> Rather than waiting for the client to download a JavaScript bundle and render the page based on its contents, we render the page’s HTML on the server side and attach dynamic hooks on the client side once it’s been downloaded.

The fact that they don't make a reference like, "hey, ya know, how _everything_ worked just a few years ago" tells me they think this is somehow a novel idea they're just discovering.

They then go on to describe a convoluted rendering system with worker processes and IPC... I just don't know what to say. They could have built this in Java, .Net, Go, really any shared memory concurrency runtime and threading, and would not run into any of these issues.

12 comments

It's actually not how things worked just a few years ago.

How things worked a few years ago: you wrote SSR pages with one set of tools (like Django Template Language), then hooked into it with another set of tools. If your pages are complex enough, you end up with weird brittleness because the "initial page load" is not handled the same way as modifications of that page.

Now it's much closer to using the same set for the initial load and subsequent edits. This is a net win for people working on the frontend, in theory.

The more nuanced thing is that frontend tooling is so lacking in terms of performance, despite being something that theoretically should work very fast. In particular, having a bunch of language tooling written in Javascript is the JS ecosystem's billion dollar mistake IMO.

Why do you need all that client-side interactivity in the first place? Most interactions can easily be handled with a full refresh, as proven by the very site you're reading this on (hackernews).

Server-side web frameworks even have modern component-based UI templating now, and features like maps can be layered on top as progressive enhancement without this bloated frontend mess.

Something like a collaborative document editor or forms with non-trivial logic.

Also, the interface of HN works because of its demographic.

Yep. I think these examples are probably fine for the SPA style approach. What gets me is the other 98% of web apps out there, that are not collaborative document editors, calendars, or the handful of other use-cases where this approach can make some sense. It feels like a lot of complexity that has been cargo-culted into the mainstream.

Much of the "progress" in web development in the past decade feels like it's just fixing problems that only exist because we're building web applications this way. React, Redux, Typescript, Server Side Rendering.. these are solutions to problems we created for ourselves by using an architecture with a dubious value proposition in most of our use-cases.

Yeah, anything like what you described are going to be web apps and need all that logic. That being said for the majority of the web (news sites, simple message boards, shopping sites, etc) couldn't they all be rendered on the server?

I have to wonder how much of the client-side rendering framework use is mainly from people forming a cargo-cult around "it's what Facebook does" when at the end of the day what Facebook does is way different from your use case.

By strictly numbers, most engineers don't work at one of those tech giants, but at small firms making CRUD apps that don't really need all that responsiveness.

"Majority of the web" is _not_ news sites and message boards and shopping sites. It's bank websites, ERP software, various CRMs (granted, like news sites...), time tracking, logistics sofware....

And, importantly, a lot of those websites have complex admin-side backends to deal with a lot of things as well! Of course in those cases you might say that the UX requirements are lower (true), but when you are basically serving as a DB frontend, you want to be able to do things quickly, even on a really bad connection.

There's a lot of cool work into partials for SSR, but React and friends are also very nice because you can build out complex UIs that operate through an API, without having to figure out a bunch of workarounds for stateless HTTP/HTML (form wizards anyone?)

All those sites have content, which require elaborate, interactive, collaborative editing and reasonably complex, flexible data models. Meaning you’re going to want to re-use much of the work you did for the editing/publishing part and you want those things to be integrated to such a degree that changing and extending things is reasonably ergonomic.
That is why for 20 years SSR frameworks have supported components.
Collaborative editors are the 1% of sites that are actually applications and need a SPA.

But Yelp is nothing like that. What part of the interface is so advanced? It's just a few links and buttons to navigate to pages and submit reviews. A few JS event handlers can handle AJAX/partial updates without an entire React frontend.

There's even stuff like https://alpinejs.dev/ and https://htmx.org/ to make this incredibly easy now.

Personally I think htmx is an interesting strategy because it unifies the rendering tech on the server (you still gotta be careful to properly scope your incremental changes).

"Advanced forms" come up all the time, and are usually when you have even a bit of non-trivial business logic. For example "if people pick this set of options, show this other option" (but you want to avoid having a form wizard, cuz then you're having to track state across multiple page loads). There's also stuff like in business applications, previewing calculations and the like.

"A few JS event handlers" can handle a lot of tiny things, but many B2B SaaS have a handful of pages with a loooot of these things, and at the end of the day you could have your hacks, or you could try to be principled about loading it.

Though it's also about just finding the right mix, there are people who can architecture their stuff "correctly", completely SSR + some JS flavoring. But it requires having people who are really good at backend and frontend figuring out the right pieces and putting it together. That diligence is hard!

More than a few years ago, but ASP.NET WebForms with the AJAX Control Toolkit did this. It was terrible for many reasons, but it did allow you to use one set of tools for everything.
I started my career with .NET and used/abused the AJAX controls too. Some of those sites are still running today, and still remain fast and responsive and simple to maintain.

The new cutting edge with Blazor is even more impressive and a serious contender for non-JS frontends. Similar advancements with Elixir/Liveview and Ruby/Hotwire

Just like JSF frameworks, with Prime being the best one.
> They could have built this in Java, .Net, Go, really any shared memory concurrency runtime and threading, and would not run into any of these issues.

It is funny how you can get away with using the wrong tool for the job in Software in a professional environment. Like imagine if you were hired to build a house, and you decided to build the foundation out of modeling clay because that's what you're used to working with. And then you started to come up with novel methods of hardening modeling clay when it proved not to be fit for purpose.

I guess you can get away with it in software because these kinds of decisions normally only manifest as increased server costs, or moments of users' lives lost to performance issues, which are much less evident to the outside observer.

Funny you should take this example. Unless you're building a skyscraper, for 1-2 floors clay is plenty good: in particular when mixed with wheat or other fibrous bodies (straw type), it's how many traditional houses have been built, and has many interesting properties:

- sourced from local material (use a different type of clay or straw if you need), no need for chemicals or for sand imports from depleted beaches on the other side of the world - recyclable to almost infinity (need a bigger/better house? just tear it down and reuse the materials) - cool in summer, warm in winter if you design it well - lasts for decades if your structure is designed well: i'm not aware of really old examples but it would be a surprise if the structure outlives us all (does someone have resources on this topic?)

So turns out your example was more interesting and less absurd than you originally thought. Just like server-side rendering uses an order of magnitude less resources (for n clients it's O(1) with caching, whereas client-side rendering is O(n)), it turns out clay is the perfect material to use an order of magnitude (or even more orders?) less resources to build your house than if you used concrete source from various polluting industries and endangered sand deposits.

Fair point re clay foundations, but I was talking about the tooling choice for server-side rendering, not server-side rendering itself. For instance if they had implemented this in Erlang or Go they likely wouldn't have run into this GC bottleneck they had to engineer around to be able to exceed 50 requests per second.

I'm a big fan of server-side rendering and am sure it could be used way more often to great effect.

They did acknowledge that, albeit subtly,

> After a string of production incidents in early 2021, we realized our existing SSR system was failing to scale as we migrated more pages from Python-based templates to React.

Apparently their old server-rendered Python app (not called SSR though for obvious reasons) was scaling just fine before the migration.

Yeah... and then this:

> We evaluated several languages when it came time to implement the SSR Service (SSRS), including Python and Rust. It would have been ideal from an internal ecosystem perspective to use Python, however, we found that the state of V8 bindings for Python were not production ready

Wow, that is some convoluted architecture

It seems like the problem here is React

Yeah, you have to do a lot to offset the problem that, out of the box, rendering React on the server is slow.
I see a lot of React hate which I can understand, but don't forget that React allows you to release faster as a big company.

React is a defacto standard that product people know and understand. They can ship complicated features faster across platforms (for example React Native), while the more technical engineers get to solve these more generic problems (dependency hells, integrations, CI/CD, performance).

Alternative solutions would potentially require more technical engineers to build features and thus slow down product development for a big company.

> "I see a lot of React hate"

Pointing out badly constructed solutions using the wrong tech is valid criticism. It's not "hate" to say that React and this SSR setup is used being used unnecessarily for a site that doesn't need it.

> "React allows you to release faster as a big company."

This is absolutely not a rule in any way. Again, using the proper tech for the situation is what matters. Large teams with complex frontends can sometimes move faster with React, but there are million other factors that go into this.

I dont want to name names, but do any tech company actually apologise after their high evangelism to the world and industry and walk back 70% of their decision five years later?

And for some strange reason this mostly happens to Web Development in general.

1. Create and evangelize over-engineered solution (React, Kubernetes...)

2. Watch potential competitors chase their tails & burn budgets adopting hot new thing

3. Quietly drop or sideline solution a few years later

Why would they? They weren’t responsible. The people who championed these approaches have left for new jobs.
I've seen that a few times now, really intelligent people over-engineering and selling a new system, moving up in the company then out. They sell technology that doesn't age well, get a fat paycheck, and never have to live with the consequences. I'm kinda done with that.

Mind you, sometimes it's inadvertent. I'm currently the only web developer at my company building a big system in Go and React. I don't believe they are very difficult or esoteric technologies, but I'm still not sure if they will be able to find a replacement if I decided to move on.

But I don't know what the alternative would have been. Probably keep trudging on with the old PHP + Dojo bombsite, but it would have the same problem because who would want to work with that tech stack? Who would be able to be productive in a 200K LOC pile of shit? I mean even if it wasn't shit, it's still 200K LOC representing a decade of work, dozens of domains and hundreds / thousands of individual features.

Which is where technology choices come in again; use simple and few tools, the problem to be solved is difficult enough already.

They will keep spitting out frontend frameworks that push computation out to the consumer. It’s probably cheaper to develop new frontend frameworks than it is to pay for server side rendering.
Dev nowadays talk about SSR like a new found panacea. Yea if we look close enough it is different from a Java spring or python Django apps, but only by a slight amount imo.

I think it is because a lot of tutorials nowadays talk about how to do X with tool Y, without telling the historical context as to why Y is in use in the first place. Usually the tool is to solve a specific problem in mind. When people discover that the tool doesn't solve their very own problem, workarounds based on the very same tool is devised.

Other common examples are kubernetes and microservices. I have seen startup jumped onto the bandwagon before having real customers where the tool in question is meant for scalability purpose

Soon they will be where we are, shaking their canes as the youngsters of tomorrow discover cgi-bin. Live and let live, appreciate them for their journey, the lessons learned and skills gained.
Not only that this statement "Server Side Rendering is a technique used to improve the performance of JavaScript templating systems (such as React). " can only be understood as a joke.

Really? A technique invented for JavaScript?!?

Yes, they could have rewritten their stack and used different technology on server and client.

Instead, this blog post shows they made small changes that resulted in a much better performing site with less server resources needed.

The idea of dropping server side rendering if the site is temporary overloaded is a good one that you can’t do if built in Java, .Net and Go.

A fork exec web server is not convoluted.

There have been way too many such nonsense in front-end developments. Putting lots of efforts to build client rendered web apps is already weird enough. And now they want to server rendering apps supposed to be client rendered? Then here comes the question: why not simply choose traditional server rendered apps?
The amount of damage you could do with simple string interpolation of HTML/JS/CSS source provided by a plain-ass HTTP server is pretty remarkable if you can use your imagination for 5 seconds.

Getting the desired plaintext documents across the network has never been such a clusterfuck in my experience.

Client side rendering is needed for apps that work offline.

SSR improves the user experience for first time use of those apps and enables SEO.

What you say is subjective but I somewhat agree with your opinion, but only for web sites, not web apps.

Overall I agree Yelp should have stuck with the existing system as their product functions as a site.

> Client side rendering is needed for apps that work offline.

Any .html page works offline. You don't need any JS or framework for that. If your JS-powered page doesn't work offline, it means either it requires online connectivity to solve problems, or its badly designed and does not respect "progressive enhancement" principles.

> SSR improves the user experience for first time use of those app

SSR improves UX for everyone. Seriously most "web" pages these days takes dozens of seconds to load and use a non-negligible percentage of our CPU/RAM. If you want to know what real-world conditions look like for literally over a billion people, run tests from a core2duo (or a similar VM) with 2GB RAM with simulated 10% packet loss and 1Mbit/s bandwidth.

I'm talking specifically about SPAs, I feel I was clear about that.

Yes, people usually reach for an SPA when they just need a site, but SPAs have their uses.

> Client side rendering is needed for apps that work offline.

Couldn't you built a native application and NOT have this problem? It seems like yet another self inflicted problem.

Sometimes you don't have the resources to build a native app, or you need a web app specifically, or you need both.

What's with other's telling people what technologies is best for them when they aren't in the scenario they are in?

> They could have built this in Java, .Net, Go, really any shared memory concurrency runtime and threading, and would not run into any of these issues.

Right, they'd have brand new issues because they were dealing with shared memory.

Like what? Server-side frameworks have existed for decades. It's not difficult to run, and modern languages make concurrency very easy.

JS doesn't have an advantage here, it's just limited to being single-threaded.

Are you genuinely telling me there are no issues with sharing memory between different threads?

Node.js is 13 years old now. I get that a lot of people think it's some new thing that only dumb frontend programmers who only know JS use, back in my day, grumble grumble. But at this point it's established, mature technology, es6 is an expressive language (much, much more so than Go, C# or Java). The multi process concurrency model is sometimes a pain in the ass and sometimes just want you want.

I've used ASP.NET and node.js in anger, have you? Or do you just attack it because you hate the JS of 2010?

> Node.js is 13 years old now.

> But at this point it's established, mature technology

I have to disagree. Day to day I maintain a variety of node.js, scala and kotlin services. By far, the most gotchas have been from the node.js services.

Simple things like validating incoming payloads are made ridiculously complex. You can get a library to do that, but which do you use? And then most of them don't export the typescript type signature so you don't get any type safety unless you re-define the type in typescript.

Let's say you want a tiered caching library? I've spent weeks of my life debugging concurrency issues because the memory store shared objects whereas the redis store didn't (because they were serialised and sent to redis). That's on the most voted caching library for node.

The quality of node.js libraries in general is far below that of scala/kotlin, and the maintenance cost is way higher. You can't easily tell if signatures for the apis of the libraries you import have changed as well. The ecosystem is still moving very fast, and while I agree that es6+ makes it a much better language, it causes its own issues when interacting with older code/libraries.

Expressive as the language may be, in production systems it leads to poorly understood code because of all the ways to do simple things, and the gotchas associated (something i believe it shares with scala).

> The multi process concurrency model is sometimes a pain in the ass and sometimes just want you want.

Give elixir a serious go and see if you ever say this again. Concurrency should be managed, much like garbage collection has been for 30 years.

Simple things like validating incoming payloads are made ridiculously complex. You can get a library to do that, but which do you use? And then most of them don't export the typescript type signature so you don't get any type safety unless you re-define the type in typescript.

I strongly recommend Zod[0]. You write your schemas in code, it validates incoming JSON at runtime, and you end up with either an error or well typed code. The schemas themselves are powerful (you can check properties of strings, etc), and the derived typescript type comes out for free and looks like a human wrote it. A very powerful tool, with a very intuitive interface.

Give elixir a serious go and see if you ever say this again.

Ah you hit me where it hurts - really need to seriously try the Erlang ecosystem.

[0]https://github.com/colinhacks/zod

appreciate the tip on Zod :) It looks like what i've been looking for!
> "Are you genuinely telling me there are no issues with sharing memory between different threads?"

Where did I say that?

I use C#, Go, Java, and lots of JS/Typescript. I like them all. I find C# far more expressive then ES6, but that's just subjective preference. The point is that the vast majority of web frameworks simplify everything to a URL route that runs some backend logic and returns some response (HTML/JSON/etc). Requests are already well isolated and you don't need to worry about threads and memory.

I can't even think of what shared memory issues a typical website like Yelp would have. Can you provide an example?

However if you do need to worry about complex multithreading, memory access and concurrency, then Node is a poor choice. The other language stacks are not only faster but have the proper data structures and ergonomics to handle it while Node/JS is single-threaded, requiring more work and creating more bugs.

I remember from using Tapestry (a Java framework) in the day, it had really aggressive in-memory caching, and you had to be really careful to disable it for components which rendered personalised or user data. We had some huge privacy leaks (which luckily we caught quickly) because we didn't realise it had this behaviour, as it didn't generally show up in local development.
Yes this this this this.

SSR ... you mean like PHP did it ... and most all of the tech did it before.

How did this even make it to Hnews...