Don't use your ORM entities for everything – embrace the SQL
blackparrotlabs.io
blackparrotlabs.io
Most IDEs provide intellisense/validation of ORM entities, vs treating SQL like a raw string.
ORM entities also make refactoring and impact analysis slightly easier.
Despite those benefits, I generally find ORMs a pain for anything besides the most basic queries.
It's funny that the same developers that try to avoid vendor lock-in don't realize they've locked themselves into an ORM forever.
Plus, SQL isn't that hard. And as a backend person, you should be able to visualize your data model anyway. A SQL datatbase is just a bunch of planes, for the most part, and the queries are how you intersect them...more or less.
[0]: https://kysely.dev/
In my space of Python SQLalchemy has been the dominate ORM of choice and I hated it but without putting out a better product I have just decided to work around it or adapt.
But if I can I use raw SQL where possible.
I'm the opposite, I would rather write SQL than "resorting to" ORM queries, which is why my favourite libraries are aiosql[1] in Python, Hugsql[2] in Clojure and similar: write the queries as SQL in .sql files, which then get exposed as functions to your code.
Dynamic SQL.
I’ve written aiosql functions that use dynamic SQL (via plpgsql) just fine.
https://www.hugsql.org/using-hugsql/composability/snippets
But the vast majority of SQL I’ve worked with simply doesn’t need it. But even when you do, the situation has never been any worse than it is with ORM’s for me.
As a comparison, DX-wise this is no safer and is indeed very similar to the usual idiom in Go for example, where you just concatenate (pre-interpolated) SQL strings. But when you actually want the compiler to prove the correctness of your queries even in a rudimentary way, these .sql file solutions usually (if not, everytime) fail to provide the necessary external checker that processes templates and uses an accurate model of your database and SQL to verify that all used combinations make sense.
The closest thing to a proper take on this I've seen is https://github.com/andywer/squid with https://github.com/andywer/postguard which, although the SQL is inlined in the code, it uses the right approach for verifying correctness as far as I could tell in the little time I experimented with it.
There is also a mindset problem that's pretty serious in the ruby world, developers would use activerecord and load the universe, rather than running a select query carefully narrowed to the specific needs. on top of this, often there are entities that are the result of joining tables, but these are never surfaced with ORMs (activerecord specifically), since the concept of an entity from a query joined with multiple tables doesn't exist except in the limited fashion of a view.
Either way, everything gets easier with a simple schema. Faster query and insert performance, easier to reason about when doing transactions, easier to maintain, etc.
Also having to redo later will wipe out the time savings of many, many simplifications. Not to mention doc and teaching reqs you’ve added to new devs.
Every single system I've encountered that had issues with scaling, performance, tech debt, etc have all been due to badly denormalized (and broken) database schemas, with no one understanding who did what why or what piece of code owns what part of the schema. You start getting arcane knowledge and little fiefdom silos and grumpy grey-beards with inflated savior complexes that are the "goto person" despite the whole thing being ridiculously simple.
Coming from a Rails legacy app, these raw SQL "clever ideas" are where I found the most issues.
But we still only have one language for querying a database. And a kinda not very good one at that. Why is that?
https://www.holistics.io/blog/quel-vs-sql/
One could argue that ORM's are the alternative language people are looking for.
I would also love to see a new language for writing declarative queries. I also feel people don't like sql not for the language itself, but the lack of tooling.
One might say that if SQL was so great there would be no ORMs -- because there would be no need.
The ORMs, if anything, are a symptom of the larger problem that is SQL.
It's not like the ORMs exist without SQL. Their primary purpose is to provide a consistent interface between different flavors of SQL so the application developer doesn't need to care what database they're using.
For example, delimiting object names in Mysql uses ` and in Postgres uses ". The ORM will ostensibly have different adapters which take care of these differences without changing the application code.
That's because TINA: There is No Alternative.
Why is there still a C:\ drive in personal computers, and an invisible device called PRN in every directory?
- mapping results to app entities or projections
- input sanitization
- dynamic query building
- connection pooling and transaction management
- supporting multiple SQL dialects with one code base
After many years of experience with Hibernate, NHibernate, Entity Framework, and a plethora of other full ORMs, I absolutely do not want lazy/eager loading, opaque query DSLs and criteria APIs, and dealing with leaky abstractions for queryables (looking at you, LINQ and JPA), and I’m pretty wary anymore over cascading persistence. These things all look great in tutorials and are nearly magical in your PoC or simple projects, but in my experience, performance problems, unexpected and sometimes hard to diagnose bugs, and lots of ugly yak shaving are inevitable for moderate complexity apps over time.
Of all of them, I enjoyed JOOQ’s SQL builder with or without codegen the most if I need to support multiple dialects or complex dynamic queries.
Speaking of JOOQ, anyone have recommendations for something similar on nodejs? I haven’t been particularly excited by prisma, typeorm, or sequelize.
Yes, use sqlite locally for development and run PG in prod. Unit tests can now use the db and finish in milliseconds. You get unit tests that have the power of integration tests and don't have to ever stub out your db. I use Redislite for the same thing.
I'm of the opinion that SQLite is the musl of the SQL world. By deciding that you'll support it first-class you'll avoid the sharp edges of database specific behavior and extension hell and write better more maintainable code.
"Sorry we can't actually put that logic the database, SQLite doesn't support spooky action at a distance."
Personally I just pick either PostgreSQL or SQLite depending on my use case (and they're different enough that there is always one obvious choice) and just interface with them directly. Hasn't served me wrong yet.
You write SQL but instead of getting back flat and potentially-collided data, you get back pure objects which are properly structured.