
The Troublesome Active Record Pattern - tannhaeuser
http://calpaterson.com/activerecord.html
======
buffington
With all due respect, Cal Paterson doesn't seem to know what he's talking
about.

The Ruby example he uses as the "The Troublesome Active Record Pattern" simply
bizarre. You'd have to deliberately avoid reading the documentation for
ActiveRecord to produce that code.

In other words, if you write terrible code, you'll get terrible results.

In actual fact, when you read the docs and actually use ActiveRecord as it was
intended you can do exactly what he says it doesn't. Here's what that looks
like:

    
    
      Book.joins(:author).where(authors: {nationality: "Italian", state: "deceased"})
    

That produces a SQL query that's nearly verbatim what he claims ActiveRecord
can't do.

~~~
calpaterson
Hi, Cal Paterson himself here. It's not a Ruby example, it's a Python example.
Don't look too deeply into the code samples for specifics though, they are a
pastiche.

You are not the only person to get the wrong end of the stick re:"Active
Record". I'm talking about the Active Record the design pattern, not about the
Ruby library that took the pattern as it's name but now is apparently a
querybuilder library. I probably need to go back and make it clearer somehow.

EDIT: I've done this now. Back to stockpiling tinned food and bullets!

~~~
ptr
An article dedicated to why Rail's Active Record library isn't actually using
the Active Record pattern would be interesting!

~~~
zrail
Rails' ActiveRecord is both a query builder and also the canonical Active
Record pattern. For example, one can say

    
    
      book = Books.joins(:authors).where(author_name: 'James S. A. Corey').first
      book.title = "Tiamat's Wrath"
      book.save!
    

It's not that they stopped supporting the Active Record pattern, it's that
they rebuilt the whole thing a few years ago with a real relational query
builder under the hood.

~~~
mnutt
Not to detract from your point, but rails 3 was released with arel in 2010, so
it has used a relational query builder for a decade at this point.

~~~
zrail
Thanks, I wasn't super clear on the timeline there.

------
cfv
I want to like this article because I agree with the overarching general
statement that the ActiveRecord pattern is troublesome.

But it's... not very good. He constructed his own interface in order to
complain about it, an interface which is so subptimal that it lets him
complain about how bad it is as if the pattern was somehow to blame.

In most Active Record implementations I've seen, a Record is a live tuple
("Active" "Record") and you typically have some form of Repository ("Books"
maybe in this example?) alongside of it which is responsible for carefully and
optimally building the record objects.

The pattern itself says nothing about how to query things, it talks about a
kind of DAO that has convenience methods to access and manipulate a tuple.

Also, what's with the REST rant? I don't think I understand how that's linked.

~~~
topspin
> He constructed his own interface in order to complain about it

Yes, it is contrived, but it faithfully represents many examples I've seen in
the real world.

> Also, what's with the REST rant? I don't think I understand how that's
> linked.

Why not? It's clearly explained. The typical REST API (as opposed to the well
designed REST API) is just Active Record over HTTP: /something/:id called in
N+1 loops. And this emerges for the same reason as it does in the Active
Record pattern; the 'central abstraction' offered by REST is mapping every
resource to a URL, as opposed to mapping every row to an object.

------
jmull
I think this is pretty confused.

The active record pattern from the article isn’t the active record pattern
from Fowler and not really how it’s generally practiced. Fowler’s active
record is about having data access methods on the data objects. The article
says whole object access and access by id, so something different. Probably
would have been a lot better not to reuse the term.

Whole-object access is very typically part of ORMs. The article raises an
issue this way: “Retrieving whole rows is hugely wasteful when only part of
the row is required to resolve a user request.”

But note that you can control what’s included in the object. In other words,
if you are running into problems due to getting too many columns because your
object has too many properties... then change your object. An ORM giving you
what you asked for isn’t a problem with the ORM. I think when people run into
this kind of thing, I think they are usually stuck in a situation where they
can’t express queries in a reasonable way. (Could be due to complexities or
limitations of a specific ORM, or could be silos between front end and back
end development.)

