Modern Frontend Complexity: essential or accidental?
Posted by BinaryIgor@reddit | programming | View on Reddit | 57 comments
Given that the current frontend ecosystem complexity is rivaling Tower of Babel, I asked:
Can we start from scratch and figure out a much simpler approach, given how browsers have evolved in recent years?
Have a read to find out ;)
CircumspectCapybara@reddit
The modern SPA and React (which I'll treat as a stand-in for all similar technologies like Angular or Vue) paradigm evolved because we've grown accustomed to the power and expressivity of JSX / TSX (if you, think about it really is quite an elegant and powerful DSL for describing UI--you get strongly typed, rigorous types for UI components and hierarchy over the loosey-goosey grammar of HTML), and the power of the React engine which elegantly represents state and state transitions, models side-effects (React is essentially an effects system for UI), and abstracts away all the reconciliation that you would otherwise have to manage on your own.
lechatsportif@reddit
Yikes, please don't let "FAANG" do all the thinking.
CircumspectCapybara@reddit
Do you implement your own container orchestration layer too because you don't trust Google to do a good job with Kubernetes?
lechatsportif@reddit
Oh sorry. I'll go post an apology on Orkut after I finish committing my changes to Google Code on my new GWT app
ZippityZipZapZip@reddit
The SPA paradigm did absolutely not evolve from JSX or TSX.
It was about rendering the view. It was always about JavaScript and state management becoming messy, relying on refreshes. The MVC. The backend calls, the (shadow) DOM, element (/component) creations, value binding, synchronization of state for client between components and client-server.
React is not essentially an effects system. It's also, very much, not a functional language for UI.
React has a lot of synctactic sugaring, making it declarative. That is useful but also misleading. It is easily possible to make the same type-safe assertions about DOM-elements being created (either directly or using shadow-DOM components).
A lot of the time it is not needed. It is very useful, obviously.
CircumspectCapybara@reddit
No, React (being the frontrunner for SPA web frameworks and which kickstarted the whole paradigm) was made popular because of its simple DSL which uses JSX / TSX. You can always program React apps in procedural JavaScript / TypeScript (using
ReactDOM.createRoot(),React.createElement()with JS classes that implement theReact.Componentinterface) without JSX / TSX. But the devx of that sucks. TSX is the way to go.After hooks were introduced, it was.
The entire idea of functional components (which you compose in a declarative DSL called TSX) is that it'ss a functional language. You have pure functional components, higher order components, an algebraic effects system called hooks, etc.
ZippityZipZapZip@reddit
It empowers (and forces) you to work in a functional paradigm. The side effects, the hooks, themselves, while being nicely sugared as algebraic, are a nice indicator it isn't actually functional programming.
It makes you work a certain way - the imperative declaration means you program reactively (and usually functonally) but it is technically incorrect and overreaching to argue it is either an effects system or a functional programming language. It uses OOP principlss too, specifically the lifecycle, composition, the blessed side-effects. The imperative nature, reliance on the React engine, just makes one forget those mostly.
shorugoru9@reddit
I was writing SPAs long before React was a thing (it came out around 2011). I had the displeasure of working on ExtJS SPAs which came out around 2007, which itself was based on an earlier framework called YahooUI (YUI) which came out in the early 2000s.
This was a time long before the standardized Javascript toolchain based on
npm, so each SPA framework had its own uniquely horrible build system (such as shudders Sencha Cmd).SPAs became more popular because of the standardized Javascript toolchain and open, non-proprietary ecosystem.
CircumspectCapybara@reddit
I mean, that's splitting hairs at this point.
What you're saying is true, but only in the same way that one would say technically Haskell is also a functional fiction, an abstraction on top of a very physical, non-functional machine (the underlying physical platform which is a at bottom a state machine). Haskell models I/O as monads, and lets you pretend everything can be expressed recursively, but in reality, under the hood, it's being executed procedurally by a very non-functional software platform.
ZippityZipZapZip@reddit
The useState is already an indicator of the wonkiness.
Just because the end result is always a DOM-element, it doesn't make it functional.
Uristqwerty@reddit
I watched an interview recently where some game programmers brought up a good point about React: It's an immediate-mode style single-point-of-truth framework layered atop the browser's retained-mode DOM. And if you have server components? Stack on another layer of each.
It's paradigm mismatches all the way down.
Pink401k@reddit
So?
mazing@reddit
I'm happy for you, react, and Imma let you finish, but Vue is the greatest of all time
Zardotab@reddit
The problem is customers don't know the cost of such extras. There is a selfish incentive in our industry to up-sale; we know the importance of YAGNI but the customer doesn't.
I know multiple GUI CRUD apps built in the 90's that still do their job just fine. Fancier stuff would not notably improve them. I think we through the productive baby out with the bathwater for ordinary CRUD.
And making the same app work well on both mobile and desktop is a very tricky goal. The lowest-common-denominator sucks. UI Shelden Coopers can do it, but they are expensive and not pleasant to work with. It may be cheaper to have 2 different apps: mobile and desktop. Most businesses just use desktop anyhow for most their apps. Making them mobile-enabled may be a waste.
Why does the same app take roughly 3x the code and cost 3x more? We are doing something wrong, it appears to be reverse evolution. šµ Reverse-YAGNI-Milking might be good job security for us techies, but screwing over biz.
BinaryIgor@reddit (OP)
Can you elaborate on what kind of interactivity you cannot achieve with HTMX? I don't see any limitations there (in this context).
CircumspectCapybara@reddit
HTMX just pushes the question of "how do we map some abstract, dynamic state to HTML" to the server instead of the browser.
If you're just pushing static or simple HTML (e.g., something constructed from a template in a single pass of variable substitution) over the wire, HTMX is a great choice.
But the moment you have highly stateful, complex apps with a lot of state to manage and a lot of interactivity so that state needs to flow both ways and be reconciled carefully, you're back to the problem of needing either to use JavaScript (whether on the browser side or on the server side) to manage that state and compute how the HTML (or equivalent DOM nodes) should change so the user sees what you want them to see in that moment.
Dimenus@reddit
Isn't this most business problems? What's an example of something you think it is not appropriate (genuine question)?
BinaryIgor@reddit (OP)
True - you cannot escape essential Complexity; my main points are just that: * There's lots of accidental complexity in the current frontend stacks * Complexity is usually easier to handle and definitely to test on the server side
n__sc@reddit
āElectric Clojureā (https://github.com/hyperfiddle/electric) comes to mind in this space. Pretty interesting stuff, leaving aside the barrier of entry and adoption issues.
KremBanan@reddit
Agreed. Nothing really compares to TSX. I canāt believe people fight for untyped html with random magic attributes.
TheWix@reddit
I feel like the people pushing for this either didn't live through the "good ol' days" or they are working on over engineered products and are over correcting
axonxorz@reddit
The good old days are what let me excuse the immense upfront complexity of modern frameworks. Implementations using things like jQuery/Moo/PrototypeJS just move that complexity to the maintenance cycle, I'd argue with a fairly hefty multiplier.
TheWix@reddit
There are people pushing to go back to vanilla JS (or TS now). Most don't realize the pain of that. I've been doing this since IE 5.5. I don't wanna go back
yawaramin@reddit
You can of course use TSX on the server with htmx if you want. And there are HTML generation libraries in many languages that offer similar levels of type safety, eg https://com-lihaoyi.github.io/scalatags/
jessepence@reddit
Tiring complete and context-sensitive are not mutually exclusive. C is a turing-complete, context-sensitive language.
CircumspectCapybara@reddit
Sorry, you're right. What I mean while the meta-languages (the set of all valid source code strings) of both HTML and JSX / TSX are context-sensitive, the computation (translating HTML code to a DOM tree in the browser) they express is not. One is capable of computing anything a Turing machine can (because it's literally just JavaScript), while the other is a simple, single-pass transformation of markup to UI in a browser.
In order to let HTML express Turing-complete computation, you need to add JavaScript. And therein lies the problem: if you want to make JavaScript do complicated and maintainable things for you, you often need to engineer a framework on top of it so you as a dev can work in higher-level abstractions.
Although technically, if we want to be really pedantic, the meta-language of C++ is not context-sensitive, because you have Turing complete computation powers right in the preprocessor and template metaprogramming and
constexpr/consteval.gnuban@reddit
Webpages were designed to be rendered fully on the server. And each webpage had a separate URL, which lead to the RESTfulness of the web. Each page has an URL, and deeplinking always work. Webpages also use HATEOAS, utilizing forms.
There was no way around this.Ā This was all on purpose and is laid out in Roy Fielding's REST dissertation.
But it was a novel concept, and hard to wrap your head around if you were used to writing stateful desktop apps.
So Microsoft, in their infinite wisdom, invented a way to break the REST model by allowing JavaScript to download more data from the server to amend the webpage.
Cue 25 years of trying to tame the ensuing chaos, in large part emulating the original REST idea of web pages, but now doing it on the client instead.
It's slight insanity. If we wanted stateful web pages, we shouldn't have started with REST. And instead of trying to revert the introduced statefullness with frameworks on the client, like what React and friends are doing, we should have developed a new type of client that could do this natively.
BigHandLittleSlap@reddit
The fundamental cause of all of this is the speed of light. No matter what you do, a round-trip will always cost about 100 milliseconds from globally distributed users to a single central location.
Most of the history of computing can be explained by the battle against relativity.
For example:
Mainframes put the "app" and the "data" into the same motherboard, reducing the latency to single digit microseconds. This allows simple code such as SQL "cursors" to run at blistering speed.
Similarly, Citrix and terminal services in general do the same thing for Windows desktop GUI apps. They put the app into the same server rack as the back-end databases and app servers, reducing latencies from ~30ms to ~0.3ms. I've seen apps get dramatically faster because of this.
Traditional web servers are much like mainframes: the app and the data are close together in space, which makes HTML rendering relatively fast even with naive code.
An optimisation Citrix did on the client was to implement short-circuits for UI interaction. Scrolling and typing would both do a local prediction and guess what the screen update would do, before verifying with the server. (3D shooter game engines do similar things!)
SPA client-side JavaScript apps are a complex and clumsy attempt at the same, short-circuiting GUI interaction locally to avoid the round trip to the web server.
The Citrix in general (and its client optimisation) is effective because it was relatively "agnostic" to the actual application, so it could consistently achieve the best possible latency of a single round trip without complex code. It would only send keypresses and mouse events, and receive only a compressed screen update stream (effectively a video with delta updates). This is optimal, and thanks to Einstein we know we can't improve on it, not in this universe anyway.
Web client apps that make multiple calls are automatically less efficient than this optimum, so they slowly grew in complexity by adopting things such as batching, GraphQL, etc... to get back down to this optimal single round-trip latency.
Meanwhile, deploying servers globally to bring the data closer to the users has become trivial thanks to the public cloud providers, so the hideously complex SPA solutions to latency isn't actually needed anywhere as much as it used to be. Similarly, network links have become faster in both bandwidth and latency, so a full round trip is barely noticable these days.
Things might shift back to purely server-hosted again, swing back to client-side, then back to server hosted only, then...
The industry has gone through these swings about once a decade for half a century now!
Kwantuum@reddit
All these articles are like "WE CAN DO IT WITH LESS!" but then they don't do it with less. They just do less.
Your HTMX app is nowhere near the level of dynamism and interactivity that frontend frameworks enable. You can do less when you need less. Not everyone needs less.
Kwantuum@reddit
Bro I'm not that blind, why is your font so huge? It's unbearable to read on mobile
BeautifulCuriousLiar@reddit
good read, iāll have to check it again and the repo when iām not on my phone. one thing we can disagree on though, i rather just use css than tailwind, but thatās my opinion.
i think we did get to a spot where everything front-end is overly complicated. there are some features from react for example that are nice to have, but imo all this tooling, frameworks and dependencies kinda surged a while back where doing something a bit more complicated was fairly easy compares to javascript only. today it still fairly is depending on what, but things have advanced a lot and we have a lot of great documentation. we all got used to the perks of using everything but native, we had to learn how to use these dependencies and more likely than not going back to the pure side is a hassle. we got no time for that now, especially since we (mostly) never thought about any kind of modularity to replace those things in the future.
i myself need to dig deeper in web components and even htmx since all we do is follow what we hear as industry standard (react) and forget or not even see what have right in our face.
it is a breath of fresh air doing things natively and the hard way or reinventing the wheel on personal projects, but at work that always isnāt possible. all of this over complexity is definitely something we should talk about more especially since the latest supply chain attacks and vulnerabilities.
TheWix@reddit
Web Components have loads of drawbacks, and I don't have any experience with htmx. The issue really is that we are building apps on a document viewer. This thing was never meant to do what we need it to do. Add to that a complete lack of a decent standard library and it's no wonder we have thousands of libraries to fill all the niches not filled by 1st party libraries.
The foundations of web development are deeply flawed, so it's no surprise that most things built on top of it are also flawed.
lanerdofchristian@reddit
I do have some experience with HTMX -- I don't think it's suitable for most tasks, if only because it requires complete full-stack integration. There's no such thing as a quick frontend tweak, because anything touching the DOM or scripts needs a backend change if it's in an area swapped by HTMX. You can't sneak some more fields into your API responses for later frontend changes because your API responses are your frontend.
It's phenomenal if you control the entire stack and only want to do little things like refresh a server-rendered table when a form is submitted or send some out-of-band messages back without doing a full refresh, but anything more complex with a large team quickly starts becoming a ravioli-spaghetti hybrid without extremely diligent review and shared understanding (moreso than more decoupled frameworks provider).
BinaryIgor@reddit (OP)
I think it depends - with HTMX you would have a team(s) of full-stacks (no front/back distinction) and then I don't see it as a problem :)
lanerdofchristian@reddit
Another major issue I see with HTMX is severely degraded type-safety and static verification capability compared to other full-stack frameworks like Nuxt or SvelteKit. The article directly shows this flaw: arguments are passed as
Map<String, Object>in most templating engines (something you now need to tack on in addition to HTMX, and hope you get all the data-to-string conversions right yourself). Even when you have frameworks that can compile-time check template arguments, compile-time checking the template itself is rarer still, requiring test suites to catch errors other frameworks handle at write-time with LSP servers or IDE plugins and compile-time with whatever compile step they're using.Throwing web components in on top of this just compounds the DX difficulties and user experience, given their utter lack of ability to cleanly be server-rendered. If having your site work with minimal to no JS loaded is important, HTMX and Web Components are antithesis to that: rather than starting from functional and making the page progressively more interactive the more loads for basically free, HTMX requires developers to carefully build both JS-free and JS-able models in the same markup; and web components just throw up their hands and refuse to play nice at all.
Modern component languages are first evolutions of templating languages like Mustache, and second evolutions of client-side and full-stack interactivity frameworks. As a tool for authoring pages (even statically or SSR-only), the likes of Vue, Svelte, and Angular far outstrip the capabilities of more primitive templating languages like Mustache or Twig -- in such a world, where components can be statically checked and freely composed, what advantages do the simpler frameworks really have?
yawaramin@reddit
You can use JSX on the server side with htmx too and get all the type safety benefits. Nothing about htmx forces the use of primitive templating languages. The advantage of htmx is that you can avoid the entire npm ecosystem (
node_moduleset al.).lanerdofchristian@reddit
If you're using JSX/Svelte/Vue, you've already bought into NPM or its competitors. And at which point, if you're using the framework for templating, why not use it for interactivity too and save the headache of trying to mesh disparate systems together?
yawaramin@reddit
You can use JSX without buying into a full 'framework'. And if you're using another (non-JS) stack there are similar HTML generation libraries that are also type-safe and don't need any JavaScript stuff.
lanerdofchristian@reddit
Even without buying into a "full framework", the mental model of JSX just isn't cleanly compatible with HTMX. JSX pushes you toward "data in, elements out", whereas HTMX is firmly in "elements in, elements out, data tags along".
HTMX isn't a magic "woo no JS" tool -- it's literally a JavaScript library itself that provides a DSL for doing fetch requests where the responses are plain text instead of structured data, and stitching the responses back into the DOM. It's not even free from the NPM ecosystem -- the quick-start CDN just serves this package.
It's a good tool, don't get me wrong, but there are very clear drawbacks when you use it outside of its niche. There are points you run into very quickly when using HTMX (even the above article does) where you have to fall back to plain JavaScript. For things like charts and dialogs and little simulations like a news site might stick in the middle of an article, HTMX just can't do those things by itself.
So, if you have a full-stack framework, reliant on parts of the NPM ecosystem, with JavaScript running in the browser... but without type safety, without good component DX, reliant on external templating tools, incapable of being statically hosted, that requires you to reach for the very tools so many claim it can avoid the moment you try to do anything beyond simple form submission... why not just start with a more feature-complete tool?
yawaramin@reddit
The mental models of JSX and htmx are orthogonal, they don't conflict with each other. JSX on the server side is just a type-safe way to write HTML. And htmx is just a way to do HTTP request-response updates to the page.
No one claimed that htmx is a 'woo no JS' tool, we are just saying that you are not forced to use the npm ecosystem with htmx.
The fact that the quick-start CDN serves a file from the npm package does not mean that you are being forced to use the npm ecosystem. As you can see quite clearly from the instructions, you can just include it as a
<script>tag without having to do the wholenpm installandnode_modulesdance.What frontend framework can do charts by itself without using a charting library? Htmx is exactly the same as React, Vue, etc. in this regard.
You don't even need a frontend framework to do dialogs, plain HTML has
<dialog>built-in now. Same with Popover API and CSS Transitions. There are lots of things built-in now that htmx can just piggy-back on top of. It eliminates the need for tons of JavaScript.Basically, none of your points are accurate. Htmx is vastly simpler and easier than SPA frameworks, and with a significantly better security posture on top of that. Many people are noticing this.
lanerdofchristian@reddit
My point there was in refutation of
since you're already bought into the JS ecosystem regardless.
Many people do come across as basically saying that ("HTMX means I don't need to write JS"). It's like the people who drive a hard line between Tailwind and CSS -- there isn't actually a line.
True, but other modern frontend frameworks do make it a lot more straightforward to manage the state around dialogs and can handle complex flow without going to the server -- things like "is the dialog open? yes/no, what are the field values? let me react to those".
In my eyes, HTMX is simpler than other full-stack frameworks like Nuxt/Next/SvelteKit/Angular/ in the same way that Snap or LabView are simpler than Java: it trades some complexity away to make some basic cases easier, at the cost of not scaling as well to anything outside its niche. The moment you have to step outside of its walls, you're face-to-face with the full complexity of unstructured JS spread across not just your entire page but every fragment it can request.
HTMX does not magically save you from improperly securing your endpoints or forgetting to validate user input. Everything you would have to do to secure another full-stack framework or an SPA you also have to do to secure a site written with HTMX. It's just a JS library, not a silver bullet.
yawaramin@reddit
When I said 'don't need any JavaScript stuff' I was talking about HTML rendering from non-JS frameworks. Read the context.
Well I'm not many people, so if you are replying to me then keep it focused and reply specifically to what I said. I can't keep track of what 'many people' said.
Do they make it simpler than this:
<dialog open>?And that moment may never come. Just look at the vast majority of webapps other there using SPA frameworks now. Most of them are landing pages. Hardly complex interactive apps.
With one exception: you effectively won't have to worry about the constant treadmill of supply chain security attacks with htmx. It's a single
<script>tag. Every time a new supply chain or other npm ecosystem attack is announcedāeg the recent axios attacksāhtmx users just go about their day like nothing happened. Because for them, nothing did.shorugoru9@reddit
As someone who has worked on older Java SSR frameworks (like Struts and Spring MVC), this is not a problem at all in Java, and has not been for decades. Java has had the ability to hot-reload frontend template changes since the days of JSP. With front end frameworks like Thymeleaf, just turn off template caching.
Isn't this like a well known problem with well known implementation patterns going back decades. In Java, we developed the "Model 2" MVC architecture and "Three Tier" architecture to carefully structure the application to prevent it from turning into a spaghetti mess between the presentation layer and all the stuff behind it.
lanerdofchristian@reddit
What I more meant by that is you can't make a change that's just a client change or just an API change -- even something that seems as straightforward as a small inline script update or a class change requires reaching to the other half of the application and making the change there, because the client is the API and vice-versa. It's just not as clean a separation when your API is returning partial presentations mixed with out-of-band client updates instead of serialized models.
HMR is accessory to that (though I will say Vite does a much better job of it than Qute does, what with actually doing the HMR in the client instead of requiring a full page refresh). I haven't worked with Thymeleaf, so I can't comment on that specifically.
My main argument in the latter half is that the presentation layer itself becomes a spaghetti nightmare on its own, given the comparatively primitive composition capabilities of nearly every server-side templating library for traditional stacks (vs the likes of Angular, Svelte, and Razor). Templates requiring specific endpoints expecting specific templates or fragments in response just isn't as easy to test and develop as independent components with stub callbacks and fake data.
BinaryIgor@reddit (OP)
Checkout this approach to build them: https://blog.jim-nielsen.com/2023/html-web-components/
It fixes some of these issues; I intent to build a collection of such components soon :)
Graphesium@reddit
That example is so basic I don't think it's even realistic. What about passing in data to components?
ValuableKooky4551@reddit
In particular that the page comes together from parts written in three different languages (HTML, CSS, Javascript) that don't share any developer tooling always leads to decelopers trying to do everything with one of them, the one that has the most tooling: Javascript (or even Typescript).
Ie, with multiple teams working on multiple apps you want reusable components, you can write importable Javascript modules that you can package in libraries. But you cant do the same with the HTML and CSS that should come with the components. So you end up doing it all in Javascript.
If all three things had been in more similar languages with the same tooling from the start, that could have been prevented. But yeah, that wasnt the initial goal of the Web.
BeautifulCuriousLiar@reddit
yeah. just look at the abomination of jsās Date object. that brought us moment/dayjs, date-fns, luxon⦠and only now we are getting close to the temporal api becoming standard. well until all browsers support it properly. yes we are looking at you safari.
BinaryIgor@reddit (OP)
100% - I'm torn on the Tailwind use ;) On the one hand it allows me to write less CSS, on the other it gets you away from the native approach. To be reconsidered
roodammy44@reddit
One thing I noticed is that itās fine for simple stuff, but can become totally unreadable for complex css.
qodeninja@reddit
nice blog post jim!
lugh_longarm@reddit
Most of it is accidental, driven by hiring pipelines more than technical requirements. React became the default because it's what new grads know, not because most apps actually need a client-side virtual DOM. Something like HTMX covers 80% of "web app" use cases with a fraction of the toolchain. The remaining 20% -- rich collaborative editors, complex offline-first -- genuinely earns the complexity. The problem is we reach for the 20% solution for every problem.
Complete_Instance_18@reddit
Totally resonate with the "Tower of Babel" feeling.
remontantcoprology@reddit
The "a server in Java" proposed here looks a lot like Wt in C++ https://www.webtoolkit.eu/wt
giantsparklerobot@reddit
There's whole generations of web devs that have never done native development on a platform with robust standard/system libraries. They don't have good examples of what a good system looks like. They then go off to build bullshit frameworks in an awful language with effectively no standard library. It's no surprise everything sucks.
JavaScript as a platform is awful. HTML was not designed for what modern devs want to do. So frameworks need to reinvent the wheel using existing DOM elements. This is usually "fuck it everything is just divs and spans". Or reinvent even more by doing everything in a canvas element(s). This is all made worse with no standard library and the absurd model of pulling in one or two line external dependencies.
Gigawatts of power are wasted every year because web devs is so fucking ridiculous. Platforms (language plus ecosystem) like Java, .NET, Cocoa have their challenges and idiosyncrasies and aren't perfect but are a far cry from the bullshit that is web development.
Plank_With_A_Nail_In@reddit
Web pages aren't the be all and end all of Front ends. My advice is to get out of webdev.
joe-knows-nothing@reddit
This really reads as a very narrow perspective even within the webdev. No mention of cgi-bin, java applets, flash or PHP in the history lesson.
Not to mention that thin clients (server side rendering) has been on-again off-again since UIs have been around (mainframes, anyone?)
It's a classic case of the "I don't understand this complexity, so it must be wrong" fallacy. Abstractions and complexities don't appear out of nowhere and for no reason. They solved a problem for someone at some point in time.
The kicker is that the "we should talk about all the complexity before we build it into the system" is how the complexities tend to get put into the system! HTML, JavaScript (via ecmascript), etc have huge governing bodies and formal processes for modifying the standards.