Ask HN: What happened to the ORM?

95 points by olalonde ↗ HN
After all those years, I sometimes feel like the whole ORM thing hasn't made progress or been settled. Which ORMs do you like and what do you like about them? What is the sweet spot? I'm asking this because database I/O has always been my biggest frustration when doing application programming. These days, I do more functional programming and tend to side with the ORM haters but I do occasionally wish I could just abstract out the database more. Anyways, I'd just like to hear HN's thoughts on this.

112 comments

[ 3.0 ms ] story [ 167 ms ] thread
Waterline-ORM Sweetspot: Auto Migration.
(comment deleted)
They still have their uses, particularly with largish LOB/enterprise apps. For me they are no longer the goto solution for apps I write though, if I started building one today I would start with an "ORM lite" (never liked the term) like dapper. If it ever got big and complicated enough that an ORM was justified then I would introduce one.

I find an ORM really excels when you've got complicated business logic, the sort of thing where there are dozens of business rules that may or may not be involved in any given installation. Something like this is extremely stateful and and ORM does a great job of tracking this state. In fact, this is what I'd define an ORM as, a state tracker for database modifications. If you don't have much state then you probably won't get much out of an ORM.

Even when using an ORM does make sense, it's important to remember that they don't make sense everywhere. For a performance critical or particularly complicated query then SQL should still be a fallback.

(comment deleted)
They're still alive and well, just not sexy anymore. The most popular one is probably still Hibernate which is helped by Java being the top high level language.

I never understood the ORM hate. Every place I worked we intermingled raw SQL when needed. Hibernate has a way to clear query caches so you can use raw SQL when you need to. You can just write raw SQL exclusively if you want within hibernate so I don't get how you could lose anything :) .

Still, my experience is mostly with Hibernate. It's extremely mature, meaning reliable, feature complete, and only 0-30% slower than raw queries in most cases. It makes adding support for things like multitenency and audited tables a breeze without losing database agnostic behavior. It makes database upgrades a no-brainer 95% of the time too. It has a built in memory caches that help enormously with common queries. Probably the biggest thing is it makes the database strongly typed so it's possible to refactor. Code refactoring in Java is easy but raw stringified SQL is nearly impossible to fix in any language.

I think the biggest counterpoint to ORM is shitty ORM. Things like SQLAlchemy generate downright horrific SQL slowing everything to a crawl and causing deadlocks. Another honest counterpoint to ORM is the learning curve. Everyone is taught SQL but the ORM is more abstract and harder to reason about, not a fun thing to learn.

TBH I think most ORM's are just poorly done. Putting an object abstraction on a relational database is hard. The only ones I've enjoyed for completeness and performance are Hibernate and to some extent Entity Framework. EF being the easiest to use but a bit slower with less features.

I have heard good things about Dapper but never used it. I like the idea of injecting a SQL DSL into the language itself, wish it was more prevalent.

>> Things like SQLAlchemy generate downright horrific SQL slowing everything to a crawl and causing deadlocks.

Not my experience... Would you care to explain ? SQLA works pretty well for me, as long as I stay in rather simple queries. For example, data analysis queries are next to impossible to express with SQLA, but that doesn't matter much.

For the simple update queries, it just works fine for me. It also allows for very good control on relationships, etc.

I've always had success with ORMs when I let them write the 95% of trivial queries (SELECT * FROM table, INSERT INTO table, UPDATE table SET ... WHERE id = x). The complex queries I'd always rather write by hand to take advantage of the expressiveness of SQL, and to make sure that queries hit my indexes correctly.
I totally agree. Mr. Fowler's article lays out a good case for ORMs being a good, leaky abstraction with manholes for diving deeper when necessary. That implies that we can't, as application developers, use it as a black box, because underlying knowledge is important. Until someone comes up with a magic algebra for paving over the details SQL, we're stuck mitigating or dealing with its realities.

One of the things I've recently prioritized is the ability to run arbitrary queries (generally read-only) at some sort of REPL prompt, for debugging and investigative purposes. ORMs and their offshoots offer a great deal of support in that area because you can converse with an ORM in a domain-specific way without dropping into raw SQL. That can be a big advantage.

Maybe things have changed, I haven't used it since school and needed an example :-/
Amazing how perception works, everything you said about Hibernate and SQAlchemy is what I think about them, only switching them around. At least for the one time I had to use Hibernate, which was two years ago.

And not just because Java is an odious, ugly language, but the SQL Hibernate generates I just didn't like.

Mind you, this is with complex queries. For 90% of them, I didn't notice any difference between them, as far as generated SQL goes anyway.

