← Back to context

Comment by Eikon

4 days ago

> The ORM is very lacking at the moment and the automatic migration generator only works with a small subset of possible operations,

I would have hoped that by 2025, new projects would have moved away from ORMs entirely. They really are a useless abstraction layer that always brings tons of trouble and usually makes queries harder to write.

Looking at the first example in the docs

https://cot.rs/guide/latest/db-models/#retrieving-models

    let link = query!(Link, $slug == LimitedString::new("cot").unwrap()).get(request.db()).await?;

I really don't get the point, and that'll certainly get worse with any somewhat non-trivial query.

Why go through all the trouble of reinventing a new DSL when the SQL equivalent would be so much cleaner?

    SELECT * FROM link WHERE slug = 'cot';

Composability is the often cited benefit. As an example, I can do the following in Active Record (Ruby):

  class Account < ApplicationRecord
    scope :active, -> { where.not(active_at: nil) }

    belongs_to :user
  end

  class User < ApplicationRecord
    scope :born_before, ->(born_on) { where(birthdate: born_on) }
  end

  active_users_with_birthdays_today = Account.active.joins(:user).merge(User.born_before(Date.today))

Contrived, of course, but it's not hard to see how you can use these sorts of things to build up record sorting, filtering, etc. Doing this by hand wouldn't be very fun.

  • The thought process can be the same in SQL. You can start by writing SELECT * FROM Account; then add your JOIN to User, then add your predicates. Then you can refine it – here, if I’m understanding the code correctly, you’re using User for a JOIN but never return anything from that table, so you could turn it into a semi-join (WHERE EXISTS) and likely get a speed-up.

  • I'm not sure why you think you’d need an ORM for that. Most SQL client libraries allow you to compose queries, and query builders, which are not ORMs, can handle that just fine too.

    • By the time you have a query builder that will rewrite queries so that column name and table names to be compatible with composition you basically have an ORM.

      Especially if you are defining types anyways to extract the data from the sql into.

Writing raw SQL is perhaps indeed easier for simple queries, but put some foreign keys inside or slightly more complex relationships between tables and you'll probably quickly fall into the trap of having to remember your entire database schema to write anything. Yes, the example from the documentation is slightly more complicated, but it checks at compile time the exact column names and types, so you get feedback much quicker than actually running the SQL query.

In addition to that, over the years of writing web services, I always found raw queries much less maintainable. The ORM immediately tells you that you messed up your refactoring, whether it is just renaming a column, or removing a table completely.

Pretty much every ORM (including the one in Cot) also allows you to write your own custom queries if the abstractions are not enough, so there's always this fallback option.

  • > Yes, the example from the documentation is slightly more complicated, but it checks at compile time the exact column names and types, so you get feedback much quicker than actually running the SQL query.

    Well sqlx checks at compile time your raw query if you use their macro, at the expense of increased compile times.

  • I never understood the hate against ORMs. It always seems like a superiority complex or some claim that SQL is “so simple”.

    Yeah SQL is extremely simple to write, that isn’t really the problem ORMs solve for me, but rather they solve composability and reuse of queries.

    I think there is a similar vein of people crying about sites not using 100% vanilla js without a framework. They are missing the point or don’t have enough experience with the actual problems the abstractions solve.

    • This is one of my biggest pet peeves against HN. It's also obvious ORMS won, every new framework creates them. People who keep speaking about not using them are crazy.

    • I am confident in easy SELECT queries, even with multiple tables, but once i have to use JOIN or other advanced things my queries are a mess, returning data i never expected.

I've yet to see an ORM which doesn't just eventually make everything harder. Especially when it comes to automatic migrations, which seldom seem to take into account production necessities like multi-node deployment, downtime considerations and not destroying your production data silently because it doesn't fit.

  • ORMs are one of those things that make sense for really tiny projects but fail to scale once complexity settles in.

    • Exact opposite experience.

      “SQL strings are one of those things that make sense for really tiny projects but fail to scale once complexity settles in“

      Large projects require reuse, composability and easy refactoring. All things ORMs excel at.

      On a small code base it is easy to rename a column or add a column, etc.

      On a large code base with already 100s of queries using that table, without an ORM it isn’t as straightforward to update all references and ensure that ever place consuming that table has the new column info.

      1 reply →

  • > like multi-node deployment, downtime considerations

    Never had an issue with Django on a large project at a previous $8b startup whose code base went over several major data refactors.

    In fact Django’s excellent migrations was specially called out for the reason for our confidence in making large DB refactors.

It's 2025, and some ORMs are quite good (eg. Django, ActiveRecord). You can have an ORM with escape hatch of SQL. I do not understand the people who are so against ORMs.

I use ORM query interfaces 80% of the time, and for performance critical queries I can use direct SQL. But the query sanitization, smart preloading and performance checks, schema management and migration tools they often provide, and type checks / hints are really nice features of ORMs.

Furthermore, ORMs are ESPECIALLY useful in teams where you have more junior devs who need these guardrails.

I think the issue here is trying to reinvent a new ORM.

As long as it's still possible to also use SQL queries with it it's fine, right? I know some people who prefer using ORMs and some who like writing SQL.

  • It’s not fine because ORMs never make sense as an abstraction layer. They try to map concepts that are inherently incompatible. Relational databases are designed around set theory and strict schema constraints, while object-oriented programming relies on hierarchical structures and mutable state.

    • Well just because it uses the word object doesn’t mean it is strictly for object oriented programming.

      Haskell and Elixir, etc all have ORMs (or rather ecto calls itself a data mapper but functionally it is 1:1 analogous to an ORM in any other language)

  • I don't think it's a good idea to have a mix of both in a codebase, because then you'll have to be good at both - kind of defeats the purpose.

    • > I don't think it's a good idea to have a mix of both in a codebase, because then you'll have to be good at both - kind of defeats the purpose.

      When using an ORM you already have to be good at both for any nontrivial query.

    • My impression is people use ORM not because they lack SQL skills (which is possible), but because it makes object-mapping much easier and with a large count of tables and ever-evolving database, at some point it just feels more natural to drop writing SQL queries and rely solely on the ORM to build up your object graph.

      I actually find myself on crosspath where I did start with just SQL but now the database has grown a lot and it is just too much effort to keep writing queries as features pile up. Switching to ORM in one take would be nearly impossible but probably it could work as a step-by-step process, so have both SQL and ORM. Still thinking about this.

Nah. The query is $slug == LimitedString::new("cot")

Also, people like ORMs. Type safety is nice too without having to map manually. Sqlx is also great

ORMs, in my experience, fail the hardest when DB admins and web devs butt heads in when and where to create models. It can be an extra hurdle for data flexibility, but it clarifies data architecture at the beginning. In our Django environment, the DB admins won and we added `MIGRATE = False`.

I never worked with Rust, but maybe the complexity is due to how the language works.

Using Django's ORM, that would simply be: link=Link.objects.get(slug="cot")

It'd still be very readable and manageable even for complex queries.

ORMs are great. They afford abundant opportunities to look like a hero by swooping into a project and cutting a page load of forty seconds to under one second.

if you can decompose the query, you can unit test the logic against inmemory sets(or language specific equivalent).