And what was possible? / Hebrew
Hello Habre!
My name is Alex and I’m the author of Symbiote.js, a frontend library for building agnostic UI components. I’m not the only developer, but the main contributor and the one responsible for the concept, development, documentation, devrel, DX – everything else. The maintainer ie. I do all this in my free time from other work, in which I am a full-stack, R&D engineer and tech leader.
Today, I would like to talk about how Symbiote came about and why it exists at all, with a huge zoo of libraries and frameworks for the front end, with a much larger audience and support from large IT companies. After all, we, engineers, really don’t like it when extra entities start multiplying around us, and we immediately start waving Occam’s razor threateningly. Right? (squinted slyly)
React
I made my first program on React many years ago, when it did not yet have the status of a leading solution in the industry and lib by default. Back in the glory days, I worked on a startup team that was building an app for live communication between doctors and patients. I joined the project at the stage when the basic architectural decisions had already been made, and the technologies were finally selected. My immersion in React was in hard combat mode, where I worked on UX along with code. My colleagues were telling me how React is conceptually cool, but I was troubled by one thought (okay, not just one)…
Syntax JSX. After all, this is a markup language. We already have HTML for this. And also, smart uncles and aunts taught us that it is good to separate logic from imagination. And here, everything is in a heap, and even with multi-level mutual nesting… IDE is buggy. The new file format is not very well supported yet, and syntax highlighting keeps breaking. The brain also breaks down in difficult cases. Yes, yes, I know decomposition, but I work “in a team” and in a team, there can be different views on decomposition.
And was it possible to get practically the same thing, and do without simple and vanilla HTML and JS?
Okay, let’s keep this question in mind and move on.
Virtual DOM. Yes, the browser’s native DOM is admittedly slow. This is not quite true, but it is acceptable. But at the stage of synchronizing the virtual DOM with the real browser, aren’t we using the same DOM API, which was called slow before? That is, we gave birth to a new entity that does not replace what we “struggled” with, but now itself eats memory and other resources?
And was it possible to write the lib in such a way that it would work efficiently with the DOM, and the new resource-intensive garden would not be gardened for the sake of it?
This is the second question we keep in mind.
State. There is a local state of the component, everything seems to be simple with it. And there is the global state of the program. It can be quite complex, and in itself, can be subjected to decomposition: divided into elements, each of which can have its own connections with other elements.
For example, your application may have a global state, one segment of which will be allocated to store user data, another to store localization variables, a third to define the current state of the UI, and so on. Each widget or microfrontend can have its own state.
Plus, when working with data, such a thing as “consistency” is important – you should not have situations where related state segments rely on different versions of the data at the same time.
But the data itself, at the same time, can have completely different sources of truth and reasons for dynamic updates: something comes from the server, depends on the user’s actions, and something comes out through some third-party API. Total asynchrony is one of the main difficulties of the frontend, there are all sorts of race conditions and the like.
At that time, Redux as a partial solution to the above problems already existed. And all kinds of wrappers to reduce the boilerplate are not yet available. There were no contexts either, as you understand. And it hurt me to look at it.
Another question arose. Such as: if the basic mechanism of reactivity is already built into the library, why not make it abstract so that it also solves the problems of the global state, without unnecessary dependencies and adapters to these dependencies?
Svelte
Svelte had quite aggressive marketing at one time. We were told it was a “disappearing framework”. That is, we have come up with another new file format for you, which, when processed by a special compiler, turns into… pure JavaScript! Fantasy!
And isn’t it possible to, ahem… well, write in pure JavaScript right away? Well, in order not to drag a new (another) compiler, a new (another) file type, a new (another) syntax into the project? What, you can’t do without “black boxes” at all?
Ok, let’s look at the compilation result. Yes, here we have the import of the basic class… And here is the sheet of imports. And here is the description of the structure of the elements… Well, in short, you understood. No special magic and another question in the mind.
Next.js
Somehow, React developers realized that a fully dynamic SPA site is not always very good. Problems with indexing, the speed of loading assets and the initial rendering of the UI… Backenders laugh and point their fingers. It’s a shame. And an idea was born: let’s bring React to the backend as well! Let’s do SSR! And for the dynamic part of the page, we will use hydration. Or even hydration. We will insert special markers and placeholders into the markup and animate them on the front at the right moment. Good?
What about me? Right next questions.
And why, in fact, React on the server?
To form the entire document and its parts (and for the server it is, stupidly, lines), we need simple Soviet… template literals. And for calling certain behavior, certain sections of the document on the client, there is nothing better than the Custom Elements standard: I inserted one in any markup place – and it activated itself, looked around through the DOM API and implemented any interactive.
And again the question: and was it possible to solve the issue at the level of the basic capabilities of the platform and the language itself? Why build another “black box” that causes the most unexpected complications at the most inopportune moment? Why reinvent something that already exists out of the box and for free?
Yes, I understand, there is also routing, rights, cache, all other assets, except for HTML… But how does React help here? None. It’s just that we don’t want to know anything but React.
Lit
I mentioned Custom Elements above. And where Custom Elements is, there is also Lit: the main library for working with web components from Google. Nice stuff that grew out of the Polymer project. Once upon a time, the guys at Google said “Use the Platform!” and that was the light. At the same time, they slightly patched the “Platform” itself so that this “Use” would not cause a lot of “Pain”. I am sincerely grateful to them for this.
But then they began to move away from the platform themselves. Not even to move away, but to fence off with abstractions. For example, to work with templates, they use their magic html function, which transforms HTML-based syntax into a special object. And this function immediately imposes a lot of restrictions on how the template literal works. For example, you become very dependent on the context of the execution of this function. And you lose the opportunity to do ordinary linear interpolation. We are told: if you want to implant something into a template, create a new instance of our special object and insert it into the parent template.
And I want to ask: and was it possible without it? Well, to create and save templates as you like, including the ability to use and animate the markup of the parent document itself?
The art of compromises
Engineering is the art of compromises. A competent engineering decision is a weighing of a large number of “PROS” and “CONS” both for the general and for individual cases. We are all human, and sometimes, we tend to overestimate or underestimate some “PROS” and some “CONS”. Part of the factors can escape the view, for various reasons. Any technology can be criticized. Any technology and solution can be good in its context, which may not be obvious to everyone and not fully understood by people from the side.
We always sacrifice something a little: convenience, performance, the number of extra kilobytes or conceptual engineering beauty. Sometimes (often) we fall into the trap of illusions. Sometimes, for the sake of illusory goals, we do extra work and produce extra entities. How to deal with this? My recipe is to ask myself questions. On the example of those that I put in this article.
My answer to these questions was Symbiote.js.
And yes, it was you can.
PS Of course, I have not listed all the questions and not all the popular technologies that these questions cause. I hope you too have questions to share in the comments.