This may sound like the worst idea ever. But I think there is no better time than now.
Trying To Fix The Web Dev: Part 3, The Formula.
Despite new frameworks popping every week, devs' burnout keeps escalating; the ecosystem is bursting at the seams, and Vercel monopoly gives billing nightmares.
For my solution to succeed, it just needs to be:
1. Modern like SPA
Composable components, shared and local state, reactivity.
2. Straight like MPA
Natural SSR (not by running front-end code on the server), fast loading, functional href
, real FormData
, and execution transparency (that was lost).
3. API free
Static endpoints only for serving files and pages. Everything else — wrapped and secured by framework.
4. NPM free
Not required to write/run, but not rejected if integration is needed.
5. Server-Driven
Keep business logic-related stuff on the server and user-related stuff in the browser.
6. Straightforward to self-host
Batteries included, a $20/month VPS + basic CI should be enough to kick-start.
7. And don't compromise UX along the way
Give users a genuine feel of a lightweight SPA.
Spoiler: I made it.
How??
1. Architecture: Stateful Server + Extra Thin Client
Imagine - while constructing the "front-end" form (button, whatever), simply attach a "back-end" form handler function, knowing that only a specific user (anonymous or logged in) on that particular form can trigger it without any additional header verification.
How it works:
- User loads page
- Server assigns session cookie (just a guid) and initializes page instance
- During template rendering, the server collects handler functions (hooks) into the page instance and prepares event-binding attributes
- Browser loads the page, attaches listeners, and connects to the server for synchronization
- Server routes client requests via session and instance to hooks
- Hooks trigger state changes inside page instance and layout updates
- Server-Client synchronization does its job
Approach Trade-offs:
- Each active page will occupy a piece of the server memory
- Server restart may interrupt active users
- Load balancing requires specific strategies
If you are curious, "back-end role" in such an approach is not gone, now it is a separate module/lib in your project or private (internal) API you call in a secure server environment.
2. Language. Type-safe, Relevant, and Easy to Learn.
Typescript is JavaScript; there is no way to deny it. It's an awesome language to hack on, but it is a shaky foundation for business logic. Additionally, because of our architecture, we can't afford an interpreted language.
There are three industry-proven candidates: Kotlin (as a JVM representative), C#, and Go.
Golang is one of the simplest languages to learn, offering good performance and a low memory footprint, while being boring.
Kotlin is a feature-rich, modern alternative to Java. It is likely the ideal choice for an enterprise. But its toolkit is too diverse - appreciated with experience, but overwhelming for most of us.
I had no experience with C#, but I suspect that what I said about Kotlin applies here as well.
The boring choice is obvious.
Fast forward — that was a perfect choice. Its concurrency model fits right, standard lib is fantastic, and there is templ, which is just genius.
3. Concurrency Control System
To synchronize parallel HTML updates/rendering, event processing, and state change reactions, and ensure the front-end receives only the latest state. No UX compromise in favor of internal simplicity
I need my version of React Fiber. Probably deserves a separate article.
4. Dynamic Components (and why, probably, React is broken)
Component in the front-end framework is essentially made out of three archetypes:
Role | Description |
---|---|
State And Input | Data that is used to determine rendered content, often a component is "watching" it to initiate a re-render on change |
Template | HTML pieces used in component logic to construct the final output |
Container | The place in the page layout that is affected or controlled by the component |
Component Flow: state/input processing -> template filling & construction -> container update
An update can affect the virtual DOM first, so only the diff will be applied in the "container"; however, the principle remains the same.
Typically, this trinity is tightly coupled and represented as a single entity. I think that is a bad design decision, introducing a new dimension of problems on scale because of a lack of execution transparency by design.
By restricting component control to state manipulations alone, it creates indirect, unpredictable flows even for straightforward UI updates.
I kept those three as separate entities, which you can combine however you like.
Entity | Description |
---|---|
Node (container) | A dynamic HTML placeholder that does not affect visuals (displays its content) and can be updated, replaced, or removed. Internally stored in a tree structure to manage lifecycle and concurrency. |
Beam (state) | A state and communication primitive that can be subscribed to or combined with Node. Respects the tree to ensure consistent render. Derivable. |
Fragment (component + template) | Entity with Render method. Composable. Can encapsulate multiple Nodes, Beams and have control methods and fields |
How it works in practice:
You create a Fragment (component) instance, providing a Beam (shared reactive state) as a dependency (or initialize an internal reactive state if you need one).
The Fragment's render function inserts dynamic Nodes with initial content into the layout and establishes Beam relationships (either by explicitly subscribing or by making parts of the layout automatically re-render on change).
On UI events, hook functions update dynamic Nodes explicitly with new content or trigger reactivity via Beam mutations.
The Fragment can expose an interface to provide abstracted control over its internal dynamic Nodes or Beams.
If you're interested in trying it out, please submit your email address here for an early preview. I would appreciate your feedback.Try it out
5. Real-time Synchronization Mechanics
That does not build on top of WS or SSE, while providing similar or better performance characteristics.
Ended up with rolling handover request, simplified logic:
- Client sends a regular request to the server
- Server streams updates (HTML fragments and remote JS calls) with some metadata
- Client opens a new request with a report (batched results and metadata)
- Server switches streaming to the new request from the old one
- Cycle continues
That approach requires a more advanced synchronization error correction protocol (because it involves two independent data streams compared to WS). Probably deserves a separate article.
So the client is always connected, delays are minimal, and QUIC is utilized.
6. Page Routing
Declarative, reactive, and type-safe.
Concept
Each page is a mini-app; if navigation occurs within the single-page routing pattern, the HTML is dynamically updated.
Page Route
Page paths are deserialized into annotated structs (Path Models), which support multiple path variants, route parameters, queries, and catch-all patterns.
The resulting Path Model is wrapped into Beam for reactive updates throughout the application, enabling type-safe routing with automatic parameter parsing and propagation.
Of course, Path Model can be serialized back to path+query string, and Path Models Beam is synced with the front-end.
Limitations
A full reload occurs when you switch between mini-apps (and thus, their Path Models); this is by design. You can still declare many patterns and derive smaller state pieces from the path structure to achieve a 100% SPA experience, though this approach prioritizes clean boundaries over maximum SPA fluidity.
No parameter or query value validation has been implemented yet.
7. Event Handlers
Pointer
, Keyboard
, Form
, and Input
related events are captured by the client library and proxied to the back-end if a handler function (Hook) is provided during rendering.
Other events require manual (but trivial) integration and will be supported out of the box later.
8. And Other Things...
Static Serving
Serve assets, images, etc, publicly or session-scoped.
JS integration tools
To call front-end functions from the server and back-end functions from JS for advanced control and integrations
Resource and session/instance management mechanics
Each active page will have representation in server memory, and UI updates will utilize server CPU, so it's a must-have.
JS/TS tools
If the developer needs to enable rich on-page interactivity or embed a mini React app — esbuild is embedded and integrated.
Front-end indications and concurrency control
To display pending states, debounce input events, block duplicated form submissions, etc.
CSP tools
To automate CSP headers insertion
It will be a paid product, but don't worry!
- Affordable lifetime ownership license, no subscription
- Source code will be available on GitHub, like any open source
- Free for development, buy only to go prod
- No telemetry or any form of data collection
Why??
- Not backed by a big company or VC funding
- Not relying on spare time maintenance
- Here to give you the best tool, not serving other interests. It's part of The Formula.
- Explain to my wife why it should not be
I believe that technologies should be accessible and free. In fact, everything should be. However, that's not a world we currently live in.
Additionally, I am currently working alone, and this cannot continue forever. I need to hire dedicated professionals (it's also part of The Formula), and the project requires top-tier talent.
Status
After nearly eight months of hard work and four or five complete rewrites, I finally checked all the boxes (and I can't describe how tough that was). It's more of an application runtime than a framework. Currently, I'm working on tests (coverage is now around 70%) and documentation. And I'm honestly enjoying the experience it provides.
Server RAM usage for a simple active page is below 1MB*, and CPU utilization is low. Probably, a $16/month (8GB RAM, 4 vCPU) Hetzner VPS will be enough to host a theoretical average app for over 1000 concurrent users with balanced performance settings. However, more detailed benchmarking is needed — I plan to dedicate an article to this topic in the future.
*UPD. Below 50KB...
My goal is to launch a public beta in August. However, before that, input from both experienced and less experienced developers would be invaluable. If you'd like to participate in early access or be notified when the beta is available, please sign up here Try it out.
Production licenses will be gifted to early access participants as a token of appreciation.
In the following article, "The Framework", we will examine its API with real code snippets.
Thank you very much for reading and your feedback. Stay tuned.
Top comments (15)
Hi Alex
Thank you for your creativity and entrepreneurial sacrifice (leaving the day job :-) ) in trying to innovate in this space and solve current architectural problems with web apps.
Sorry for the bother. Have i understood the strategic aspects of your framework correctly below?
Node, Beam, Fragment is an architectural improvement in it's own right, but breaking out "beam" and "node" from the traditional component approach better aligns with 1, 2 and 3 above?
Thanks in advance for your time.
Hi Hunter
It is more precise to say that API is eliminated by delegating UI event handling to the back-end (then server streams partial HTML updates)
Right, this is how bidirectional, low-latency communication is achieved via QUIC.
The rolling request is used to synchronize the DOM state with the server, while UI events are sent with their dedicated requests (and have separate coordination mechanics, which I will cover later). With HTTP/2+, the overhead of additional and parallel requests is minimal. This approach allows me to achieve better parallelism, support multipart form data as a native payload, and maintain a communication organization that is not too different from the standard.
At least I believe it is an architectural improvement. 1, 2, and 3 make it possible to perform as expected.
Thanks for your questions!
Thank you for the answers Alex! I understand!
I can imagine quite a lot of time/effort goes into this .. key to the dance:
"That approach requires a more advanced synchronization error correction protocol (because it involves two independent data streams compared to WS). Probably deserves a separate article."
This is important. Everyone is invested in their backend ecosystems.
"If you are curious, "back-end role" in such an approach is not gone, now it is a separate module/lib in your project or private (internal) API you call in a secure server environment."
This is quite a scope of work :-).
Thanks
Hunter
really feels like a game-changer. Huge respect for the crazy leap and chasing what you believe in. The industry needs that kind of energy. Good luck!
Great job! I can see a lot of experience, common sense, and erudition. Good luck!
Just first thoughts to share:
Much appreciated!
I added a detailed flow to clarify the Node/Beam/Fragment section.
Checked out Vaadin; it's an impressive platform, so I'll explore it further for inspiration. I found it amusing that, compared to my thing, it feels like Java compared to Go on multiple levels.
This is wild...in the best way..
Sounds great! All the best.
Thanks, that's very important for me to hear.
Sure! just signed up for the early access.
I'm willing to contribute to the project in some way, but don't have enough experience in Go, may be I can do something with tooling part or so. Anyways nice start, keep going!
Interesting. Thanks for sharing!
Man if it makes you happy it is worth it.
I hope it will make more people happy.
This looks awesome, but jeez, that's a massive leap of faith.
Thanks. Yeah, I feel the pressure.
Spoiler for the next article: here is a basic code snippet with a counter implementation.