I think your experience reflects that basic gist I've gotten from most professionals: ORMs are complicated and convoluted because they provide a solution for a very complicated problem. If you are somewhat new and don't understand things, it's easy for you to see the few problems introduced by ORMs and think that it means that the whole concept is just unnecessary bloat. With more experience and wisdom, you understand that the few quirks of ORMs are a necessary evil for having a software that solves such a complex and flexible task.
That's a good analysis of things, and on top of that, when you're new you'll treat an ORM like a hammer in search of a nail. So someone who's less experienced and doesn't realize that you should supplement the ORM with raw SQL queries might try to evaluate complicated queries in the ORM, with the usual results. Some of that has changed with e.g. LINQ and IQueryable in .NET, but it's no panacea.
ORM's are good form, why?

- Avoids mistakes when dealing with writing raw SQL queries (SQL is quite repetitive in practice)

- The declarative nature of classes maps well to types and relationships

- The declarative nature of classes maps out well to tables, even with polymorphism [1]

- Keeping "Models" in an ORM often maps out well to migration utility (Alembic, Django Migrations)

- Object-chaining map very well to queries

- ORM objects can be reused and composed

- They can abstract out intricacies across SQL dialects

- They can potentially make it easier to migrate to different SQL servers if no specialized features were used

- Can help avoid common security vulnerabilities like SQL injections

- When something can't be expressed via ORM relationships, they tend to allow the dev to drop down to raw SQL. In the case of SQLAlchemy, there is a core query language [2], too.

- In the case of Django, QuerySet is used as a standard throughout extensions that power a whole community. Plugins that don't even know each other (e.g. django-filter and django-tables2) can operate on the same django queryset to filter/search and sort/display data.

I mention QuerySet/Django ORM quite a bit in a recent blog post at https://www.git-pull.com/code_explorer/django-vs-flask.html.

[1] http://docs.sqlalchemy.org/en/latest/orm/inheritance.html [2] http://docs.sqlalchemy.org/en/latest/core/

Yes, with Django I would definitely stick to ORM. But when working in Golang or some other new language, unfortunately there isn't any ORM library that good. So people usually stick to SQL.
> Object-chaining map very well to queries

This isn't remotely true; it turns what looks like an in-memory access into a network round trip. Navigating your database using an idiom of lists and member accesses is a fast path to n+1 queries all over the place, or ORM tuned to eagerly fetch the whole world when you only touch a tiny piece of it.

The closer an ORM's API is to a monad, the happier you'll be. Fundamentally, accessing the database is executing remote code; the more you can package up into the query before it goes off and does anything, the better performance you'll see.

IMO trying to shoehorn objects (in the OO sense, with polymorphism, data hiding and behaviour) into a database is wrong-headed. Data hiding in particular is a wrong mental model for thinking about facts in the database, and the more is hidden, the harder it will be to reason about performance and bulk remote operations generally.

(comment deleted)
>This isn't remotely true; it turns what looks like an in-memory access into a network round trip. Navigating your database using an idiom of lists and member accesses is a fast path to n+1 queries all over the place, or ORM tuned to eagerly fetch the whole world when you only touch a tiny piece of it.

Only if you use naive ways of doing this. In .NET at least your overcomplicated expression can be compiled down to the minimum query needed to pull the bits of data you use.

It's not magic. I've personally removed dozens of N+1 queries from code written by one of my fellow consultants caused by misuse of Entity Framework. Here's a reference: https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113)....

At least with EF it's possible to completely disable lazy loading. I always recommend doing so -- when you're passing entities between methods it's pretty easy to lose track of what's implicitly 'loaded' on the object. Innocent changes to a method that operates on an entity can cause a database round trip...horrible idea.

> This isn't remotely true; it turns what looks like an in-memory access into a network round trip. Navigating your database using an idiom of lists and member accesses is a fast path to n+1 queries all over the place, or ORM tuned to eagerly fetch the whole world when you only touch a tiny piece of it.

That isn't true for e.g. Django's ORM, which lazily evaluates the query and only actually accesses the db after that, with a single query, and filtering done in SQL.

This is not the case - with Django's ORM you are still susceptible to the N+1 problem. It can be largely mitigated through `select_related` and `prefetch_related`, but the fundamental issue is there - that simply by accessing an attribute for an object that wasn't already fetched, you can do another database query without it being at all clear in the code that this will happen.
That's true, though select_related is mostly the default, and that behaviour is documented well. In practice, I very seldomly find N+1 cases with Djangos ORM, even when doing relatively complex data analysis.
This didn't use to be the case, I remember a single line of Django orm causing several hundred db requests, but maybe they improved it.
When was that and what was the form of the query?
(comment deleted)
An ORM provides easy DB access and various implicit operations. The trade off is that if you don't know what you're doing, your app gonna have bad performance. Seems fair to me.