Getting objects by id is very typically part of ORMs as well. But ORMs very
typically include a mechanism for queries as well, so I don’t even know why
that came up. Maybe again the problem the author is trying to get at is lack
of reasonable flexibility to express queries.

Now, a general issue with ORMs is that they tend to bury queries under an
extra layer of abstraction. I think there’s an article to be written about
that.

The article does go on to say a data access mechanism should have first class
queries and first class transactions. Which are completely true, but dont
really have anything to do with active record. (Those are just a start,
though. Some think an integrated query language is “first class“, which is
backwards.)

The part about restful apis is probably not worth breaking down. it conflates
restful, the idea, rest apis as commonly implemented, http, json, and so
forth.

~~~
bradly
Also, if you need or may need multiple attributes of an object, fetching the
entire object by id may save time as the id-based query can be cached and save
you from making multiple trips to the db.

------
davedx
I work a lot with EntityFramework (the "unit of work" pattern) and it does
solve the issues the author brings up fairly well: queries are first class
(LINQ with static typing), and transactions are on by default between each
.Save operation.

However it's not a magic bullet by any means: EF will still sometimes generate
weird join queries that require serious head scratching and second guessing to
get into a better form, and huge nested .Include / .ThenInclude trees are
extremely common when your data is highly normalized. This can lead to the
same kind of inefficiencies seen with the ActiveRecord pattern.

FWIW what really slows things down isn't typically selecting every column,
it's those really giant joins or joins on things that aren't indexed. I think
there's some room for improvement in the DX of EF itself here -- it could at
least tell you if you write a join on a non-indexed column. (It has all the
information there, as the indices are defined in EntityMap source files).

Overall, when building applications on a RDBMS <-> OOP <-> REST API, there are
indeed different fundamental abstractions, and no framework or library will
save you from needing to understand how those abstractions map between each
other.

And believe me, GraphQL is also no panacea! It has its own similar but
different issues.

------
pmontra
The problem with this code

    
    
      book = Book.get(isbn="978-0099466031")
      book.copies_in_stock -= 1
      book.save()  # WRONG! irreparable data loss here
    

is that in SQL you should write it like this:

    
    
      update books set 
        copies_in_stock = copies_in_stock - 1
      where isbn = '978-0099466031';
    

No needs of transactions.

I don't remember how to do it with Django's ORM but in Rail's ActiveRecord I'd
use directly the connection object

    
    
      update_query = sanitize_sql(['update books set copies_in_stock = copies_in_stock - 1 where isbn = ?', isbn])
      ActiveRecord::Base.connection.execute(update_query)
    

This saves a lot of programming pain, possibly skip some unnecessary bugs and
get better performances.

