[HN Gopher] Clojure: Realtime collaborative web apps without Clo...
___________________________________________________________________
Clojure: Realtime collaborative web apps without ClojureScript
Author : bko
Score : 152 points
Date : 2025-04-08 18:40 UTC (3 days ago)
(HTM) web link (andersmurphy.com)
(TXT) w3m dump (andersmurphy.com)
| nkh wrote:
| One thing I've always liked about the Clojure community, is that
| they are obessesed with state. I think they have correctly
| identified, that when things go sideways, it is because state
| management was involved in some way. This is a cool front end
| approach using datastar to put all of the state on the backend
| and side step front end state management entirely. There are some
| other really interesting things happening with electric clojure
| and a new "framework" called Replicant. Of all of them, Replicant
| seems the most intriguing to me personally. If it didn't exist, I
| think I would be trying to use datastar as this article outlines.
| fancyswimtime wrote:
| single minded react user signing in. When dealing with state in
| react theres generally a good incentive not to model state
| exactly like the database model. Does Clojure removing this
| abstraction improve the application? I can see many pros but
| not knowledgable enough to see the potential shotgun
| nkh wrote:
| The Clojure ecosystem embraced react early and built on top
| of it (with OM, Reagent and Re-Frame (for SPAs). The UI =
| f(applicationState) is definitely viewed as the correct
| approach. In other words, gather all your application state
| in one place. Whenever it changes, pass all of it to a single
| function to produce the next version of your UI.
|
| Replicant takes this idea and runs with it, narrowing it down
| even further: The function is a pure function that returns
| the new UI as data. Replicant uses this representation to
| update the DOM accordingly.
|
| That's it. No partial updates, no mutable objects, no network
| activity from UI components. It's basically a templating
| system. You give it data, and it returns a view. Whether that
| data comes from a DB or any other place, it's just a clojure
| data structure. Here is the article that most of this comment
| is lifted from: https://replicant.fun/top-down/
| enugu wrote:
| Doesn't re-frame also work with ui as a pure function of
| state? (https://day8.github.io/re-frame/a-loop/)
|
| So, is the main difference that replicant is not built on
| top of react?
| diggan wrote:
| reagent (which you'd put re-frame on top of) is what
| handles rendering (via React) when we're using re-frame,
| and is the library you can say does "UI as a pure
| function of state". re-frame is basically like redux
| (over-simplification) but for the CLJS ecosystem, focuses
| solely on state and how to mutate it.
| knallfrosch wrote:
| I agree with state management being the culprit. But the most-
| hyped solution nowadays seems to be: "We'll just ignore
| frontend state, whatever the consequences for user experience.
| We'll just say this is how HTML/CRUD was always supposed to
| work and that will make it fine. [Appeal to authority]"
| jwr wrote:
| Based on my 30 years of experience building software systems,
| state really is the biggest cause of problems and bugs in
| systems. The less state you can have, the better. If you can
| compute something instead of storing it, do so. Obviously you
| always eventually end up needing state, but it's good to
| minimize its use and treat it very, very carefully.
| austin-cheney wrote:
| There is a direct correlation between complexity of the state
| model and the risk it imposes. State doesn't have to be risky
| at all. If in an application there is only a single state
| object and it's only a single dimension in depth it is never
| the pain point. In fact it helps to identify where the actual
| pain points are elsewhere in the application.
|
| This is why I will never touch something like React, a self
| inflicted wound.
| pm2222 wrote:
| My summary: one single copy of state is maintained on server side
| and clients are updated through polling or SSE.
| andersmurphy wrote:
| Basically. It's generating the html for the game state every
| 200ms (regardless of the number of connected users) and pushing
| it down to any connected clients via SSE. The magic is Brtoli +
| SSE + a properly sized context window means it runs well even
| on 3G connections (you can play with this in chrome dev under
| the network tab throttle option).
| sooheon wrote:
| To clarify, dealing with spotty connection / offline mode is not
| possible with this kind of approach?
| andriusbartulis wrote:
| Offline mode - not possible.
|
| Spotty connection - no problem, SSE reconnects.
| realharo wrote:
| Reconnects _eventually_ when things time out, but in the
| meantime, there will be a lot of frustrated waiting.
| andersmurphy wrote:
| In my experience it reconnects faster and more reliably
| than web sockets. Also nothing is stopping you telling the
| user the connection is down/retrying etc.
| andersmurphy wrote:
| Also because of the compression being used the game runs fine
| on 3G connections.
| mike_ivanov wrote:
| But.. SSE is limited to like 6 or so connections per _browser_ ,
| no?
| millipede wrote:
| Not with HTTP/2
| andriusbartulis wrote:
| With HTTP/1 the limit is 6 per domain per browser. With HTTP/2
| the limit is negotiated between the server and client, 100 by
| default.
|
| It's not a problem in practice, just ensure to use HTTP/2.
| andersmurphy wrote:
| I'd also add even if for whatever reason you are on http1.1
| (you should really use http2 for server driven apps there's a
| lot of other advantages). Datastar prunes connection based on
| the browser visibility API so on http1.1 a user would need 5-6
| visible tabs/windows visible at the same time for you to hit
| any issues.
|
| Assuming you are using Datastar the way I do with a single
| persistent SSE connection.
|
| This feature is also much better for the battery life of
| devices and the server as you are only sending stuff if the
| user is there.
| mike_ivanov wrote:
| BTW the demo app is broken: white screen with some Content-
| Security-Policy errors in the console.
| andersmurphy wrote:
| It's embedded in an iframe on the blog, you can always try
| example.andersmurphy.com to access the game directly.
| geokon wrote:
| Sorry, I'm not a webdev so I'm not sure how to check this -
| but how large is the final downloaded page? (HTML+JS) The
| inspector in Firefox says it's 20MB, which seems large. But
| maybe I'm looking at it wrong
| andersmurphy wrote:
| There's is not final download it's a streaming game. It's
| live multiplayer.
|
| The initial bundle is 12kb (datastar + initial html + css).
| What you are seeing is the constant SSE connection
| streaming compressed data.
|
| Are you behind a corporate proxy? Also what version of
| firefox? Because it's working fine on firefox for me?
| j13n wrote:
| The linked post relies on the Datastar project, which requires
| use of `unsafe-eval` in one's Content-Security-Policy [1]:
|
| > When using a Content Security Policy (CSP), unsafe-eval must be
| allowed for scripts, since Datastar evaluates expressions using
| an IIFE (Immediately Invoked Function Expression).
|
| The project itself links to Mozilla's docs on CSP, which state:
|
| > The unsafe-eval keyword can be used to override this behavior,
| and as with unsafe-inline, and for the same reasons: developers
| should avoid unsafe-eval.
|
| Out of the box, htmx uses a similar approach, but one can disable
| this use of eval [2]: htmx.config.allowEval - can
| be set to false to disable all features of htmx that rely on
| eval: - event filters - hx-on: attributes
| - hx-vals with the js: prefix - hx-headers with the js:
| prefix
|
| [1]:
| https://github.com/starfederation/datastar/blob/develop/site...
|
| [2]: https://htmx.org/docs/#configuration-options
| andersmurphy wrote:
| Author of the blog post here. Happy to answer any question!
| andriusbartulis wrote:
| There are two main high-level approaches for architecting web
| application client state:
|
| 1. Client-heavy state management (separate client application)
|
| 2. Client-light state management (thin client)
|
| The state has to be tracked and managed somewhere. In most web
| applications, that state is persistable - at some point, it will
| end up in a DB of some kind.
|
| As such, we already have to handle it on the server side,
| regardless of whether we also choose to separately manage it on
| the client side or not.
|
| So the question at the end of the day is: Do I want to separately
| manage the application state on the client side, when I am
| already managing it on the server side?
|
| In a lot of situations, the pragmatic answer would be "No". Why?
| Because managing the state in two places causes duplication,
| synchronisation issues, diverging logic issues, and so on. We are
| increasing the accidental complexity of our solution.
| Maintainability becomes slower, riskier and more expensive.
|
| For some applications, the extra complexity might be worth it. In
| most web applications I have worked with (eCommerce, internal
| business tools, booking systems, online calculators, business
| dashboards), it is not.
|
| And "let's keep all state and processing on the server" does not
| anymore mean no real-time or highly interactive features. With
| SSE and fast HTML fragment merging, we can enjoy highly
| interactive and performant web applications with almost no
| client-side application state management.
| sesm wrote:
| In eCommerce, saving the cart on client side and doing
| optimistic updates in case client network is unstable is a
| standard feature.
| dominicrose wrote:
| It still means less real-time and less interactive and likely
| more costly. Separating front and server is not a bad thing
| even if it brings some kind of duplication.
|
| With .cljc files you can avoid the duplication anyway.
| lukev wrote:
| I've had quite my fill of "real time" and "interactive" web
| pages. Slow, bloated, terrible UX as often as not.
|
| Even Datastar/HTMX might be too much. Server-side HTML can
| get you very, very far in most applications. It's faster,
| lighter, and looks and feels cleaner.
|
| Case in point: do you really prefer a heavy, real time
| interactive message board like the default interface of
| Reddit? Because we're talking on HN, which is super fast,
| light, usable, and completely server-side.
| Capricorn2481 wrote:
| Htmx is the exact opposite of what they're talking about.
| There are two concepts here: how you render and where you
| keep state.
|
| HTMX is real time in the sense that it updates over the
| wire, but you're still keeping all state on the server
| side.
|
| I don't want to reload the page to open a drawer. You'll
| notice HN isn't exactly mobile friendly because that
| requires hiding enough options that you need drawers or
| other ways to shrink and expand. You can do that with CSS,
| but that's still state, just state localized to that page
| load.
|
| And there are sites that have unavoidably complex session
| state. You can have a resource that's on half the pages but
| is expensive to compute. I don't want to load it every time
| when almost none of it changes. I don't want to cache it on
| the server if it's a big payload
|
| There's a world of difference between no state and over
| bloated SPAs
| recursivedoubts wrote:
| you might want to consider fixi, our ultra-minimalist
| implementation of general hypermedia controls:
|
| https://github.com/bigskysoftware/fixi
| andersmurphy wrote:
| Oh and something I forgot to mention in the blog post.
|
| - The server is in Europe. - The process can be modified at the
| REPL while It's running (and the changes get pushed out to all
| users by the SSE). So the board, HTML, CSS etc can all be changed
| on the fly without a restart.
| lerp-io wrote:
| That's cool, but as a developer building web apps, I'm not
| competing with php websites, I'm competing with native apps.
___________________________________________________________________
(page generated 2025-04-11 23:02 UTC)