Also, Django ORM documentation is very clear and you can use Django Debug Toolbar to analyse the raw sql generated.

> Fundamentally, accessing the database is executing remote code; the more you can package up into the query before it goes off and does anything, the better performance you'll see

The queries that most tend to be making just aren't that sophisticated. Relationships tend to be basic.

And I haven't even mentioned stuff that'd really, really hard to express/manage in pure SQL like tree/nested information that has to stay balanced [1]. Thanks django-treebeard/mptt and sqlalchemy-orm-tree.

> The closer an ORM's API is to a monad, the happier you'll be.

Developers using ORM's simply aren't caring about ORM's matching a certain construct. They care that models emit correct representations of their schemas and that the data is retrieved "fast enough".

Take it a different way: the best part about ORM's? They're effective 95% of the time, right out the box, so you end up avoiding time that'd be spent over and prematurely optimizing.

> IMO trying to shoehorn objects (in the OO sense, with polymorphism, data hiding and behaviour) into a database is wrong-headed.

Objects in things like SQLAlchemy declarative and Django Models map perfectly to generated SQL, so they also act as a way to generate migrations. It's that accurate. A lot of the relationships project's need expressed tend to be vanilla joins.

> Data hiding in particular is a wrong mental model for thinking about facts in the database, and the more is hidden, the harder it will be to reason about performance and bulk remote operations generally.

ORM's strive to hit a value sweet-spot in terms of code expressiveness, reducing duplication, handling the bread and butter relationships and types. That covers what most developers really need.

Perhaps there are projects out there not fitting to ORM's. Not all projects are sophisticated data mart projects, but even then, a good share of those still go back to simple joins at the end of the day.

And I've even gone as far as trusting heavy-duty stuff like django-mptt, along with plugins that filter and sort. I don't even look at the queries, all I see is they're running performantly. In all these years, SQL queries have never been a bottleneck. Maybe it's because I'm only storing simple stuff.

[1] https://en.wikipedia.org/wiki/Nested_set_model

(comment deleted)
But they're unnecessary baggage, because your top six reasons all work in reverse.

If you define your schema in your database and derive your data layer from that, you get everything in your list (apart from that thing that totally always happens where you switch your underlying database technology every few months).

But then you don't have your database defined in two places. And if anybody ever does modify the db by hand, your build will break and it will quickly surface itself as an issue at compile time instead of via an obscure error message somewhere 40 levels deep in the call stack.

How do you in practice derive the data layer from the schema? Somehow parsing the schema in the application code?
Personally, I generate base classes for table objects at compile time.

You can look at a table and its keys to determine what sort of thing it represents (entity, lookup, association, etc.) and build out helper stuff as needed. So in addition to basic CRUD anything that looks like an entity gets .Load(), .Save() and accessors for fields as well as .GetByWhateverID() methods for any foreign keys. I base my actual Entity classes off of those auto-generated base classes, so they can get blown away and recreated as often as necessary.

I also wrap any one-off stored procs that are lying around in calling code, so that they can be used in place of the standard-issue Frankenstein SQLBuilder thing that an ORM would have.

It's kinda all the upsides of an ORM, but without any dynamic garbage, schema-as-config-file, mystery auto-SQL, migrations, or (again) Almost-SQL-In-The-Magic-Query-Languague to Not-The-SQL-I-Meant conversion.

I always liked Django's ORM because it didn't even try to do any complicated stuff, it forces you to drop down to raw SQL instead (I haven't used Django in years, hopefully it's still like that).

Compared to something like Hibernate or SQLAlchemy that tries to support everything under the sun and can result in a lot confusion when trying to understand what exactly it's doing.

What happened to the ORM?

To my opinion, it was a bad solution to the wrong problem.

For one, we're not that enamoured with objects anymore (what with functional programming, immutability, etc).

Second, SQL and DDL, being declarative, is both a higher abstraction that (at least) most ORMs, and offers more fine level control to the DB at the same time!

Third, people don't really switch databases that often, for the abstraction between different SQL syntaxes to matter.

> Third, people don't really switch databases that often, for the abstraction between different SQL syntaxes to matter.

But third-party libraries shouldn't assume any specific SQL database, so if you want libraries that can do database things, ORMs are very useful.

