While there is a compelling case for leveraging the full power of Postgres (especially features like SKIP LOCKED and pg_notify), this approach feels like a classic trade-off between fine-grained control and long-term maintainability.
Relying solely on raw SQL and manual mapping certainly eliminates "ORM magic," but it replaces it with a significant maintenance burden.
For specialized, high-performance systems like video transcoding, this level of hand-tuning is a superpower; however, for the average CRUD-heavy SaaS app, the "boilerplate tax" of writing eighty lines of repository code for a simple related insert might eventually cost more in development velocity than the performance gains are worth.
> for the average CRUD-heavy SaaS app, the "boilerplate tax" of writing eighty lines of repository code for a simple related insert might eventually cost more in development velocity than the performance gains are worth.
Perhaps, but IME this kind of thing is much more often the cause of poor performance in CRUD apps than the frontend frameworks that are usually blamed. I have been able to make very snappy SaaS apps by minimizing the number of queries that my API endpoints need to perform.
I've also found that the ORM mainly reduces boilerplate for Insert/Update operations, but often adds a very significant amount of boilerplate for read queries. We ended up using a very lightweight orms for simple inserts / upserts and writing raw SQL for anything else.
Considering anybody with a noggin is going to be separating the SQL into it’s own module or whatever rather than just throwing straight inline SQL at your database wherever you it, you’re hardly less likely to have things like accidental writes, anyway. This is clearly someone who fell in love with Postgres, felt ORM abstractions that diluted the Postgres goodness were bad, and then did some mental experiments to consider all of the theoretical ways ORMs suck.
Though the article mentions the distinction between ORMs and query builders, it doesn't really make a case against query builders being bad. After all, it wraps up by building a kinda crappy one-off query builder.
No, just because raw SQL queries work great for your toy blog/todo app with 3 tables and simple relationships, doesn't mean they work great for real world business applications with 100 tables and complex networks of relationships. Try maintaining the latter before you make blanket claims like "ORM bad".
> just because raw SQL queries work great for your toy blog/todo app with 3 tables
In my experience, ORMs work well for toy projects, but become cumbersome to maintain in enterprise ones, especially where performance matters. There is a large overlap between engineers who refuse to learn SQL because it's not "convenient", and those who prefer ORMs because they are "easier", resulting in cohorts that don't know how to use either.
But also, I don't see how ORMs make managing large databases any easier, other than those with embedded migration capabilities, which can be very well extracted to their own tools.
I’m glad you’re head-over-heels in love with Postgres— it’s really cool, and I’ve occasionally had projects that really benefited from it… but most of those incredible features just aren’t useful for run-of-the-mill projects. Learning how to profile your ORM queries is a lot easier than maintaining a bunch of code from a different language embedded into your code base. If you’re writing articles about Postgres, you probably have no idea how much of a PITA that context switch is in practice. It’s funny how getting expertise in something can make it more difficult to understand why it’s useful to other people, and how they use it.
Mostly agreed with the author about ORMs. The provided querying abstraction works against developers when queries reach a certain level of complexity, and at the end of the day, understanding these complexities is not optional.
But I would caution against adding too much business logic to the database, and tying message passing to your database doesn’t sound like the best of ideas.
When someone says that X is bad and not to use it, what I really hear is, “I’m ignorant to some use cases but that won’t stop me from having a loud opinion.”
I oscillate on being tired or amused by just how common tech people make this basic error. But I don’t believe it’s ever in bad faith. I think people in general suffer from perceiving their context as the context even though they’ve experienced maybe 1% of what there is out there.
> And if you need real SQL intervals, Django pushes you towards raw expressions or `Func()` wrappers.
It's possible to use a very similar construct to SQL Alchemy here by using the `Now` [function](https://docs.djangoproject.com/en/6.0/ref/models/database-fu...) (it uses `STATEMENT_TIMESTAMP` which is likely more correct than `NOW()` here alternatively there is `TransactionNow`) by doing `Now() - timedelta(days=30)`.
25 comments
[ 3.1 ms ] story [ 47.1 ms ] threadRelying solely on raw SQL and manual mapping certainly eliminates "ORM magic," but it replaces it with a significant maintenance burden.
For specialized, high-performance systems like video transcoding, this level of hand-tuning is a superpower; however, for the average CRUD-heavy SaaS app, the "boilerplate tax" of writing eighty lines of repository code for a simple related insert might eventually cost more in development velocity than the performance gains are worth.
Perhaps, but IME this kind of thing is much more often the cause of poor performance in CRUD apps than the frontend frameworks that are usually blamed. I have been able to make very snappy SaaS apps by minimizing the number of queries that my API endpoints need to perform.
I've also found that the ORM mainly reduces boilerplate for Insert/Update operations, but often adds a very significant amount of boilerplate for read queries. We ended up using a very lightweight orms for simple inserts / upserts and writing raw SQL for anything else.
Where does SQL fall in the video transcoding pipeline?
No, just because raw SQL queries work great for your toy blog/todo app with 3 tables and simple relationships, doesn't mean they work great for real world business applications with 100 tables and complex networks of relationships. Try maintaining the latter before you make blanket claims like "ORM bad".
In my experience, ORMs work well for toy projects, but become cumbersome to maintain in enterprise ones, especially where performance matters. There is a large overlap between engineers who refuse to learn SQL because it's not "convenient", and those who prefer ORMs because they are "easier", resulting in cohorts that don't know how to use either.
But also, I don't see how ORMs make managing large databases any easier, other than those with embedded migration capabilities, which can be very well extracted to their own tools.
- Your friendly local pentester
[1] - https://en.wikipedia.org/wiki/SQL_injection
But I would caution against adding too much business logic to the database, and tying message passing to your database doesn’t sound like the best of ideas.
I oscillate on being tired or amused by just how common tech people make this basic error. But I don’t believe it’s ever in bad faith. I think people in general suffer from perceiving their context as the context even though they’ve experienced maybe 1% of what there is out there.
>“Roughly” because Django ORM doesn’t support the JSONB `?` operator.
The `has_key` [lookup](https://docs.djangoproject.com/en/6.0/topics/db/queries/#has...) does exactly that.
> And if you need real SQL intervals, Django pushes you towards raw expressions or `Func()` wrappers.
It's possible to use a very similar construct to SQL Alchemy here by using the `Now` [function](https://docs.djangoproject.com/en/6.0/ref/models/database-fu...) (it uses `STATEMENT_TIMESTAMP` which is likely more correct than `NOW()` here alternatively there is `TransactionNow`) by doing `Now() - timedelta(days=30)`.
The result is the following `filter` call
which translates to the following SQL which can be confirmed in [this playground](https://dryorm.xterm.info/hn-47110310)