sanitize_sql is from
[https://api.rubyonrails.org/classes/ActiveRecord/Sanitizatio...](https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html)

~~~
JoshuaDavid
The transaction-free approach still has potential issues though. Consider:

    
    
        select isbn, copies_in_stock
        from books
        where isbn = "12345678"
          and copies_in_stock >= 1;
        isbn:            12345678
        copies_in_stock: 1
    
        update books
        set copies_in_stock = copies_in_stock - 1
        where isbn = "12345678"
    

If you have two of the above requests happen at the same time such that the
execution order is SELECT SELECT UPDATE UPDATE, you'll end up with -1
copies_in_stock. You can try adding an "and copies_in_stock >= 1" to the
UPDATE and then look at the number of affected rows, and perform recovery
logic if the number of affected rows is 0, but at that point you've basically
implemented a transaction, probably badly if it isn't what you set out to do.

~~~
ivanhoe
Don't mix transactions and locks. Transaction just ensures that all queries
within it were run successfully and atomically. Locks are used to prevent race
conditions. And in your particular example neither a transaction, nor lock
would help, you need to check explicitly in UPDATE if you've got any books
left.

~~~
balfirevic
Race conditions absolutely are among the set of things transactions are
supposed to help with and locks are one of the several ways they do it. It's
the I from ACID, which stands for Isolation.

Transactions are not omnipotent in that regard and they fail in several well-
researched ways, but it is in their purview.

------
raziel2p
The only difference between active record and non-active record is that with
the former, there's some globally accessible database client session object
that the record objects know how to get to.

There's no difference between `Author.get(id)` and
`session.query(Author).get(id)` in terms of code design, data integrity or
whatever. Maybe you could make the argument that by forcing you to interact
with the `session` object you are encouraged to call `session.save()` which
makes the transaction explicit, but I find that a thin argument.

People are going to make O(n^2) mistakes like the one used as an example here
regardless of whether they use an ORM. In fact, people make those sort of
mistakes without even touching the database.

Yes, the ability to expose query objects is super important, but there are
active record libraries that do this, for example Laravel's Eloquent or
Django's ORM.

Seems like this article is conflating a lot of issues that aren't really
related. I would re-write the whole thing as:

1\. The importance of native query objects in your ORM 2\. The importance of
transactions 3\. Traps of O(n^2) when using ORMs

Don't even bring up active record.

~~~
lmm
> There's no difference between `Author.get(id)` and
> `session.query(Author).get(id)` in terms of code design, data integrity or
> whatever. Maybe you could make the argument that by forcing you to interact
> with the `session` object you are encouraged to call `session.save()` which
> makes the transaction explicit, but I find that a thin argument.

What's thin about it? The most important aspect of code is being able to read
it and understand what's going wrong; a seemingly innocuous function call that
actually interacts in a complex way with some hidden global state is pretty
much the worst kind of code you can write.

------
gt565k
You can't misuse an ORM pattern and then call it bad for performance reasons.

Same thing is true for SQL.

I'm currently working on migrating a 10 year old asp dot net c# system of
applications. The amount of nasty stored procedures being called from other
stored procedures that call on multiple views and total disregard for data
modeling best practices is atrocious. I've had to just throw away shit code
and get the business requirements in order to completely redo the SQL queries
because they were impossible to read and fix.

We had a single query with over 2000 execution plans due to parameter
explosions! It confuses the shit out of the database query planner and caused
intermittent performance issues based on who ran the query with what
parameters and when they did it.

His code example with m*n run-time is a total misunderstanding of how to use
an ORM and is common among people who are used to thinking of databases as
dummy stores. In reality, a database run-time can provide great performance
when used correctly. Data partitioning, query optimization, materialized
views, are all great skills to have in your toolbox. Most developers don't
know jack about databases, data-modeling, and performance optimizations.

Active Record is fantastic for CRUD operations, it removes boilerplate code,
and still provides good ways to join relational objects. Always select only
the fields you need in ORM queries to reduce overhead and the data retrieved.
Tune your database (rebuild indices, and partition your data properly).
There's a general disregard for understanding technology and hacking of
business logic in the service layer which results in poor performing
applications. Perhaps that's what makes people hate on Rails and Active
Record. It makes it easy to write bad, non-performant code. What works today
on your small dataset, will not work 6 months or 5 years from now.

A tool is only as good as the person using it.

Don't be a fanboy hating on specific technologies, when you don't know when to
use what for a particular use-case.

------
ben7799
I have seen nothing but problems with the whole Active Record/ORM pattern in
the last 15 years.. mostly java enterprise software. The problems existed
before but IME they've gotten a lot worse. I'm pretty much in total agreement
with the author, although he doesn't touch on all the points.

The "pattern" (pun intended) always goes like this:

\- Gotta get 1.0 out really fast

\- Agile Dev really coming on strong last 15 years

\- Architect/early dev lead is not a DB guy

\- ORM + Active Record looks attractive and fast due to less code

\- Junior devs don't even have to learn SQL

\- Simple Active Record architectures let you layer on new stuff to get the
all important 2-week sprint demo. Since Agile overvalues the sprint demo &
showing stuff in the UI is what matters to PMs performance doesn't matter and
gets ignored here. Infrastructure work for future scaling doesn't count as
points and/or is considered poor use of time.

\- Tons of entities & repositories based around Active record start getting
built, it saves tons and tons of code over hand written/optimized SQL code

\- Project launches and starts getting customers, getting bigger, needs to
scale

\- Active Record & Naive ORM generated queries start breaking down due to
locking too many rows in DB

\- Database deadlocks eventually start happening

\- DB Connection pools eventually start running out of connections

\- Customers are furious

\- New Upper management comes in and wants to rein in PM to try and get
developers to work on the massive tech debt. Agile starts to not even work
since tech debt makes it look like no work is happening since it doesn't demo.

\- Developers start having to piecemeal replace some ORM generated queries
with native queries/query builder type patterns

\- Eventually the team starts to have to rip the ORM out completely to
continue scaling.

The alternate pattern that happens is that the team gets hot under the collar
to drop the SQL database for a column-store/NoSql database. The problem wasn't
the SQL database here but bad usage of patterns that the NoSQL DB probably
doesn't support if it is not an ACID DB.

My feeling is most of these ORM based/Active Record type systems are often 3-4
orders of magnitude slower. Fixing some of this is somewhat one of my
specialties.. I have rewritten many stacks that were taking minutes to finish
and had them working in < 1 second once the ORM was taken out of the picture.

~~~
dpc_pw
This comment is spot on. I've seen similar scenarios myself. :)

99% of articles like the above one come down to: "developers wanted to pretend
there isn't some underlying data model underneath, and abstracted it away".

The problem isn't that much in ORMs / query builds etc. It's with "Objects"
itself, and the idea that one can abstract away the data model and pretend the
business code can be written as a bunch of OOP classes and such. It seems like
a genius move at the beginning but hits a really steep cliff once the load and
complexity of problems incrementally increases. When it starts to happen it's
actually funny how not many people can actually realize what happens, and they
usually struggle with band-aid solutions and blaming irrelevant pieces.

I decided to post on twitter about it:
[https://twitter.com/dpc_pw/status/1240719977071575040](https://twitter.com/dpc_pw/status/1240719977071575040)
in case anyone is interested.

------
floatboth
Don't pretty much _all_ "active record" ORMs now have query builders and
encourage their use? The pure pattern seems pretty dead to me..

------
tirumaraiselvan
> One new alternative is GraphQL which is a query language and server that is
> able to combine REST endpoints into a single result - making the query a
> first class object.

I think it would be more correct to just say that GraphQL encourages queries
as first class object as it exposes the entire schema and allows for granular
querying.

Implementing it by composing REST APIs is, well, an implementation detail.
There are other ways to do it too.

------
sidewndr46
I wrote about this years ago on the topic of Golang specifically:

[http://www.hydrogen18.com/blog/golang-orms-and-why-im-
still-...](http://www.hydrogen18.com/blog/golang-orms-and-why-im-still-not-
using-one.html)

The phrase "object-relational impedance mismatch" resonates pretty strongly
with me still. That being said, I still use lots of ORMs in my day job.

------
johncs
> This pattern of access is simple and intuitive but causes huge problems in
> even small projects. Retrieving whole rows is hugely wasteful when only part
> of the row is required to resolve a user request.

Retrieving a whole row rather than just a single column from a database that's
likely on a local network definitely doesn't seem "hugely wasteful",
especially for smaller projects.

~~~
calpaterson
Author here - I'm afraid it is hugely wasteful. The network is really nothing
to do with it but instead the fact that you have to make a table read instead
of an index read. This seems like a small thing but as I describe it changes
the complexity class of the algorithm you have effectively written.

~~~
balfirevic
How does it change the complexity class of an algorithm?

Database performing a table read because additional columns were requested
shouldn't change the complexity class if the query was otherwise successfully
satisfied using the index.

I searched the article for "complexity class" and, while I found the phrase,
it didn't explain much.

~~~
calpaterson
Sorry, yes, you're right. It doesn't change the complexity class just because
you have to go to the heap. It is however a lot slower for the usual reasons:
the heap is less dense in memory, it's a second thing you have to do, you have
to serialise/deserialise more, etc etc.

But yes, you're right: just going to the heap is not a change in complexity
class.

------
jchw
As a knee-jerk response I was going to point out that all of these problems
were already solved well by complementary patterns available in libraries like
SQLAlchemy, but after reading it is clear that what the author is referring to
as “Active Record” is different than what you are probably thinking. That
said, I don’t know any ORMs that don’t have query building APIs right now, so
I’m not sure what this is aimed at; its clearly possible to combine object
based ORM interfaces very nicely with query builders. The relationship is
beneficial imo. I think this will become more apparent in languages like Rust
where you could potentially use metaprogramming to provide a bunch of type
safe sugar revolving around your model classes in the query builder (I haven’t
tried SQL in Rust yet - this may already exist for all I know.) It is already
useful in Python where it can aid in migrations, scaffolding for tests and new
environments, and of course, the pretty common case where you are really
dealing with entire objects, like CRUD.

~~~
steveklabnik
It does!

[http://diesel.rs/](http://diesel.rs/)

(Created by one of the former maintainers of ActiveRecord in Rails. It's much
more of a query builder than an AR implementation.)

------
FeepingCreature
If you can separate call sites somehow, can't you just lazily fetch attributes
as required and also remember which attributes were needed for next time? Ie.
teach the ActiveRecord library to "learn" its usecase automatically, while
appearing trivial to the user.

A similar pattern could be done for on-demand joins, though you might need
macro magic (or a dynamic language that lets you, say, subtype 'string') to
derive joins from attribute values. For instance, if you have a sufficiently
advanced typesystem, you may return instead of a string something like a
`SqlSourcedType(string, "tablename", "attributename", originalCallSiteId)`
that acts interchangeable with a string. Then later on when passing such a
type to a load on another table, the framework would know that you were using
a join idiom, and could load the joined tables directly in the first query the
next time around. Basically JIT SQL.

------
tlarkworthy
1\. ActiveRecord is extremely troublesome but not because of bandwidth. Sure,
data movement is a performance headwind, but not the end of the world (in a
row store all the data is adjacent anyway). Active record problematic because
of its non-immutable nature. You cannot copy and mutate rows without weird
side effects (in Spring for sure), which leavers you with an inherently
imperative interface to your DB.

2\. REST does not insist you fetch whole rows. You can have field masks with
REST, it's inherently untyped, there is literally nothing preventing you
adding that performance optimization if you need it, in a backwards compatible
way (add the field mask query param later when you need it).
[https://developers.google.com/slides/how-tos/field-
masks](https://developers.google.com/slides/how-tos/field-masks)

------
kingraoul3
This is described as The Object-relational Impedance Mismatch:

[https://en.wikipedia.org/wiki/Object-
relational_impedance_mi...](https://en.wikipedia.org/wiki/Object-
relational_impedance_mismatch)

Thanks you Cal for the insightful and concise article!

------
cryptonector
Yes, ORM patterns generally lead to great inefficiencies, such as client-side
implementation of JOINs and such.

That doesn't mean that you can't have a RESTful API for a relational database
that doesn't have these problems. For example, PostgREST does exactly that.

------
mjevans
I think I'd like to just write a native SQL query (past basic tables and joins
there's always some platform specific quirk or optimization) and have a better
way of mapping that to generic 'interfaces' (maps/dictionaries/whatever) or
typed objects with members of specific types, and have the data correctly
coerced to those types.

The biggest pain point nearly always also seems to involve time conversions
that might or might not use default assumptions and environment variables
which language examples almost never cover. Toss in specifying an encoding to
handle the result as (or to disable) and I think that's what might make up a
good DB interface specification for drivers to implement.

------
cryptica
I still don't understand what problem ORMs solve and I've used many different
ORM solutions for over a decade. SQL really is the best and simplest interface
to get data out of a relational database. Some people will argue that SQL is
too complicated for simple queries like CREATE, READ, UPDATE, DELETE but I
would disagree because these simple operations require simple queries. Reading
an element by ID is the easiest operation of all: `SELECT * from tablename
WHERE id=idnumber`. It's not difficult at all. Abstracting this away inside
complex objects doesn't solve any real problems, it just creates more
problems.

Sometimes developers will argue that they end up reusing variants of the same
query in too many places... That is a poor argument; if that's the case, it
means that your code is poorly designed. If you organize your code in such a
way that components have clear and distinct responsibilities, you're not going
to end up with duplicated queries.

Also, the perceived 'impedence mismatch' between OOP languages and relational
databases is only apparent when you try to design a data layer in OOP code
which exposes a general purpose interface to execute specific queries.
Impedence mismatch can be solved simply by dealing with each database
operation on a case-by-case basis using raw SQL or Language Integrated Queries
(E.g. LINQ). It adds a little bit of boilerplate, but it's absolutely worth
it. You don't need general purpose entities just to access data.

------
hinkley
I really wish there were one right answer, but it doesn’t seem like there is
one.

The last time I tried to write code with just raw SQL, the query patterns got
tedious really quickly, and I found myself using the most bizarre patterns to
normalize query fragments and avoid typos in rarely used queries. It all made
sense to me as I did it but others thought I was nuts. They were probably not
wrong.

But every time I used an ORM or worked with people who did, things got sloppy,
quickly.

~~~
colonwqbang
It's a pretty radical alternative but I've been playing a lot with the
postgresql-typed library. It actually connects to the database during compile
time, validates the queries and calculates appropriate type constraints for
your application code.

Simple typos are caught of course, but also more subtle problems. If for
instance you drop a NOT NULL constraint on a column in your database, your
application will not compile until you add in the appropriate NULL handling.

Like I said it is a bit radical, but to me it clearly feels like the future
and I miss it whenver I work with a traditional DB mapping.

------
ptr
It's a very interesting topic but it gets weird when large words ("completely
unworkable", "causes huge problems in even small projects", "retrieving whole
rows is hugely wasteful", "unfortunately Active Record access patterns break
down almost immediately") are used to describe something that doesn't match
one's experience at all. I wish we could focus on the Active Record concept
itself rather than implementation details or specifics of projects where the
pattern has been applied.

The article talks about projection, joins and aggregations, but many projects
in Rails that I've seen just don't need those: it's often solved in the
application. Yes, it's SUPER SLOW, it loads data just to throw it away again
and can break ACID properties, but it generally works -- these applications
aren't handling transaction on a stock exchange or controlling a pacemaker.
And in the case of Rails' ORM, it's been very easy to do real joins,
projections and aggregations when needed, meaning that the Active Record
pattern itself hasn't excluded that possibility.

The paragraph about transactions is also a bit weird as it has nothing to do
about the Active Record pattern afaics.

~~~
calpaterson
Apologies for the word largesse - I should probably have edited out the
adjectives (which always cause me a problem) more aggressively.

I actually find that even though these applications aren't running pacemakers
that they still don't work. It's really easy to write what is in fact bad
algorithm for accessing (eg) shoe size data when you are working on rows only
as objects.

I cannot speak (and am not) for the Rails ORM but the problem here is less
about some library people use but about the primary affordances being "get
this object by id, edit it, and save it back". This is just not right IMO and
is the underlying cause of the famous "object-relational mismatch". I suppose
I come down on the relational side of that in that I think object access is
the problem.

------
gmac
I agree that too much abstraction from your SQL database is a world of
frustration. I feel that ‘query builders’ themselves can end up feeling like
too much abstraction, albeit of a slightly different kind. I’ve written about
an alternative approach for TypeScript [1] and am in the process of turning it
into an npm module (complete with nested queries that produce LATERAL joins,
with all results fully typed).

[1] [https://github.com/jawj/mostly-
ormless/blob/lateral/README.m...](https://github.com/jawj/mostly-
ormless/blob/lateral/README.md) (note: I’ve updated the code and write-up
here, but not yet the instructions for setting it up, so YMMV on that)

------
jasonkester
What a strange article. It starts off nice enough, describing a handy pattern
that lots of us use to handle individual records for CRUD.

But then it constructs this giant crazy strawman of ways that you could do a
bunch as ridiculous silly things, and how if you did those crazy things it
would completely wreck your performance.

Certainly it would. But nobody would ever _do_ any of those crazy things.

My version of this thing, for instance, makes a point of wrapping all your
Stored Procedures for you, and provides methods to construct single items or
lists of them from a dataset that you hand it. That solves roughly 100% of the
panicky disaster scenario that the author predicts. (The other 0% is the giant
(also usually 0%) performance hit you get from pulling all six columns of data
from a table when you only need two of them.)

So yeah, don't recursively enumerate the entire database three times when you
can write a query with a Join in it. But don't make up silly query languages
that aren't SQL either.

Just do what sensible people do with this data pattern and you'll find that it
works nicely.

------
tln
I wish Django's ORM had a query builder that was closer to sql. Aggregations
are a PITA and HAVING clauses require small mammal sacrifices.

Not sure the blame belongs to the ORM but Django rest framework has been the
source of N+1 queries as well

------
febeling
I observe how this post creates the wish in me to say hurtful things to the
author.

Active Record is a powerful pattern - for it's usecase. You use it to build
applications to find, create, update and delete objects. It's not useful for
complex queries, aggregations, graph applications, NLP, mathematical analysis,
or any other use case it's not meant to solve. No surprise there.

The ruby package of the same name includes quite a few things that support
complex queries and aggregation, and that way it goes beyond the pattern which
it is named for (including all of the examples that are presented as the
better alternative solution). For requirements that are best solved by
dropping to SQL you can bypass the records machinery entirely, in marked
difference to some other ORMs which try to completely hide and monopolise DB
access.

------
ZiiS
The problem with equating SQL and GraphQL is you are not expected to write the
RDBMS. One day it may be as easy as creating a DB schema; for now I will focus
on developing RPC APIs.

~~~
praveenweb
Have you checked out [https://hasura.io](https://hasura.io)? That may be
exactly what you are looking for. You create DB schema (Postgres in Hasura's
case) and point Hasura to Postgres to get an instant realtime GraphQL CRUD.

Disclaimer: I work at Hasura.

------
mmedal
Perhaps “dead Italians” isn’t the greatest example data to be using at this
moment.

~~~
pmontra
I'm Italian, living in Italy. I didn't feel anything bad going on with the
example. Actually I was happy to see an example about Italy. It's a rarity.

------
fraktl
I stopped reading at the title "Problems arise".

Example contains a piece of code that uses nested loops and each loop queries
the db - producing many queries.

There's no problem with ActiveRecord. There's a big problem with programmers
not learning relational databases.

Here's an excerpt from the post: _" To get all ISBNs of all deceased Italian
authors in the Active Record pattern"_

Now look at this black magic: you create an SQL view with all the joins you
need in order to cross-reference data and extract what you want, then you have
your ORM use your view as if it were a table. Job done. Your code is now 1
liner and the database won't receive too many queries.

I know that the text references SQL views but this is what's said about them:
"SQL has views too of course, but most ORMs don't have any support for them"

A view is, for all intents and purposes, a table to the ORM. There's no
special support needed for it, if you use the view to read from it.

I'm sorry for being one of those guys who shit on someone's hard work, but the
mister Cal Peterson missed the whole football field here.