They can always do SQL abstraction without doing the object-relational mapping.
I never seen orm as abstracting database out. More like easier to maintain way to access it and have features like caching/consistency available without much work.
It could be that most haters are simply using the wrong one? If JPA was my sole experience with ORM I would side with the haters as well, fortunately I built enough successful projects using DBIx::Class and SQLAlchemy to understand the advantages of using an ORM to the point that I refuse working without one.
I think part of the reason they are not talked about anymore is that people like to talk about the hot and sexy and not the mature and incrementally improving. The hot and sexy now is full of Javascript and noSQL so ORMs aren't as relevant as before. ie: schemaless JSON + dynamic languages have eaten a big piece of the cake.

That said, people seem to still use ORMs in Ruby, Java, C#, etc. and frameworks look fairly mature.

(comment deleted)
The big question is when to use ORMs/raw SQL.

SQL is the most concise and perfect fit for RDBMS.

However, at the application level there are benefits of using ORM.

- The application itself is usually imperative style as against the declarative nature of SQL.

- Chaining is sometimes more readable and concise. One can chain dynamic filters.

- Abstract the underlying data model with higher level names. SQL eq. of table views.

- Hides the underlying relational model. Which can sometimes be helpful in a large code base. And sometimes a curse.

I normally opt for ORM in Rails/Django web apps. But SQL in

- Performance critical - Report generation, where it might be complex and declarative nature of SQL shines.

Nowadays I'd say there's something better than ORMs, take Slick (Scala) for instance, it's an FRM (Functional Relational Mapping) or JOOQ (Java). Both of those provide pretty much 1:1 DSL to SQL while at the same time granting a lot of flexibility and type safety.
ORM is a leaky abstraction. It does provide benefits, like avoiding SQL injection, however, it still don't have the level of flexibility as SQL, and probably will never do.

It is still useful though, but hardly stands on its own term.

SqlAlchemy and the Django ORM are good - recently worked on a C# project and the devs are very anti-orm.

I think, the REPL makes a huge difference.

In a python project, getting a shell at any point - or experimenting in the shell Jupyter is really straightforward.

The brackety languages have options for this sort of thing now, but it is still just a lot more hassle - the easiest way to get something similar is to just write SQL.

What I see are often custom ORMs implemented for a specific library or purpose. Many of the APIs I work with daily are in fact a type of ORM.

That said, I never use an ORM myself unless it's one I implemented as stated above.

It is a good tool, people use it, however it turns out it was not a silver bullet (tm). I think it is pretty much settled for what things it makes sense or not.
ORMs aren't very good. They make simple things slightly simpler and get in the way the rest of the time.
I used to use an ORM but I discovered in fact that SQL gives me precision, control and performance and indeed I like it. I'll never go back to the ORM.
I still use ActiveRecord a lot (Rails) and I would not write SQL queries manually since the ORM works so well. But I used ORMs in other languages and they were not as convenient so maybe it depends on the ORM itself. The time it saves compared to standard queries is massive.

ORM haters would likely point out that complicated queries are hard to write with an ORM but these complicated queries are not a massive part of the work and can still be abstracted away in a method using SQL if needed.

Another good point of the ORM: validation of data. There are plenty of validation you cannot enforce in SQL (regex, postcode...).

> Another good point of the ORM: validation of data. There are plenty of validation you cannot enforce in SQL (regex, postcode...).

Those particular examples are trivial to enforce in every DBMS I am aware of...

Dbms constraints are a usually a subset of constraints that are supported by ORMs.
Not sure in other platforms, but in the .NET world, ORM(read Entity Framework) usage skyrocketed and improvements are made with each version of .NET released. I use Entity Framework. Both at work and when doing my home projects. Code First. I started with Database First and only recently moved to Code First. It feels so good modeling my database using C#.NET classes instead of doing it in SQL. Don't get me wrong. I do like SQL and I am very good with it, but I prefer C# better.

Speed of development greatly improved since I switched to EF. Speed of development is what matters most for me when building first version of an application.

(comment deleted)
One of the issues is that programming languages are evolving so fast it is hard for ORM libraries to keep up.

A good ORM library needs to be well designed, battle tested and mature. Then it provides great benefits over writing just raw SQL. That takes years of development effort.

The problem is that with new languages (Node, Golang, Rust) there hasn't been enough time and effort put into ORM yet to produce something good enough to be usable.

When developing in Java or Python there are great, mature and battle tested ORMs I would use but with these new languages I had bad experience with ORMs as they still seem experimental and have issues therefor stick to raw SQL when working in newer languages.

The problem with ORM? I always forget DQL "like" syntaxes while I never forget raw SQL.