Separate your Back- & Frontend! How to fix the industry's biggest mistake:
Photo by Toa Heftiba / Unsplash
TL;DR
If you're here for quick, low quality slop that you can consume in seconds, you're not in the right place. Please re-consider and enjoy a well written blog post :)
However, you can skip roughly the first half if you just like the technical stuff.
Historically, when it comes to the web, there was more or less just the server who held important code logic. We had a "backend" but we probably didn't even called it that way. It was just either a server or a client. And if you know something about software development, you know that one can also be the other one at the same time.
So, for the communication between two entities, many protocols, formats and conventions evolved.
Then, of course, there's HTML and CSS which is kind of the paint to our canvas, so that the whole nerdy IT thing looks a little bit more pleasing to the normal human being.
We began to use so much that it became the defacto standard of how you style anything in the software world. Web-Technology is everywhere and many things mimic it or implement at least parts of it.
So all of the sudden we have super fancy user interfaces that not just imitate boring forms that were printed on paper before, but almost artworks that sometime struggle keeping the balance between usability and creative art.
But for these UIs it's not enough to sprinkle some paint on the canvas, you need a lot of logic. It's a full blown application with states, logic, data saved etc.
These two things require wildly different skills - and kinds of people!
It's tempting to put all the logic in one place and essentially stop caring whether it's the backend or frontend because you don't want to jump between places and potentially wait for compiling steps and such. But since it's the frontend developer who will end up doing this in his webapp, the probability is high that the logic between front- and backend will be so mixed up and intertwined that you not only end up with messy code, but also insecure and vulnerable code.
For many inexperienced developers who use NextJs, it happens frequently that serverside code is being bundled into the client bundle.
we constantly get nextjs users who are confused at why they cannot access secrets they linked to their backend, in their frontend
— dax (@thdxr) August 1, 2024
i suspect they're not even aware their code makes it to their frontend
idk if dissolving the backend/frontend split is gonna lead to a good place
It also makes it hard to separate the logic and as soon as you change something in the frontend logic you'll have to rewrite the serverside code in it and your mental model of the backend falls apart. The thing is, your frontend changes all the time when you start developing a product.
When you have a separate backend you firstly think about the separate backend logic differently but you're also building your constructs and mental model separately.
This is not to say that they're completely independent and unrelated things!
In fact you'll only build a good backend for a great product, when you're catering it's functionality for a frontend.
If you're not even doing that, you're probably either in reality not building a product for an end-user and you don't know it yet, or you're just in for the joy of developing a technical solution to a technical problem.
(Be careful if the above applies to you and you're not conscious about it.)
In fact, in an ideal world you build the entire product in reverse. Find out what works for the user in the end product and work backwards until you build your backend and your database schema at last!
Sadly that's not so easy. So you'll have iterate with both. But keep the frequency to changes in the backend as low as possible so it doesn't grow into a giant mess in the end. This also applies to the frontend, but here you'll need to change fast and often, so without backend logic tied to it too heavily you'll be able to do that.
You need to balance the DevOps & Developer Experience (DX) tradeoff between separating the frontend and backend.
There're multiple options for this and everything comes with it's up and downsides.
For example, if your project is pretty much self contained like just one frontend and a tightly related backend, tRPC is great.
You basically have your functions you're calling from your frontend and they're fully typed but executed on the server, triggered via some network request.
The downside is, that
1) You cannot really separate your backend from that one frontend in a good way - especially not if your intention is to let it be used by a third party
2) You'll be building your backend most probably in a way that leaves you with a lot of endpoints for each individual frontend usage - or you'll at least have massive overfetching because you want few endpoints but cover many usages
3) If you want to avoid both pitfalls from 2) you'll find yourself building complicated and annoying filtering / selection mechanisms via parameters you pass to the function.
That's basically always what you end up with, if you're using some kind of RPC. But it is kind of the easiest concept because it doesn't add too much boilerplate around your code other than describing the interface of it.
If you're using Typescript and thus tRPC you can rely more on type inference, so you don't have to maintain so much yourself.
Another downside is that a RPC interface is not so much made for being exposed publicly. If you need that you'd probably rather go with a RESTful or GraphQL API.
A RESTful API basically just adds development overhead compared to tRPC but you can better expose it to third parties publicly.
However, the other issues persist and ultimately make your clientcode less optimal because you'll end up doing more logic in the client to not introduce a new endpoint for every single logic variation. It makes the life of the front developer harder and also the backend developer's.
Then there's GraphQL... essentiall it's heaven for the frontend and sometimes a trap for the backend developer.
Because you can run into performance issues pretty fast GraphQL if you're not cautious, it has gotten a bad reputation in some parts of the community. These people preach 'keep it simple'... and they have a point, but they're often the same people who vibe code bad frontend code.
So... are we doomed for eternity with bad trade offs? Maybe not. Not anymore.
What about taking the good things from tRPC and GraphQL?
Combine them and drive the developer away from performance issues by encouraging good design from the start. All while keeping the setup an absolute no-brainer.
I'm talking about the combination of Samarium and Cobalt.
Cobalt enables you to write a GraphQL powered backend while giving you a developer experience that even crushes tRPC. On both sides.
On the server you define one operation (Query, Mutation or Subscription) per file. This gives you a nice folder structure and inferes the operation name from your folder/file naming.
Every operation is just a simple Typescript function with no additional boilerplate code.
You have one "ctx.ts" file that exports your factory function for your GraphQL context value.
You can easily access your fully typed context value with a magic function called
$$ctx that's available in the global scope.
The operation arguments and it's return type will be automatically typed in GraphQL, infered from Typescript.
That's it.
Almost... you might ask "Not all Typescript types can be expressed in GraphQ, what about these?"
And you're right, but that's what Custom Scalar Types are for. The problem was - till now - that there's no good implementation on the clientside for them. Even the biggest and probably most mature GQL client, Apollo, is still struggling to support them in an easy, non-annoying way.
Samarium solves this (and many more annoying GraphQL things).
This solves all pain points in the whole backend/frontend pipeline and leaves just with beautiful code - and less code!
With Samarium & Cobalt you can finally either vibe coding your next dating app or solving really hard problems without stacking up technical debt and draining your happiness - or that of your staff.
Give it a try and leave some stars on Github :)
If you like my blog posts and open source projects, please consider joining my mailing list!
You'll get new blog posts delivered right to your inbox and it gives me feedback and motivation to keep doing what I'm doing :) !