Also ORMs when you have to do anything more complex, specially if they involve aggregations: welcome to my awkward undocumented APIs, you now embark on a journey through hard-to-search-through class definitions and source dives that you'll share with every programmer that will touch your code in the future.
Migrating from, let's say, Django, to something else requires you to basically rewrite your app from scratch. Migrating from SQLPage to Django requires you to run the standard django `python manage.py inspectdb`, then copy-paste your existing database queries, and your are ready to go.
Unless all product managers get in a room and define the canonical implementation of all web app features i think we are destined to do a lot more plumbing for a long time to come.
Job security for one, but it's hard to say in the abstract which coding style will be better.
But when you are a team of 3 with a startup to launch, for instance, you don't really care about permissions to edit categories and the no-category state. You just want that line of text at the top of the post that says which category it belongs to.
And you want to do it in a way that will allow you to later easily come back to it and start thinking about the "no-category" state and multiple categories for a single post.
Because they're never exactly the same, and you end up with heaps of special cases and handling and it would've been easier to write it three times from the beginning.
And even if they start out as exactly the same, in any non-trivial codebase that won't hold true for long.
Sometimes you want duplication because you believe the different code-copies will continue to diverge and require custom alterations.
Other you believe the copies will remain structurally the same while growing in number, so you hollow them out with reusable helper functions or macros or whatever.
What you say is on-point, and we should have mentioned it in the post.
The way I see it is: at the beginning, everything is repeated three times on the three layers. Then, as time advances, complexity grows, and you start having much more specific requirements that will need one of the layers to differ slightly.
The common approach is to just duplicate everything three times at the beginning to be ready for the moment when something needs to diverge.
What SQLPage [1] is saying, is: when you start, just think about the database. Make it the single source of truth, and iterate quickly to find out what form the data you work on will need to have. You won't get it right the first time, so it's crucial you don't find yourself having to do the work three times for every change. And then, when you need some frontend-specific feature, make just a react component for it and integrate it in the application. Then, as the app grows, you will progressively write a full frontend for it, and an external backend, but you will never have to re-do the work you have done in the beginning. This has allowed me to make some applications that I wouldn't even have thought I would have the courage to start before.
People from those communities say it's a relief building this way, though they're building simple proofs of concept still and it's not clear to me how well the approach holds up in fully fleshed out products. But it does seem to make a lot of sense in situations where a lot of the work involves keeping a bunch of devices in sync with each other.
If you're writing code where the front or back end data objects will break if you add a new db field, you're doing something wrong.
Something I have noticed about Fowler-esque / Uncle Bob-esque codebases is that usually only the guys who wrote it understand how it works. Which is either a blessing or a curse depending on whether you wrote the thing yourself or somebody else did it. And it also seems to defy the point of "making it easy to swap implementations by writing a ton of interfaces".
Is this entire article basically forgetting that as an option?
Autogeneration is a thing.
Yes it does suck. You know what sucks worse? Zero separation of concerns and the tar pit you get from it.
I don't want to write tests for the same functionality over and over. Repeated functionality should be extracted, tested in isolation and then used in composition with other tested code.
This is how you write correct code without stress or worry. People that take "just write the code" as dogma have produced some of the most untestable, bug-ridden code I've ever encountered.
The benefits: you write code faster, automatically uniform and the result is “dumb” and less abstract AKA easy to debug and modify. Tedium/boilerplate is gone, you focus in the overall model.
The costs: you think more up front, you have to see the result first (hand written). It’s easy to see common patterns too early.
With some patience, caution and experience some of the costs can be mitigated.
So much time & frustration expended simply to avoid typing out the magic database commands... And the constant ego trips attempting to outperform 30+ year old query planner codebases on 7-way+ joins by using baby's first ORM.
> the tar pit
If you find yourself stuck in one of these, I strongly recommend giving this a shot: https://curtclifton.net/papers/MoseleyMarks06a.pdf
"but it won't scale"
We are in the era of hyperscale SQL engines. Database engines that are spread out across multiple servers, racks and buildings. Engines so vast & complex the compute & storage responsibilities have to be separated into different stacks. But, they (the good ones) still work just like the old school approach from an application perspective. The workload necessary to actually saturate one of these databases would be incredible. I some days wonder if Twitter could be rewritten on top of one without much suffering.
And, if you aren't trying to go big and bold or spend a bunch of money, there's always SQLite. It also supports basically all the same damn things. It can run entirely in memory. It has FTS indexing. Your CTE-enabled queries will work just fine on it. If you find SQLite doesn't scale with you, swapping to a different engine really isn't that big of a deal either. You will have some dialect conflicts but it's generally very workable, especially if you use some thin layer like Dapper between your code and the actual connection instances.
Ultimately they tried to do more than asked which then caused problems because maintenance is now harder, and some types were removed while others were “enriched”, and much like uranium, became more dangerous to wield.
Other than that I completely agree. Devs get hang-up on trivial syntax topics waaaay too often, when the actual time-killer lies in reasoning and performing test-cycles.
I view abstraction as the single best way to make each other permanently more productive.
Don't we have ChatGPT/Copilot to do it for us now?
One one winner with splitting tech on horizontal boundaries is that changing a feature is a largely high cohesion change. All the code bung updated in that commit are related to one another, and despite the fact that there are "many" places that the code needs updates, at least they all relate to one another.
There was some effort in the java community to meet the problem half way with something called point cuts. This allowed some level of contracts which you could "insert behaviour into all instances of X" which had some success, but I haven't see it in the wild for a while, so I'm not entirely sure it survived.
There's no need for a separate API layer or painstaking synchronization with a standalone frontend. Features that took days of work across all three tiers now take just hours in a single unified backend context.
* Update your BlogPostModel, to add a ManyToMany(CategoryModel) field
* Update your .html template with a for loop over blog_post.categories.
* `django-admin makemigrations && django-admin migrate`
Your life can be so simple. All you need to do is to reject Javascript.
Regarding solo projects, I agree that simpler stacks like BaaS or other innovations can be sufficient. However, fast-scaling companies often require the unparalleled flexibility and customizations offered by an in-house 3-tier model. This tailored approach ensures they can effectively meet the evolving demands of their growing operations.