Rendered at 10:18:21 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
hackingonempty 17 hours ago [-]
> The quick rule: if you need bidirectional, low-latency communication (chat, collaboration, games), WebSocket; if you only push from the server, SSE is simpler and cheaper to operate.
For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
Maybe if you are making many client requests per second there is an advantage to not sending full headers/cookies/etc... on each request but not if you're sending requests in response to user clicks/touches.
Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
clappski 16 hours ago [-]
> The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
In my experience this isn’t true; firstly you’re relying on an implementation detail of the platform that you’re executing on, of which you have no control over on the client side. Secondly, even if you aren’t opening a new connection per request, you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol - effectively a length and a mask to get the contents, rather than some (in http1 land) fuzzy parser.
If you can guarantee you’re hitting http2 or http3 then you might be closer in latency, but due to the complexity of both I would imagine plain http1 negotiated persistent WebSockets provide the best latency.
galaxyLogic 14 hours ago [-]
But, if you can do things on the client (with JavaScript) you don't need to send (so many) requests to the server, making latency less of an issue.
Doing things on the client means user's CPU is doing some work which else would need to be done on the server, for maybe thousands of clients at the same time.
So I understand some people don't like JavaScript, but then I think the solution would be WebAssembly. I mean the point of distributed computting is that the computational load can be distributed. Perhaps counter-intuitively that often also means less need fo communications and latencies.
jallmann 14 hours ago [-]
Sure, but SSE connections in the browser - the typical use case - are persistent, so there shouldn't be frequent re-connecting.
Unlike WebSockets, browser SSE also has a built-in recovery mechanism so you don't miss events across reconnects (`Last-Event-ID` header), although this does require explicit support from the server-side app.
Given that the WebSocket handshake has an additional round-trip, SSE would typically be faster in terms of time-to-first-byte.
> you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol
Both protocols have their own minimal framing, but "an entire HTTP stack" is a stretch. It's hard to argue that `data:value\n\n` is more complex than the WebSocket binary protocol. Simplistic, sure (no binary payloads), but also simple enough to, say, pipe through a regex. Good luck doing that with WebSocket.
crabmusket 12 hours ago [-]
> faster in terms of time-to-first-byte
Yes, but all these approaches are optimised for long sessions, not TTFB. If that were very important (say, ecommerce) you might prefer a framework that can hydrate and send the full page on first request, rather than setting up a socket to get data.
> data:value\n\n
I believe the poster was referring to requests from the client to the server having to traverse the HTTP stack, not data coming down from the server via the SSE stream.
est 5 hours ago [-]
> incredibly simple WebSocket protocol
Srsly bro? Websocket is simple? Talking about handling state persistency and connection partitions, such a headache.
clappski 4 hours ago [-]
Yes, WebSockets are very simple to implement a server or a client for. It’s a small variable size header, 5 or so frame types and a typically constant mask over the contents.
Borg3 3 hours ago [-]
Hehe, yeah.. I recently touched WebSockets and yes, its complicated stuff. But hey, for vibe coders everything is easy, right? ;)
paulddraper 12 hours ago [-]
WebSockets have head-of-line blocking.
HTTP/2 does not.
zero_shift 12 hours ago [-]
But surely it does? It is TCP, even with multiplexing
I thought that was the whole motivation for building HTTP/3 on QUIC (UDP)?
paulddraper 12 hours ago [-]
Well, yes. There is always some form of it on TCP.
But your HTTP/2 reverse proxy won't block fast responses on slow responses.
As long as your WebSocket reverse proxy does the same, you're fine.
(And same stipulation for your client.)
7bit 5 hours ago [-]
h2 does have head-of-line blocking on the transport level.
CodesInChaos 2 hours ago [-]
> just use SSE
And then the user opens your website in a handful of tabs, and everything breaks because having enough open SSE connections blocks ordinary http requests to that origin.
You can avoid that by using a shared worker for all your tabs, but then you lose the simplicity advantage.
formerly_proven 2 hours ago [-]
This is only true if the browser is connecting via HTTP/1.1, then you get the six connections per origin limit. Relevant for localhost:8000 though.
simlevesque 14 hours ago [-]
No mention of WebTransport makes me very doubtful of this article. It's 2026, all browsers support it and it's bidirectional and lower-latency than WebSocket.
opendomain 13 hours ago [-]
I just looked on the w3c
WebTransport is in proposal stage - it is not official yet
7bit 4 hours ago [-]
It's supported in all major browsers. Doesn't have to be published for it to be used. Look at SPDY which was used well before it even reached RFC status and all major browsers supported h3 before it was completed. It's not a blocker.
inigyou 10 hours ago [-]
There's no advantage of SSE compared to Websocket unless you're using a fan-out proxy.
dzonga 13 hours ago [-]
yep 100%. the SSE version is less headache and can easily scale.
rowanG077 2 hours ago [-]
I love SSE. But it has real limitations. One not mentioned yet is that you cannot send binary data.
sublinear 16 hours ago [-]
> Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
Sound advice otherwise, but you could have left this part out of the comment :D
You underestimate how many "sufficiently complicated SPAs" are slapped together low-code projects. Their maintainers have no idea what you're even talking about.
hackingonempty 15 hours ago [-]
You're right and said maintainers probably have never heard of Greenspun's Tenth Rule either.
You and your sibling reply are the same comment and I don't like talking to bots. You sound like a redditor from 15 years ago, but your account isn't new, so I'm not sure.
hackingonempty 13 hours ago [-]
> You and your sibling reply are the same comment and I don't like talking to bots. You sound like a redditor from 15 years ago, but your account isn't new, so I'm not sure.
Sound advice otherwise, but you could have left this part out of the comment :D
xutopia 14 hours ago [-]
Funny he mentioned Chris McCord as the originator of this technique with Liveview. The reality however predates that with Sync in Rails that you guessed it... was also Chris McCord's doing. Rails at the time didn't have the capacity to handle it so it was just a tech demo then and a big reason why Chris McCord moved to Phoenix. He was once a prolific Rails developer. That guy is helping the web move forward.
ricardobeat 11 hours ago [-]
We were also doing this at Booking.com many years earlier, using morphdom and custom templating libraries. I think I wrote the first version in 2014-2015.
ilumanty 10 hours ago [-]
Why did you stop doing this? What was the successor?
hackmack10 9 minutes ago [-]
So basically, this entire conversation and article is summed up by 6 in one hand and a half dozen in the other in regards to this method vs traditional SPA's.
Would really love to read this in your own words. In any case, I find the rationale for each of your points to actually better support the SSE approach. Your post actually sold me more on why I should not use websockets. Interesting.
andros 13 hours ago [-]
I'm very happy to know that I've made you think, and that you've wisely chosen SSE for absolutely all cases. That's what a senior professional often does: limit themselves to one technology.
wild_egg 13 hours ago [-]
Who is talking about limiting to one technology?
I'm saying websockets are not fit to purpose and I can't see a compelling reason to ever reach for that particular technology any more than I'd reach for COBOL. Sadly, the browser environment gives us very few choices for streaming data connections.
Really though, I suppose I am limiting to a single technology: HTTP. Which kind of makes sense, I am putting hypertext content in a hypertext client over the hypertext transport protocol. If browsers spoke SSH, maybe I'd use that instead. I can't see a use case where adding a second protocol to my apps justifies the large increase in complexity when the capabilities are already better provided by the protocol that I'm already using.
gitaarik 4 hours ago [-]
Now I'm convinced on HTML over WebSockets!
Kinrany 12 hours ago [-]
As a fellow senior professional I too keep vacuum tubes in my toolbox.
throw1234567891 13 hours ago [-]
Less is more.
tommica 14 hours ago [-]
Love the nuance that is going on. Absolute blast to read posts by people that truly understand the technogy they are working with.
andros 13 hours ago [-]
I'm learning a lot. Technology is full of nuances and details.
sudodevnull 14 hours ago [-]
ai slop
andros 13 hours ago [-]
I wish! It would have saved so many hours and drafts….
AloysB 9 hours ago [-]
Hm, Andros I do have my doubts - I know that you are a proponent of generative AI. We had a discussion over your outspoken opposition to https://human-emacs.org/.
I also know that you are an excellent writer/thinker, even prior to the advent of generative AI.
I'm in two minds here, and don't want to insinuate that you are outright lying, but I do believe your workflow relies on generative AI.
> NoDerivatives — If you remix, transform, or build upon the material, you may not distribute the modified material.
If you do use generative AI, it feels hypocritical to use this licence while using a technology that essentially tries to circumvent the copyright issue by remixing, transforming and building upon the material it has ingested.
andros 9 hours ago [-]
This kind of comment loses a lot of its weight by being posted here on HN instead of sent privately. If this were really a good-faith question, a DM would have done it.
I'm not going to follow the trend of putting up an "/ai page" or a disclosure badge just because it's fashionable right now, whether it's Derek Sivers' approach or anyone else's. It wouldn't even fix anything. A sign saying "I don't use AI" doesn't stop the accusations, it just gives people a new line to doubt.
I'm tired of justifying every word, every sentence, every bullet point I write, as if writing carefully were itself suspicious now.
This is the real damage this kind of thing does. It breaks the quiet deal that used to exist between a writer and a reader: you put out content, and got at least some minimal credit for the work behind it. Now there's none of that left. No credit, no gratitude, not even the confidence that a human read it. That's exactly why there's less and less original content being written.
AloysB 8 hours ago [-]
> This kind of comment loses a lot of its weight by being posted here on HN instead of sent privately. If this were really a good-faith question, a DM would have done it.
I'm sorry if it came across as a personal attack, it wasn't the intention.
It felt like the right place to comment. The article is public, the accusations are public, the licence is public and you made your position on generative AI public on the human-emacs project.
You expressed your position on the human-emacs publicly and we had a good discussion over it, in public. To me, this was on the same level.
I don't think that half of a conversation should be done via DM, while the other is made public.
> This is the real damage this kind of thing does. It breaks the quiet deal that used to exist between a writer and a reader: you put out content, and got at least some minimal credit for the work behind it. Now there's none of that left. No credit, no gratitude, not even the confidence that a human read it. That's exactly why there's less and less original content being written.
I agree, but you have to blame the right entity here. It's not the readers' fault that a deluge of AI generated content flooded the internet.
Generative AI broke that "quiet deal", not the readers. The readers are as much a victim as is the author who writes the content.
Generative AI came with a ton of negative side effects for the entire population. This is one of them.
Interestingly enough I read your article in elfeed, and came here for the comments as I was getting suspicious of the use of generative AI, which I refuse to read.
(Cue pivot to "oh, I only meant that it wasn't slop, I never pretended that I wrote a single word of it.")
KolmogorovComp 12 hours ago [-]
> The number is real. But there's a catch.
> The gap vanishes. It isn't measuring WebSocket against SSE, it's measuring
> And here is the deeper part the reply misses:
> In short, it isn't a structural problem, it's a design one.
> The real argument isn't the wire, it's the architecture
> Where SSE genuinely wins
> What really separates the two models is the architecture
lukeholder 11 hours ago [-]
Yep all very clearly AI writing.
andros 9 hours ago [-]
You guys are exhausting… I don't know whether to laugh or cry.
JodieBenitez 3 hours ago [-]
Here’s a little analogy: I’ve been composing electronic music for 35 years now, and some people still think it’s just a matter of pressing buttons, that it’s easy, that anyone can do it the same way, that I do this because I don’t have what it takes to learn to play a “real instrument” and that I’m not a “real musician”.
You get used to it, and eventually you stop caring. Too bad for them if they’re more interested in the process than in the work itself.
chrisweekly 16 hours ago [-]
Excellent article. More compelling than TFA being discussed. Thanks for sharing!
pseudosavant 16 hours ago [-]
Definitely a well reasoned counter point to choosing websockets over SSE.
nchmy 16 hours ago [-]
I forgot to link to OP's response to the response. It's mostly Ai slop.
It's a better argument than the Datastar supporter's.
sudodevnull 13 hours ago [-]
the true irony is the article is about datastar, never change hackernews
whitemoonx 16 hours ago [-]
God damn
gwbas1c 12 hours ago [-]
A lot of the people who oppose this technique don't understand context: The right solution to your problem often involves understanding the problem you're trying to solve!
In my case, I work on two Blazor websites: One is standard in-browser WASM with Restful JSON (and some CSV) over http; the other is server-side Blazor that uses the websocket technique that this article describes.
The server-side Blazor approach is for an internal web application that has a lot of quick-and-dirty pages that replace what used to be ad-hoc database queries and ad-hoc scripts. It's not an "industrial strength" web application that requires high scalability, because it's only a handful of employees who use it. It's also a joy to work with. To be specific, we don't need to go through the exercise of designing an API, making sure that contracts serialize, ect, ect, just to slap a UI around what used to be a script.
The WASM page that uses Restful JSON (and csv) is our customer-facing web application: JSON (and CSV) help with debugging; but the cost of making an API is very high. Development on the customer-facing web site moves much more slowly, but it's "worth it" for an industrial-strength site.
Would I build a highly scalable website using HTML over a websocket? Maybe. The issue is time to market: Because you don't have to build an API, you can move faster; but I don't know if scalability issues will arise.
pjmlp 5 hours ago [-]
Which kind of proves the point I made elsewhere about ASP.NET Ajax, as Blazor in spirit is a kind of WebForms 2.0/Silverlight.
avgDev 9 hours ago [-]
I also use blazor server for internal web apps. It is fun to work with and development is quick for a C# dev.
These mean that you can for example have a websocket serve just the new HTML, and then let native browser code figure out inserting it into the DOM, without any dependency. I’m guessing things like LiveView could eventually migrate to this if it becomes standard, and eliminate more of their JS bundle.
nzoschke 16 hours ago [-]
Close but htmx with SSE and dom swaps and morphing gets you there without reinventing any wheels.
Pretty much every web app I build has this pattern in it from day 1, as they all quickly expand to have a realtime inbox and notifications subsystem to support workflows and agents.
nchmy 14 hours ago [-]
now check out datastar for a smaller, faster, more extensible, more powerful version of that
nzoschke 9 hours ago [-]
I have tried it many times and I want to like it but I find the LLM doesn't program in it well.
Maybe it's my fault, or maybe a model training, or something to improve in docs and SDKs?
sudodevnull 13 hours ago [-]
HTMX only does GET, doesn't to expo backoff, etc. Let alone the fact you need more extensions. Try it before claiming victory maybe?
cpill 5 hours ago [-]
not true. does all the verbs. extension, in what?
pjmlp 16 hours ago [-]
Love how DHTML, ASP.NET Ajax, JSF Ajax kind of keeps being re-invented.
tclancy 15 hours ago [-]
Indeed, “The initial learning curve is steeper than dropping in a <script>”. Maybe for you, but I was doing that basic thing in a mix of Django around 2010. It was getting away from traditional form POST and reload the page while still taking advantage of Django’s templating system. I keep trying to find the best answer to this approach across the things available as it feels like it would make vibe coding easier to manage and understand as well.
noopydoopy 15 hours ago [-]
Exactly dear Lord we went the spa route for a reason
mikestorrent 12 hours ago [-]
The problem with the SPA is that the browser is a document platform being abused to run GUI applications. It's missing the core primitives that real UI platforms had in the 90s, like decent smart sortable tables and data bindings. So all that crap is redone in JS ten thousand times by developers of varying talents and the result is, has been, and will continue to be crap usability and hugely bloated sites.
It's time to replace the HTML part of the browser with something else designed for purpose: making rich UIs that use platform-native widgets and mirror the human user interface design guide for the platform. XUL was a step in the right direction.
pjmlp 15 hours ago [-]
The problem is that we went too far, and newer generations are now rediscovering the past.
red_admiral 2 hours ago [-]
> the server sends the HTML already built and the client just places it where it belongs
Mind blown.
A much bigger pet peeve of mine is making a SPA when a bunch of HTML pages would do, and would give you sensible URLs and the ability to open more than one tab in the first place.
When you actually need a SPA, I'd only go websockets if I really need that low latency and your clients are close enough in the first place, if they're half the way around the world on a slow connection you have different design constraints. A chat application works just fine over SSE or similar. Client-initiated requests even work with plain old fetch().
deepsun 15 hours ago [-]
> Place the HTML where it belongs
Well, some drawbacks are not accounted for when replacing HTML parts: input elements lose focus, if some view was scrolled, then it gets unscrolled, jumping under user's pointer etc.
cmoski 15 hours ago [-]
I don't recall encountering these issues. What framework was that on?
deepsun 14 hours ago [-]
Any framework, if you _replace_ DOM elements, not just modify their attributes.
sudodevnull 14 hours ago [-]
things like datastar handle this automatically for you
aitchnyu 15 hours ago [-]
I like the Vue/React/Svelte model of the DOM being a function of the data. For example, in a shopping cart, I add two chocolates, the number against the chocolate, the count at top and a banner encouraging me to reach X total all center around a data structure.
I use Django Ninja, Zod, InertiaJS+Vue and its as easy as using Django's templating engine, but static typing ensures my view doesnt emit unrepresentable data, my TS doesnt accept unrepresentable data, Vue+TS dont allow logic errors in template. AI makes it effortless. Again, the loaded page is a function of the data supplied at the view.
With HTMX, I'm writing several server-side functions to mutate the DOM imperatively and using HTML attributes to call them. Its great for forms but that shopping cart example needs code scattered across multiple functions and templates.
wild_egg 13 hours ago [-]
> With HTMX, I'm writing several server-side functions to mutate the DOM imperatively and using HTML attributes to call them. Its great for forms but that shopping cart example needs code scattered across multiple functions and templates.
That's a weird way to use htmx. I've built a number of large apps with htmx over the last few years and never done this. Seems a great way to have a bad time.
MrBuddyCasino 14 hours ago [-]
Yes I don’t see the appeal of HTMX in most cases. Vuejs has less footguns than React, and LLMs can produce it decently well to at least get started and then refactor.
mikestorrent 12 hours ago [-]
The appeal is literally a "fuck you" to anyone that makes me have to run a build process for a web page, when I used to just be able to click reload and see the reality in front of me in seconds on a machine 1/500th the speed.
blovescoffee 12 hours ago [-]
You can drop react into an html page with a script tag. You don't need a build step. The build step is only requisite if you'd like the full benefits of react, which personally I find worth it. Cached builds are quite fast. All build steps/compilers/etc. exist to add some devex upgrade on top of some lighter-weight thing that many devs find useful.
llbbdd 14 hours ago [-]
The appeal is that it's not React, aimed mostly st people who have a grievance against React for one ill-informed reason or another. In time it will either become React by another name or die out.
mikestorrent 12 hours ago [-]
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
MrBuddyCasino 5 hours ago [-]
Look at the average React site in the wild, and notice that it is dog slow due to the various performance bugs it exhibits. Apparently nobody can get it right.
bob1029 2 hours ago [-]
I know the point is to minimize JS here, but have we ever considered sending raw JS over the socket? JavaScript can be a lot more compact than the final DOM that it affects. Dynamic JavaScript is much more interesting than dynamic html. SSR HTML is a boring, solved problem. I need something more exciting in my life these days.
zemnmez 12 hours ago [-]
>Safer against injection: since the server renders and escapes the HTML before sending it over the channel, an attempt to sneak in a <script> travels as inert text and reaches your neighbor's screen as plain letters, not as code. The same architecture that makes a chat trivial makes it immune to XSS.
I strongly disagree with this point, and in general I've seen the reverse is true. Only the client truly knows how it will interpret especially esoteric kinds of html tags and relying on the server for sanitisation is relying on the system furthest from the authoritative renderer.
altairprime 14 hours ago [-]
> that loose back-and-forth over HTTP weighs more than an always-open WebSocket
This is definitely true for HTTP/0 and /1. Is it still true under HTTP/3, or has the underlying ‘single conduit, many channels’ model improved things if used correctly?
1vuio0pswjnm7 7 hours ago [-]
"Was Top 7 on Hacker News"
Contrast with something like "had 7th most comments on Hacker News"
Perhaps in some cases (b) might drive (a) which then drives (c), and of course (a) can drive (b)
As such, (b) votes and (a) ranking are almost always aligned
However, (b) votes and (c) comments are not always aligned
For example, comments with large point accumulation, i.e., votes, and hence high ranking, usually receive some negative replies (source: personal experience)
This can also be true for submissions gaining points rapidly
socketcluster 13 hours ago [-]
Reading this is especially interesting to me because it's what I've been working towards for the last 14 years or so. Though like this group, it also came together for me piece by piece.
I got interested in this specific idea back in the early days of Firebase I saw someone built a realtime HTML component with PolymerJS called 'collection' and I became consumed by the idea of fully generic realtime self-updating components. My approach is a bit different than OP or that of HTMX though; it's JSON over the wire, not HTML.
I've built a full implementation in Node.js with a set of declarative frontend components.
Livewire existing and working so much simpler than the status quo really deserves attention.
felixding 10 hours ago [-]
I used all of them in various projects: "traditional" Ajax-based SPAs, HTML over WebSockets/SSE, etc. Then I found Inertia.js and never looked back.
With Inertia.js, you get the real feel of an SPA without the complexity of maintaining APIs just for the frontend. You can even make some pages plain HTML (like the homepage, legal pages, etc.), while making pages that require reactivity SPAs.
isomorphic- 14 hours ago [-]
Allowing unmoderated real-time chat with all visitors is certainly a decision.
mattrighetti 16 hours ago [-]
> Less traffic and less latency per action: a single persistent connection avoids repeating the TCP handshake and the HTTP headers on every interaction.
You don’t need a TCP connection for everything.
If you’re optimizing for that you can consider client-side caching which you can instruct using cache headers that every browser support. That usually reduces heavy hitters by a lot, even if you set the browser TTL to 1 minute which is fine for most of the scenarios.
austin-cheney 15 hours ago [-]
HTTP over WebSockets: Sounds like the same tag line I have been using in my project for the past 2 years.
My approach is pretty simple. Since the connection phase of WebSockets is RFC2616 compatible, per RFC6455, you can use the same server logic to connect both.
17 hours ago [-]
coreyp_1 12 hours ago [-]
I hate modern web development.
I was a Drupal developer back in the day, and I loved its templating system (the fact that HTML was assembled on the back end) and it was so powerful! It allowed for so much customization without leaking the internals of your representation to the front end, which was much more secure, IMO. Now, you have to give your templating logic to the client and potentially expose parts of your system to the end user.
Then, along comes MVC, and everyone drank the Kool-Aid, despite the fact that nobody ever actually implemented MVC purely, because it wasn't designed for the web. It wasn't designed for general systems, either. You may say, "you're wrong! You can adapt any system to MVC!", and I would respond that that is not what I mean. I mean that it is designed for custom applications (what you are referring to), and not frameworks which are for general use. You might claim that it is a framework, and I would point out that the nuance is in where the customization can be controlled and distributed. (Side note: I know MVC has been around a long time, but so have I and I remember when ajax was the hot new toy. MVC took a long time to gain traction and to infiltrate everything... and now we have ultra slow websites with tens of megabytes to download before they can even show a blank page. I stand by my statements and distain for MVC.)
I was building my own CMS that went back to server-side rendering, but I stopped because I just didn't have time to work on it. This may inspire me to do it again, only this time I'll use an LLM to get me through it faster.
exodust 1 hours ago [-]
Please revisit your CMS idea. They're still around, like Kirby and others.
You are right about the old templating systems. I still use them. I still use PHP, it's ridiculously fast and mature. It has a readability unmatched by other languages (for devs like me with design background).
I'm surprised to hear CS people like yourself talking up the old templating systems!
> I hate modern web development.
For me, modern web dev is about modern CSS and JS and native browser APIs and all that fun stuff. It's ironic now with LLMs, the people caught in the grinding gears of MVC bloat are benefiting least from LLM.
Well, perhaps I can't make that claim, but the point is LLMs do a fantastic job at focused, single-layer arrangements such as the good old templating systems, vanilla JS and just direct "old school" frontend coding. Why burden ourselves or the LLM with truck loads of layered dependencies or a million steps to hello world? No need but people get angry on this topic though, so I just do my thing in my corner of the web and deliver results for those I build for.
egeozcan 15 hours ago [-]
This gives me too much <script runat="server"> or even JSF vibes.
noopydoopy 15 hours ago [-]
They keep trying to reinvent webforms, bleh
doublerabbit 17 hours ago [-]
> Simple, elegant and fast.
Until someone bombs your websocket server and you then have nothing at all.
simon84 13 hours ago [-]
> What are its advantages?
> There is only one rendering engine, cutting down complexity.
Actually there are 2 rendering, the server sending HTML and the browser drawing it on your screen.
So why not push the logic and send a jpg image of the rendered content itself. Lol
The whole concept of decoupling the frontend and the backend is that they can be agnostic of each other, they need to align but it is not the same skills/concerns.
Serve rendered html like in 2000 and you have recreated a smart way to do what industry spent decades running away from.
jdkoeck 3 hours ago [-]
According to your logic, front end apps thus have three rendering engines: the server (rendering json), the browser, the JavaScript framework. Count the rendering engines like you want, the point still stands: server rendered apps are architecturally simpler.
There is no one size fits all solution. Frontend-heavy projects have their place, server-rendered ones too.
mikestorrent 13 hours ago [-]
There are proxies to enable old computers with old browsers to render the modern web as an imagemap. And of course, 20 years ago I was doing everything you'd want to do with a modern webapp with PHP; fast enough for a thousand users on a dual CPU Xeon.
floodfx 15 hours ago [-]
A few years back I wrote a backend implementation of the LiveView protocol in Typescript (https://liveviewjs.com) and played around with another BunJS-specific implementation (https://hotdogjs.com/). (Also did Java and Go versions but that's another story.)
There isn't an official "protocol" so I had to figure it out by watching the WS traffic and determining how it worked which was fun if not tedious. That said, the more I learned, the more I was impressed by the efficiency and the programming model which felt simpler yet more powerful than SPAs.
I did get to a point where I just got too busy to keep up and over the last couple of years things have changed a bit on the "protocol" side.
But recently (a week ago-ish), I started poking at the old LiveViewJS repo with the help of coding agents. Now that Phoenix is past 1.0 and the JS runtimes (Node, Deno, Bun) have more overlap in terms of APIs and library support, I think it will be more straight forward and frankly easier to get and stay at parity.
radarsat1 2 hours ago [-]
> It is not memoryless request-response: there is a process per connected client that remembers where it is. It is the opposite of htmx, which is deliberately stateless.
This reflects some misconceptions I had about websockets before I used this protocol in a real application.
In practice I found the following to be true:
- Connection can drop at any time, be ready to reconnect, don't depend on that happening on the same server as before.
- Messages may arrive out of order due to how framing works. Many implementations will complete a shorter message before an earlier longer message finishes. Don't depend on stream order for a one request-one response style messaging.
Yes it's a serial TCP stream but it's basically modeling an asynchronous message protocol on top of that. This is reflected in the browser side API which just provides "onmessage" and leaves it up to you to track state.
What this boils down to is that it's great for latency (but arguably superseded by WebTransport), but ultimately you're best off using it to handle a stateless protocol just as you would HTTP requests. If you do maintain state per connection, that state should only be metadata about that connection, such as a connection-level request id, or the caching of some contextual info (eg. user id) to avoid repetition in future messages.
So I usually consider it a transport-layer optimization rather than something my application fundamentally depends on.
At the end of the day with HTTP/2 reusing open connections combined with SSE, you should be able to achieve essentially the same behaviour and even latency. It's still useful to support but I disagree that the transport should dictate the framework style here, they are simply different layers that shouldn't be concerned with each other.
insane_dreamer 10 hours ago [-]
Are we coming full circle? I feel like I was doing this with Ajax 15 years ago ...
hyperhello 17 hours ago [-]
What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
What’s right with the idea, really? It’s exactly what the tried and true preferences of developers have been shown to be: getting in the way of the happy path for no reason.
Now you can have build steps and put story points in Jira and do it all on the server where we don’t have to see it, and the success condition is that the text gets served. Both sides can be happy now.
doublerabbit 17 hours ago [-]
> What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
Overhead, Security, Denial of service to name a few.
You now have two states to track. Is the web-server serving the current version that the web socket is rendering?
Is your monitoring enough? Is your monitoring going to detect if the web-socket server is suddenly taken offline? How do you monitor your web server is alive and ready to serve requests in the time of need?
How do you determine if the socket server is actual offline and not frazzled itself in an event-loop? A stray network packet you never conditioned it for.
If both your web-socket server and the web-server are going to follow the same source of truth for fail-over why not just use the web-server?
The web-server is a tried and true method of serving websites. An application that allows you to easily load balance; tune and enhance with other security features. The best part is than you can actually serve a website without requiring JavaScript.
Enterprise/corporate/country DPI firewalls like to silently block web sockets; any requests you're going to be rendering blank back to the client. Determining if the client is blocked isn't easy and how do you let the clients report an issue if they can't render the support page?
The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Correctly configured DPI firewalls block these upgrade headers so for all you know the connection has been made but the renderer fails silently.
You still need a service to serve the JavaScript fronted so unless you create a WebSocket HTTP server which you've then opened a can of worms; you have a perfectly functional web-server sitting around idly wasting resources.
As I bombard your web-socket server with faux requests slowloris style. Your web-server is alive and as far as it knows your socket server is alive too, how do you determine if the web socket is actually under attack? This adds more complexity in the mix.
It's not wrong per-se. For a infrastructure learning exercise sure. However for anything else it's a waste of time and will cause headaches. The resources and the overhead for it all just isn't worth it. It's a mirage of something that looks opportunistic but isn't.
Furthermore any alterations need testing on both parts. If you were to apply a hotfix for the web-socket side, does this negatively effect the web-server side?
Does it perform the same way in Firefox and Chrome? If Firefox is slower at parsing the JavaScript html json, how are you going to accommodate that?
Rohansi 10 hours ago [-]
Most of your comment incorrectly assumes that you need a separate server for WebSocket but you don't. That HTTP request to upgrade the connection to WebSocket can go to your webserver which likely already has support built in to handle it.
> The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Correctly configured DPI firewalls block these upgrade headers so for all you know the connection has been made but the renderer fails silently.
Slack and many other applications use WebSockets. IIRC there were proxy issues with WebSocket when it was new but that was 15 years ago. Also you will absolutely be able to detect the connection not succeeding.
frollogaston 16 hours ago [-]
Article should've mentioned caching, or lack thereof
ChiperSoft 17 hours ago [-]
Is this satire? Rage bait? Why would you ever do this?
Just make a normal website!! You've invented an MPA with extra steps!
mikestorrent 12 hours ago [-]
Then you'd have to focus on making a website someone gives a shit about, with content worth pursuing. Instead, one can just retreat into the engineering ivory tower, making an ever purer object that ever fewer people can understand or care about.
x0x0 17 hours ago [-]
Responsive site built via server-side render, with a particular benefit for partial page updates. 90% of the benefit of an SPA but 10% of the code: no api, no moving of system of record for state back and forth between database and browser (it's always db), etc. And you can avoid react.
A common use case: eg in Rails, a user has a table open. you can stream new records to the top of that table as they are created in ~5 lines of ruby (a broadcast on the model, and put the table rows in a turbo_frame with a turbo_stream_from somewhere on the page).
There are real limits to this -- you have to hold the update dependency graph in your head -- but the benefits are huge for small to medium amounts of responsiveness.
My experience has been great with Rails/Turbo and htmx.
frollogaston 16 hours ago [-]
I get how there's virtually no API if your client is simply rerendering the entire page each time it gets a ws message, but the article glosses over the partial page updates and just says "place the HTML where it belongs." How would that work? You'd need some kind of API for the client to request page parts and/or the server to tell the client where to place them.
x0x0 15 hours ago [-]
no api. The way it works is thus (rails, because it's what I regularly use):
The system is called Turbo. A turbo_frame is just a custom html element. You can largely use it instead of a div.
Turbo's js watches for navigation events (link clicks, form submits) that originate inside one of these elements, and instead of letting the browser do a full navigation, it:
1 - makes the request itself, against the usual rails routes. It uses your normal routing, sessions, cookies, etc.
2 - Intercepts the html response and looks for a <turbo-frame id="..."> that matches the id of the frame that triggered the request
3 - Swaps just that element's contents into the page.
more concretely: suppose I have a list of people in a flex table. I wrap the people table in a turbo_frame so I can prepend to it, and wrap each person in a turbo_frame so I can update it. My page can look as so:
turbo_frame "people", class: "person-table flex flex-row" <-- the overall list
turbo_frame person_1, class: "person-row flex flex-col"
the row for person 1
turbo_frame person_17, class: "person-row flex flex-col"
the row for person 17
turbo_frame person_12345, class: "person-row flex flex-col"
the row for person 12345
etc.
If the user edits a form for person 12345, that is wrapped in turbo_frame person_12345.
The browser makes the request itself, grabs the response, pulls out the contents of turbo_frame person_12345 (and NB: the response can be a whole page or just this fragment), then swaps that in for the existing person_12345.
It's designed so you can make your normal MPA with page navigations and reloads for editing a table row or whatever, make a small set of updates to the html, and have this work almost entirely for free. It sounds too good to be true but I've been using it for years and it often really is that easy.
edit: for server-initiated updates, I can broadcast to select listeners:
1 - replace a turbo_frame (person_12345 was updated externally, and I want to just swap out)
2 - prepend (eg for a css table, put my new person_12345 row in front of other rows;
3 - append (same, but at bottom)
frollogaston 12 hours ago [-]
This is kinda what I was expecting, but there is a whole library handling it for you. I just noticed the article mentions LiveView which probably does something similar.
x0x0 10 hours ago [-]
Yeah, there definitely is a library here. It is a small library: ~5 klocs. In part because it just does (a lot less) than React and leans more on working with the browser instead of a shadow dom, etc.
The downside is it does less than react. You will mostly have to keep the reactivity dependency tree in your head. Huge win for most b2b apps; A+ for internal admin pages; solid A for config pages in almost all apps; not a great fit for (parts of) highly reactive b2c apps.
17 hours ago [-]
3dedb728-3f77 13 hours ago [-]
All of this to finally come back to old web with html generated on server.
It make me happy.
carllerche 15 hours ago [-]
Topcoat (Rust) is aiming taking this approach as well: https://github.com/tokio-rs/topcoat. The project is still in the early days. It won't require WebSockets, but WebSockets will be an option.
mircerlancerous 15 hours ago [-]
Frankly I think serving dynamic HTML at all is a problem in an SPA. Use a service worker to cache structure, control, and styling, then use an API to fetch dynamic data in a more efficient manner.
cbsmith 11 hours ago [-]
Everything old is new again. ;-)
dalemhurley 14 hours ago [-]
We have come full circle.
frollogaston 16 hours ago [-]
So a RESTless webserver
nchmy 16 hours ago [-]
In what way is it restless?
frollogaston 16 hours ago [-]
Listed under advantages in the article: "State lives on the server. It is not memoryless request-response: there is a process per connected client that remembers where it is. It is the opposite of htmx, which is deliberately stateless."
nchmy 14 hours ago [-]
but if state lives on the server and all you are delivering is html, how is that not "restful"?
frollogaston 13 hours ago [-]
One of the main properties of REST: "Each request from any client contains all the information necessary to service the request, and the session state is held in the client."
In practice, REST makes things like caching, load-balancing, and debugging easier at the cost of needing to send more state back and forth. In HTTP, there are ways to reduce or at least hide the repeated TCP handshakes.
alt227 2 hours ago [-]
I dont get what you are arguing. If state lives on the server you have added complexity of holding state for every user. This is exactly what REST was designed to remove.
frollogaston 2 hours ago [-]
That's what I'm saying, the design in the article is not REST
16 hours ago [-]
cpill 5 hours ago [-]
for normal web stuff this is bananas. for an online, multiplayer game it would be awesome!
TacticalCoder 11 hours ago [-]
Instead of fighting about which of the three options highlighted in TFA: HTMX (or Unicorn etc.), SSE or Websockets is "the best" I think we should take time to be extremely thankful that...
At long last we've got very serious contenders actually catching on that have "barely any JavaScript" (TFA's words).
We switched to SSE and couldn't be any happier.
Any tech that allows to reduce the need to reach for JavaScript on the frontend is a godsend.
bluerooibos 14 hours ago [-]
Meh, just use Rails and Hotwire.
animitronix 13 hours ago [-]
Get your html generation tf out of my backend!
goatlover 15 hours ago [-]
Is Meteor.js still in use? I recall early on it was all the rage, but that didn't last too long.
I had thought it delivered updated html over sockets, but maybe it was just the data. Did seem to be a bit more of a heavy JS framework, but it's been years.
For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
Maybe if you are making many client requests per second there is an advantage to not sending full headers/cookies/etc... on each request but not if you're sending requests in response to user clicks/touches.
Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
In my experience this isn’t true; firstly you’re relying on an implementation detail of the platform that you’re executing on, of which you have no control over on the client side. Secondly, even if you aren’t opening a new connection per request, you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol - effectively a length and a mask to get the contents, rather than some (in http1 land) fuzzy parser.
If you can guarantee you’re hitting http2 or http3 then you might be closer in latency, but due to the complexity of both I would imagine plain http1 negotiated persistent WebSockets provide the best latency.
Doing things on the client means user's CPU is doing some work which else would need to be done on the server, for maybe thousands of clients at the same time.
So I understand some people don't like JavaScript, but then I think the solution would be WebAssembly. I mean the point of distributed computting is that the computational load can be distributed. Perhaps counter-intuitively that often also means less need fo communications and latencies.
Unlike WebSockets, browser SSE also has a built-in recovery mechanism so you don't miss events across reconnects (`Last-Event-ID` header), although this does require explicit support from the server-side app.
Given that the WebSocket handshake has an additional round-trip, SSE would typically be faster in terms of time-to-first-byte.
> you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol
Both protocols have their own minimal framing, but "an entire HTTP stack" is a stretch. It's hard to argue that `data:value\n\n` is more complex than the WebSocket binary protocol. Simplistic, sure (no binary payloads), but also simple enough to, say, pipe through a regex. Good luck doing that with WebSocket.
Yes, but all these approaches are optimised for long sessions, not TTFB. If that were very important (say, ecommerce) you might prefer a framework that can hydrate and send the full page on first request, rather than setting up a socket to get data.
> data:value\n\n
I believe the poster was referring to requests from the client to the server having to traverse the HTTP stack, not data coming down from the server via the SSE stream.
Srsly bro? Websocket is simple? Talking about handling state persistency and connection partitions, such a headache.
HTTP/2 does not.
I thought that was the whole motivation for building HTTP/3 on QUIC (UDP)?
But your HTTP/2 reverse proxy won't block fast responses on slow responses.
As long as your WebSocket reverse proxy does the same, you're fine.
(And same stipulation for your client.)
And then the user opens your website in a handful of tabs, and everything breaks because having enough open SSE connections blocks ordinary http requests to that origin.
You can avoid that by using a shared worker for all your tabs, but then you lose the simplicity advantage.
WebTransport is in proposal stage - it is not official yet
Sound advice otherwise, but you could have left this part out of the comment :D
You underestimate how many "sufficiently complicated SPAs" are slapped together low-code projects. Their maintainers have no idea what you're even talking about.
https://en.wikipedia.org/wiki/Greenspun's_tenth_rule
Here is the original: https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
Here, I'll do you one better: https://en.wikipedia.org/wiki/List_of_eponymous_laws
You and your sibling reply are the same comment and I don't like talking to bots. You sound like a redditor from 15 years ago, but your account isn't new, so I'm not sure.
Sound advice otherwise, but you could have left this part out of the comment :D
https://yagni.club/3mstlyuxe5s26
I'm saying websockets are not fit to purpose and I can't see a compelling reason to ever reach for that particular technology any more than I'd reach for COBOL. Sadly, the browser environment gives us very few choices for streaming data connections.
Really though, I suppose I am limiting to a single technology: HTTP. Which kind of makes sense, I am putting hypertext content in a hypertext client over the hypertext transport protocol. If browsers spoke SSH, maybe I'd use that instead. I can't see a use case where adding a second protocol to my apps justifies the large increase in complexity when the capabilities are already better provided by the protocol that I'm already using.
I also know that you are an excellent writer/thinker, even prior to the advent of generative AI.
I'm in two minds here, and don't want to insinuate that you are outright lying, but I do believe your workflow relies on generative AI.
There is value in disclosing how you use generative AI to help you write articles. See https://www.bydamo.la/p/ai-manifesto or https://sive.rs/ai.
If you do use generative AI, may I suggest that you review the choice of licence: https://creativecommons.org/licenses/by-nc-nd/4.0/
> NoDerivatives — If you remix, transform, or build upon the material, you may not distribute the modified material.
If you do use generative AI, it feels hypocritical to use this licence while using a technology that essentially tries to circumvent the copyright issue by remixing, transforming and building upon the material it has ingested.
I'm not going to follow the trend of putting up an "/ai page" or a disclosure badge just because it's fashionable right now, whether it's Derek Sivers' approach or anyone else's. It wouldn't even fix anything. A sign saying "I don't use AI" doesn't stop the accusations, it just gives people a new line to doubt.
I'm tired of justifying every word, every sentence, every bullet point I write, as if writing carefully were itself suspicious now.
This is the real damage this kind of thing does. It breaks the quiet deal that used to exist between a writer and a reader: you put out content, and got at least some minimal credit for the work behind it. Now there's none of that left. No credit, no gratitude, not even the confidence that a human read it. That's exactly why there's less and less original content being written.
I'm sorry if it came across as a personal attack, it wasn't the intention.
It felt like the right place to comment. The article is public, the accusations are public, the licence is public and you made your position on generative AI public on the human-emacs project.
You expressed your position on the human-emacs publicly and we had a good discussion over it, in public. To me, this was on the same level.
I don't think that half of a conversation should be done via DM, while the other is made public.
> This is the real damage this kind of thing does. It breaks the quiet deal that used to exist between a writer and a reader: you put out content, and got at least some minimal credit for the work behind it. Now there's none of that left. No credit, no gratitude, not even the confidence that a human read it. That's exactly why there's less and less original content being written.
I agree, but you have to blame the right entity here. It's not the readers' fault that a deluge of AI generated content flooded the internet.
Generative AI broke that "quiet deal", not the readers. The readers are as much a victim as is the author who writes the content.
Generative AI came with a ton of negative side effects for the entire population. This is one of them.
Interestingly enough I read your article in elfeed, and came here for the comments as I was getting suspicious of the use of generative AI, which I refuse to read.
The sentence that pushed my suspicions over the edge is the follow up article (https://en.andros.dev/blog/bd74e61a/were-fighting-over-the-w...) which starts with:
> A while ago I published HTML over WebSockets.
"A while ago" being the day before.
EDIT: on second thought, you might have drafted this weeks before publishing and simply forgot to edit it out.
https://www.pangram.com/history/3e369754-75da-4922-8386-c0f3...
(Cue pivot to "oh, I only meant that it wasn't slop, I never pretended that I wrote a single word of it.")
> The gap vanishes. It isn't measuring WebSocket against SSE, it's measuring
> And here is the deeper part the reply misses:
> In short, it isn't a structural problem, it's a design one.
> The real argument isn't the wire, it's the architecture
> Where SSE genuinely wins
> What really separates the two models is the architecture
You get used to it, and eventually you stop caring. Too bad for them if they’re more interested in the process than in the work itself.
https://en.andros.dev/blog/bd74e61a/were-fighting-over-the-w...
In my case, I work on two Blazor websites: One is standard in-browser WASM with Restful JSON (and some CSV) over http; the other is server-side Blazor that uses the websocket technique that this article describes.
The server-side Blazor approach is for an internal web application that has a lot of quick-and-dirty pages that replace what used to be ad-hoc database queries and ad-hoc scripts. It's not an "industrial strength" web application that requires high scalability, because it's only a handful of employees who use it. It's also a joy to work with. To be specific, we don't need to go through the exercise of designing an API, making sure that contracts serialize, ect, ect, just to slap a UI around what used to be a script.
The WASM page that uses Restful JSON (and csv) is our customer-facing web application: JSON (and CSV) help with debugging; but the cost of making an API is very high. Development on the customer-facing web site moves much more slowly, but it's "worth it" for an industrial-strength site.
Would I build a highly scalable website using HTML over a websocket? Maybe. The issue is time to market: Because you don't have to build an API, you can move faster; but I don't know if scalability issues will arise.
These mean that you can for example have a websocket serve just the new HTML, and then let native browser code figure out inserting it into the DOM, without any dependency. I’m guessing things like LiveView could eventually migrate to this if it becomes standard, and eliminate more of their JS bundle.
Pretty much every web app I build has this pattern in it from day 1, as they all quickly expand to have a realtime inbox and notifications subsystem to support workflows and agents.
Maybe it's my fault, or maybe a model training, or something to improve in docs and SDKs?
It's time to replace the HTML part of the browser with something else designed for purpose: making rich UIs that use platform-native widgets and mirror the human user interface design guide for the platform. XUL was a step in the right direction.
Mind blown.
A much bigger pet peeve of mine is making a SPA when a bunch of HTML pages would do, and would give you sensible URLs and the ability to open more than one tab in the first place.
When you actually need a SPA, I'd only go websockets if I really need that low latency and your clients are close enough in the first place, if they're half the way around the world on a slow connection you have different design constraints. A chat application works just fine over SSE or similar. Client-initiated requests even work with plain old fetch().
Well, some drawbacks are not accounted for when replacing HTML parts: input elements lose focus, if some view was scrolled, then it gets unscrolled, jumping under user's pointer etc.
I use Django Ninja, Zod, InertiaJS+Vue and its as easy as using Django's templating engine, but static typing ensures my view doesnt emit unrepresentable data, my TS doesnt accept unrepresentable data, Vue+TS dont allow logic errors in template. AI makes it effortless. Again, the loaded page is a function of the data supplied at the view.
With HTMX, I'm writing several server-side functions to mutate the DOM imperatively and using HTML attributes to call them. Its great for forms but that shopping cart example needs code scattered across multiple functions and templates.
That's a weird way to use htmx. I've built a number of large apps with htmx over the last few years and never done this. Seems a great way to have a bad time.
I strongly disagree with this point, and in general I've seen the reverse is true. Only the client truly knows how it will interpret especially esoteric kinds of html tags and relying on the server for sanitisation is relying on the system furthest from the authoritative renderer.
This is definitely true for HTTP/0 and /1. Is it still true under HTTP/3, or has the underlying ‘single conduit, many channels’ model improved things if used correctly?
Contrast with something like "had 7th most comments on Hacker News"
Consider (a) algorithmic ranking, (b) votes and (c) discussion, i.e., comments, aka replies
Perhaps in some cases (b) might drive (a) which then drives (c), and of course (a) can drive (b)
As such, (b) votes and (a) ranking are almost always aligned
However, (b) votes and (c) comments are not always aligned
For example, comments with large point accumulation, i.e., votes, and hence high ranking, usually receive some negative replies (source: personal experience)
This can also be true for submissions gaining points rapidly
I got interested in this specific idea back in the early days of Firebase I saw someone built a realtime HTML component with PolymerJS called 'collection' and I became consumed by the idea of fully generic realtime self-updating components. My approach is a bit different than OP or that of HTMX though; it's JSON over the wire, not HTML.
I've built a full implementation in Node.js with a set of declarative frontend components.
https://github.com/Saasufy/saasufy-components?tab=readme-ov-...
And https://saasufy.com/
I'm thinking to make open source.
It's nice to see major frameworks coming to a similar conclusion.
[0] https://symfony.com/bundles/ux-live-component/current/index....
With Inertia.js, you get the real feel of an SPA without the complexity of maintaining APIs just for the frontend. You can even make some pages plain HTML (like the homepage, legal pages, etc.), while making pages that require reactivity SPAs.
You don’t need a TCP connection for everything.
If you’re optimizing for that you can consider client-side caching which you can instruct using cache headers that every browser support. That usually reduces heavy hitters by a lot, even if you set the browser TTL to 1 minute which is fine for most of the scenarios.
https://github.com/prettydiff/aphorio
My approach is pretty simple. Since the connection phase of WebSockets is RFC2616 compatible, per RFC6455, you can use the same server logic to connect both.
I was a Drupal developer back in the day, and I loved its templating system (the fact that HTML was assembled on the back end) and it was so powerful! It allowed for so much customization without leaking the internals of your representation to the front end, which was much more secure, IMO. Now, you have to give your templating logic to the client and potentially expose parts of your system to the end user.
Then, along comes MVC, and everyone drank the Kool-Aid, despite the fact that nobody ever actually implemented MVC purely, because it wasn't designed for the web. It wasn't designed for general systems, either. You may say, "you're wrong! You can adapt any system to MVC!", and I would respond that that is not what I mean. I mean that it is designed for custom applications (what you are referring to), and not frameworks which are for general use. You might claim that it is a framework, and I would point out that the nuance is in where the customization can be controlled and distributed. (Side note: I know MVC has been around a long time, but so have I and I remember when ajax was the hot new toy. MVC took a long time to gain traction and to infiltrate everything... and now we have ultra slow websites with tens of megabytes to download before they can even show a blank page. I stand by my statements and distain for MVC.)
I was building my own CMS that went back to server-side rendering, but I stopped because I just didn't have time to work on it. This may inspire me to do it again, only this time I'll use an LLM to get me through it faster.
You are right about the old templating systems. I still use them. I still use PHP, it's ridiculously fast and mature. It has a readability unmatched by other languages (for devs like me with design background).
I'm surprised to hear CS people like yourself talking up the old templating systems!
> I hate modern web development.
For me, modern web dev is about modern CSS and JS and native browser APIs and all that fun stuff. It's ironic now with LLMs, the people caught in the grinding gears of MVC bloat are benefiting least from LLM.
Well, perhaps I can't make that claim, but the point is LLMs do a fantastic job at focused, single-layer arrangements such as the good old templating systems, vanilla JS and just direct "old school" frontend coding. Why burden ourselves or the LLM with truck loads of layered dependencies or a million steps to hello world? No need but people get angry on this topic though, so I just do my thing in my corner of the web and deliver results for those I build for.
Until someone bombs your websocket server and you then have nothing at all.
Actually there are 2 rendering, the server sending HTML and the browser drawing it on your screen.
So why not push the logic and send a jpg image of the rendered content itself. Lol
The whole concept of decoupling the frontend and the backend is that they can be agnostic of each other, they need to align but it is not the same skills/concerns.
Serve rendered html like in 2000 and you have recreated a smart way to do what industry spent decades running away from.
There is no one size fits all solution. Frontend-heavy projects have their place, server-rendered ones too.
There isn't an official "protocol" so I had to figure it out by watching the WS traffic and determining how it worked which was fun if not tedious. That said, the more I learned, the more I was impressed by the efficiency and the programming model which felt simpler yet more powerful than SPAs.
I did get to a point where I just got too busy to keep up and over the last couple of years things have changed a bit on the "protocol" side.
But recently (a week ago-ish), I started poking at the old LiveViewJS repo with the help of coding agents. Now that Phoenix is past 1.0 and the JS runtimes (Node, Deno, Bun) have more overlap in terms of APIs and library support, I think it will be more straight forward and frankly easier to get and stay at parity.
This reflects some misconceptions I had about websockets before I used this protocol in a real application.
In practice I found the following to be true:
- Connection can drop at any time, be ready to reconnect, don't depend on that happening on the same server as before.
- Messages may arrive out of order due to how framing works. Many implementations will complete a shorter message before an earlier longer message finishes. Don't depend on stream order for a one request-one response style messaging.
Yes it's a serial TCP stream but it's basically modeling an asynchronous message protocol on top of that. This is reflected in the browser side API which just provides "onmessage" and leaves it up to you to track state.
What this boils down to is that it's great for latency (but arguably superseded by WebTransport), but ultimately you're best off using it to handle a stateless protocol just as you would HTTP requests. If you do maintain state per connection, that state should only be metadata about that connection, such as a connection-level request id, or the caching of some contextual info (eg. user id) to avoid repetition in future messages.
So I usually consider it a transport-layer optimization rather than something my application fundamentally depends on.
At the end of the day with HTTP/2 reusing open connections combined with SSE, you should be able to achieve essentially the same behaviour and even latency. It's still useful to support but I disagree that the transport should dictate the framework style here, they are simply different layers that shouldn't be concerned with each other.
What’s right with the idea, really? It’s exactly what the tried and true preferences of developers have been shown to be: getting in the way of the happy path for no reason.
Now you can have build steps and put story points in Jira and do it all on the server where we don’t have to see it, and the success condition is that the text gets served. Both sides can be happy now.
Overhead, Security, Denial of service to name a few.
You now have two states to track. Is the web-server serving the current version that the web socket is rendering?
Is your monitoring enough? Is your monitoring going to detect if the web-socket server is suddenly taken offline? How do you monitor your web server is alive and ready to serve requests in the time of need?
How do you determine if the socket server is actual offline and not frazzled itself in an event-loop? A stray network packet you never conditioned it for.
If both your web-socket server and the web-server are going to follow the same source of truth for fail-over why not just use the web-server?
The web-server is a tried and true method of serving websites. An application that allows you to easily load balance; tune and enhance with other security features. The best part is than you can actually serve a website without requiring JavaScript.
Enterprise/corporate/country DPI firewalls like to silently block web sockets; any requests you're going to be rendering blank back to the client. Determining if the client is blocked isn't easy and how do you let the clients report an issue if they can't render the support page?
The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Correctly configured DPI firewalls block these upgrade headers so for all you know the connection has been made but the renderer fails silently.
You still need a service to serve the JavaScript fronted so unless you create a WebSocket HTTP server which you've then opened a can of worms; you have a perfectly functional web-server sitting around idly wasting resources.
As I bombard your web-socket server with faux requests slowloris style. Your web-server is alive and as far as it knows your socket server is alive too, how do you determine if the web socket is actually under attack? This adds more complexity in the mix.
It's not wrong per-se. For a infrastructure learning exercise sure. However for anything else it's a waste of time and will cause headaches. The resources and the overhead for it all just isn't worth it. It's a mirage of something that looks opportunistic but isn't.
Furthermore any alterations need testing on both parts. If you were to apply a hotfix for the web-socket side, does this negatively effect the web-server side?
Does it perform the same way in Firefox and Chrome? If Firefox is slower at parsing the JavaScript html json, how are you going to accommodate that?
> The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Correctly configured DPI firewalls block these upgrade headers so for all you know the connection has been made but the renderer fails silently.
Slack and many other applications use WebSockets. IIRC there were proxy issues with WebSocket when it was new but that was 15 years ago. Also you will absolutely be able to detect the connection not succeeding.
Just make a normal website!! You've invented an MPA with extra steps!
A common use case: eg in Rails, a user has a table open. you can stream new records to the top of that table as they are created in ~5 lines of ruby (a broadcast on the model, and put the table rows in a turbo_frame with a turbo_stream_from somewhere on the page).
There are real limits to this -- you have to hold the update dependency graph in your head -- but the benefits are huge for small to medium amounts of responsiveness.
My experience has been great with Rails/Turbo and htmx.
The system is called Turbo. A turbo_frame is just a custom html element. You can largely use it instead of a div.
Turbo's js watches for navigation events (link clicks, form submits) that originate inside one of these elements, and instead of letting the browser do a full navigation, it:
1 - makes the request itself, against the usual rails routes. It uses your normal routing, sessions, cookies, etc.
2 - Intercepts the html response and looks for a <turbo-frame id="..."> that matches the id of the frame that triggered the request
3 - Swaps just that element's contents into the page.
more concretely: suppose I have a list of people in a flex table. I wrap the people table in a turbo_frame so I can prepend to it, and wrap each person in a turbo_frame so I can update it. My page can look as so:
etc.If the user edits a form for person 12345, that is wrapped in turbo_frame person_12345.
The browser makes the request itself, grabs the response, pulls out the contents of turbo_frame person_12345 (and NB: the response can be a whole page or just this fragment), then swaps that in for the existing person_12345.
It's designed so you can make your normal MPA with page navigations and reloads for editing a table row or whatever, make a small set of updates to the html, and have this work almost entirely for free. It sounds too good to be true but I've been using it for years and it often really is that easy.
edit: for server-initiated updates, I can broadcast to select listeners:
1 - replace a turbo_frame (person_12345 was updated externally, and I want to just swap out) 2 - prepend (eg for a css table, put my new person_12345 row in front of other rows; 3 - append (same, but at bottom)
The downside is it does less than react. You will mostly have to keep the reactivity dependency tree in your head. Huge win for most b2b apps; A+ for internal admin pages; solid A for config pages in almost all apps; not a great fit for (parts of) highly reactive b2c apps.
It make me happy.
In practice, REST makes things like caching, load-balancing, and debugging easier at the cost of needing to send more state back and forth. In HTTP, there are ways to reduce or at least hide the repeated TCP handshakes.
At long last we've got very serious contenders actually catching on that have "barely any JavaScript" (TFA's words).
We switched to SSE and couldn't be any happier.
Any tech that allows to reduce the need to reach for JavaScript on the frontend is a godsend.
I had thought it delivered updated html over sockets, but maybe it was just the data. Did seem to be a bit more of a heavy JS framework, but it's been years.