Ce qui se passe
L'auteur du billet a écrit son propre ORM, puis s'en est éloigné. Il maintient aujourd'hui Extralite, une gem Ruby pour SQLite, et défend une position simple : la couche de mapping ligne-vers-objet est une mauvaise abstraction pour des données relationnelles.
Son argument principal n'est pas idéologique, il est quantitatif. Combien de requêtes différentes votre application fait-elle réellement ? Pas d'appels : de requêtes distinctes. Son estimation, tirée des bases de code qu'il a observées, tourne autour d'une douzaine par table. Un constructeur de requêtes générique offre des centaines de méthodes pour produire n'importe quoi ; vous en utilisez douze.
Ce que l'ORM ne sait pas dire
La liste des absences est concrète : fonctions de fenêtrage, CTE, et
surtout la clause RETURNING.
L'exemple est parlant. Récupérer des lignes puis les supprimer donne, avec une
boucle naïve, un SELECT suivi d'un DELETE par ligne — le N+1 classique.
Corrigé en delete_all, on descend à deux requêtes, mais rien ne garantit
qu'elles touchent les mêmes lignes hors transaction, et il reste deux
allers-retours. Or PostgreSQL et SQLite savent faire les deux d'un coup :
delete from beads where material = ? returning *;Une requête, les lignes supprimées et leur contenu renvoyé. L'auteur indique n'avoir trouvé aucune API ActiveRecord pour l'exprimer.
Au coût fonctionnel s'ajoute un coût de masse : il chiffre ActiveRecord à environ 43 000 lignes de code, plus la mémoire et le temps processeur du mapping.
Ce qu'il propose à la place
Une classe par type de données — un PostsStore — qui expose exactement les
opérations utiles et renvoie des tables de hachage ordinaires plutôt que des
objets d'entité :
posts = PostsStore.new(db)
id = posts.create(title: 'foo', body: 'bar')
post = posts.by_id(id)
posts.update_by_id(id, title: 'FOO')
posts.delete_by_id(id)
posts.all
posts.all_by_category(category: 'baz')Vu de l'application, c'est la même chose qu'avant : une interface qui masque la base. La différence est que l'interface est fermée. On ne peut plus inventer une requête au fil de l'écriture d'un contrôleur : il faut l'ajouter à la classe, donc l'assumer et la relire. Les associations ne disparaissent pas pour autant — elles deviennent une méthode qui fait la jointure et renvoie le graphe attendu.
Ce que ça coûte
Le billet est une prise de position, et il faut lire ses limites comme telles. En quittant l'ORM on perd les associations gratuites, les validations, les migrations et le vivier de conventions partagées. Il faut connaître SQL — l'auteur y insiste, non sans une digression assez sévère sur le développement assisté par IA et l'indifférence à la performance.
C'est donc un choix d'architecture, pas une optimisation à appliquer partout. Le terrain d'essai naturel est celui qu'il désigne lui-même : les données qui n'ont jamais été des entités — étiquettes, séries temporelles, journaux d'événements, files de traitement, usages clé-valeur. C'est là que l'objet par ligne coûte le plus et rapporte le moins.
À retenir
La question à se poser avant de choisir un ORM n'est pas « que sait-il faire » mais « de combien de requêtes ai-je besoin ». Si la réponse tient sur une page, l'outil général est peut-être plus lourd que le problème.
Source : Noteflakes