Skip to content

10 erreurs fréquentes que les développeurs RubyOnRails font

Soyez sociable ! Partagez :
  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

Freelance Ruby On Rails

Cet article est une traduction de l’article :  http://www.toptal.com/ruby-on-rails/top-10-mistakes-that-rails-programmers-make. Le blog de l’auteur : http://rubyjunky.com/

Ruby On Rails (‘RoR’) est un framework populaire très en vogue, basé sur le langage Ruby oeuvrant à simplifier et à rationaliser le processus de développement d’une application Web.

Rails a été construit sur le principe “Convention au dessus des configurations”. Simple mais, par défault, Rails attend que les développeur suivent de bonnes pratiques (convention de nommage, structure du code…) découlant sur le fonctionnement ‘magique’ de votre application sans ajouter de détail.
Ce principe a ses avantages, mais également ses inconvénients.
Les plus embêtants : la “magie” opérant peut amener à des maux de tête terrible, des confusions et des “Mais c’est quoi ce truc?”. Il peut également ajouter quelques ramifications indésirables fermant les yeux sur des points de sécurité ou causant des baisses de performance.

Par conséquent, même si Rails est facile à utiliser, il est également facile de mal l’utiliser ! Voici donc, 10 erreurs fréquentes (et leur solution) que les développeurs Rails font.

10 erreurs fréquentes en Ruby On Rails

#1 – Trop de logique dans le contrôleur

Rails est basé sur l’architecture MVC. Dans la communauté Rails, on parle depuis un moment de “Gros model, petit contrôleur” (Fat model, skinny controller) malheureusement, plusieurs application Rails ont tendance à oublier ce principe. C’est tellement plus simple de déplacer la logique de rendu (qui devrait être dans un helper) ou la logique de modèle dans le contrôleur.

Le problème est que le contrôleur commencera à enfreindre la règle de responsabilité unique (qui donne a une class, une fonction) engendra une application difficilement maintenable. Voici les seuls types de logique que l’on devrait trouver dans un contrôleur :

  • Gestion de la session et des cookies, incluant l’authentification, les autorisations.
  • Le ‘requêtage’ : Trouver le ou les objets nécessaires à la consultation de la page. Idéalement, on ne devrait faire appel qu’à une méthode de requêtage dont on mettra le résultat dans une variable d’instance que nous afficherons dans la vue.
  • Gestion des paramètres envoyés par l’utilisateur. Analyser et mettre en forme la requête utilisateur et appelé le model approprié.
  • Afficher ou rediriger : Envoyer le résultat (XML, JSON, HTML…) ou rediriger l’utilisateur.

Ces quelques règles sont la limites de la règle de responsabilité unique et on ne devrait rien trouver d’autres dans le contrôleur.

#2 – Trop de logique dans la vue

Le principe fondamental de “ERB” (ndt: ou tout autre moteur de rendu) est qu’il permet de générer des pages contenant des variables. Mais si vous n’êtes pas suffisamment attentif, vous finirez avec de gros fichiers contenant un mix de HTML et de RUBY impossible à maintenir correctement. Cette zone est également l’endroit ou la règle “Don’t Repeat Yourself” (ndt: lire mon article : 5 questions d’un développeur devrait se poser quand il code ) est la plus transgressée.

Par exemple, l’utilisation abusive de logique dans une vue. Prenons l’exemple du current_user, méthode permettant de récupérer les informations de l’utilisateur connecté, souvent, nous obtiendrons ce type de code :

<h3>
  Welcome,
  <% if current_user %>
    <%= current_user.name %>
  <% else %>
    Guest
  <% end %>
</h3>

Un moyen plus propre d’obtenir ce résultat est d’être certains que l’objet retourné par “current_user” est toujours initialisé, même si aucun utilisateur est connecté. Voici ce que je vous propose :

def current_user
  @current_user ||= User.find session[:user_id] if session[:user_id]
  if @current_user
    @current_user
  else
    OpenStruct.new(name: 'Guest')
  end
end

Ceci vous permettra de remplacer le précédent code par le suivant dans votre vue :

<h3>Welcome, <%= current_user.name -%></h3>

Quelques astuces supplémentaires :

  • Utilisez les layouts et les partials pour regrouper les éléments qui se répètent (Formulaire, carousel…)
  • Utilisez les helper pour y insérer la logique (comme indiqué ci-dessus)

#3 – Trop de logique dans le model

Selon les deux premiers points, il est préférable de réduire la logique dans le controlleur et dans le modèle, donc, selon l’architecture MVC (modèle vue controlleur), il ne reste que le modèle pour y mettre la logique, n’est-ce pas ?

Et bien, pas complétement.

Beaucoup de développeurs font cette erreur et finisse par mettre toute leur logique dans le modèle ActiveRecord conduisant à une application qui transgresse la règle de responsabilité unique mais également qui est un cauchemar à maintenir.

Les fonctionnalités telles qu’envoyer des emails de notifications, interfacer avec des services externes (API), convertir dans d’autres formats et j’en passe, n’ont pas vraiment lieu d’être dans un modèle ActiveRecord qui ne devrait faire guère plus que chercher des éléments en base de données et faire persister les données.

Donc si la la logique ne doit être ni dans la vue, ni dans le controlleur, ni dans le modèle, où doit-elle aller ?

Découvrer le “Plain Old Ruby Domain Object” (lire : Un bon vieux modèle Ruby”). Avec un framework très lisible comme Rails, les nouveaux développeur sont souvent retissant à l’idée de créer leur propre classe en dehors du framework. En plus, déplacer la logique en dehors du modèle dans “POROs” est considéré comme ce qu’un médecin prescrit pour éviter les modèles trop complexe.
Grâce aux POROs, vous pouvez encapsuler les notifications par mail ou les interactions avec des API dans leur propre classe plutôt que de tout mettre dans le modèle ActiveRecord.

Avec ceci en tête, ce qui devrait rester dans un modèle ActiveRecord est :

  • ActiveRecord configuration (relations & validations)
  • Les simples méthodes de mise à jour (mise à jour de datetime, ou de statut…)
  • Les méthodes type “Access wrappers” permettant de masquer des données internes aux modèle (Exemple: fullname qui fusionne firstname et lastname)
  • Les requêtes spécifiques (plus complexes que le simple “find”). Généralement, on ne doit jamais trouver la méthode “Where”, ou toutes autres méthodes de requête (select, join…) en dehors du modèle ActiveRecord

#4 : Utiliser un helper générique comme dépotoir

Cette erreur est une corollaire de la précédente erreur. Comme déjà expliqué, le framework Rails met l’accent sur les principaux composants du framework MVC (modèle, vue, controlleur). Il y a plusieurs bonnes définitions des bonnes choses qui ont leur place dans chacun de ces composants, mais parfois nous pourrions avoir besoin de méthodes qui n’entrent dans aucun de ces trois composants.

Les outils de génération de rails créent de manière systématique un dossier Helper contenant un fichier helper qui s’associera avec la ressource créée. Il devient alors très tentant de remplir ces fichiers de fonctionnalités qui n’ont pas formellement leur place dans le modèle, dans la vue ou dans le controlleur.

Même si Rails est centré sur le modèle MVC, rien ne vous empêche de créer vos propres types de classes et d’ajouter les dossiers appropriés pour contenir le code de ces classes. Lorsque vous avez des fonctionnalités supplémentaires, réfléchissez à grouper vos méthodes ensemble et trouver le nom adéquat pour cette nouvelle classe créée. Utiliser un framework compréhensif comme Rails n’est pas une excuse pour laisser de côté un bon code orienté objet.

#5 : Utiliser trop de GEMs

Ruby on Rails possède une riche bibliothèque de GEM qui fournissent presque toutes les fonctionnalités désirées par un développeur. Très pratique pour construire rapidement des applications complexes, mais trop d’application gavent le gemfile de manière disproportionnée et n’utilise pas complètement les fonctionnalités.

Ceci pose plusieurs problèmes. L’utilisation abusive de GEMs rend l’application plus lourde qu’elle ne devrait l’être pouvant ralentir l’application en production. Qui dit application plus lourde, dit forcément serveur plus puissant et donc l’augmentation des coûts de production d’une application.
Une application gavée de GEMs la rend plus lente à démarrer ralentissant les tests et donc poussant le développeur à en faire moins…

Garder en tête que chaque GEM que vous utilisez ajoute à votre application des dépendances avec d’autres GEMs, qui eux-mêmes peuvent avoir des dépendances et ainsi de suite.
Par exemple, le GEM ‘rails_admin’ emmène avec lui 11 dépendances au total soit 10% de plus que ce qu’une application Ruby On  Rails a besoin pour fonctionner.

(NDT: Un application Ruby On Rails a besoin d’environ 40 GEMs de base pour fonctionner)

Considérer soigneusement si l’ajout de GEM apporte réellement une plus valu à votre application.
Par exemple, les développeurs auront tendances à ajouter ‘rails_admin’ car il apporte une belle interface d’administration basée sur la structure de vos modèles. Mais, il ne fera pas vraiment mieux qu’une simple interface comme PHPMyAdmin. Même si votre application nécessite une gestion d’utilisateurs avec droits d’accès individualisé, vous ne voulez probablement pas qu’ils aient accès à toutes votre base de données. Le développement de votre propre interface est préférable en rapport à l’ajout d’un tel GEM.

#6 – Ignorer vos logs

Bien que les développeurs connaissent l’existence de log en version de développement ou de production, ils ne prêtent généralement pas attention  au contenu de ces fichiers. Même si certains logiciels permettent de mieux gérer les logs en production (Honeybadger ou New relic), il est également important de garder un oeil sur les logs pendant la période de développement et de test.

Comme déjà mentionné, Ruby On Rails fait beaucoup de magie pour vous, spécifiquement dans les models. Définir les relations dans les models rend la récupération des données beaucoup plus facile dans la vue. Toutes les requêtes SQL nécessaires sont générées par votre application. C’est coo. MAIS comment savoir si les requêtes générées sont efficaces ?

Un exemple rencontré couramment est la “Requête N+1“. On comprend facilement le problème, mais la seule manière de l’observer est de regarder les requêtes SQL générées dans les logs.

Admettons que vous ayez ce bout de code typique des blogs. Vous souhaitez, simplement, afficher les commentaires d’un article :

def comments_for_top_three_posts
  posts = Post.limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

En regardant de plus près les logs générés par cette fonction, nous pouvons observer que pour chaque article, une requête est générée pour récupérer les commentaires :

Started GET "/posts/some_comments" for 127.0.0.1 at 2016-01-20 20:05:13 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.4ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (5.6ms)  ELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 1]]
  Comment Load (0.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 2]]
  Comment Load (1.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 3]]
  Rendered posts/some_comments.html.erb within layouts/application (12.5ms)
Completed 200 OK in 581ms (Views: 225.8ms | ActiveRecord: 10.0ms)

l'”eager loading” d’ActiveRecord rend possible de réduire significativement réduire le nombre de requêtes en vous laissant spécifié par avance toutes les associations qui seront chargées. Il suffit simplement d’appeler les méthodes “includes” (ou “preload”) fourni par “ActiveRevord::Relation”. Avec ces méthodes, “ActiveRecord” s’assure que toutes les associations demandées sont chargées en utilisant un nombre de requête minimale.

def comments_for_top_three_posts
  posts = Post.includes(:comments).limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

Avec cette modification, nous pouvons observer dans les logs qu’une seule requête permet de récupérer les commentaires :

Started GET "/posts/some_comments" for 127.0.0.1 at 2016-01-20 20:05:18 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.5ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (4.4ms)  SELECT "comments".* FROM "comments" WHERE"comments "."post_id" IN (1, 2, 3)
  Rendered posts/some_comments.html.erb within layouts/application (12.2ms)
Completed 200 OK in 560ms (Views: 219.3ms | ActiveRecord: 5.0ms)

Plus efficace non ?

Cette solution au “problème N+1” n’est qu’un exemple de code inefficace qui peut exister dans nos application si nous ne faisons pas attention à nos logs. En période de développement, vous devriez observer plus minutieusement vos logs pour trouver (et modifier) le code inefficace.

Consulter les logs est un bon moyen de venir à bout du code inefficace avant que votre application n’aille en production. Autrement, vous ne vous rendrez compte des problèmes de performance qu’une fois en production puisque les données en production sont généralement plus nombreuse que celles en développement. Si vous travaillez sur une nouvelle application, même votre base de données de production semblera performante au début étant donné le nombre de données présentes. Mais le nombre de données grandissant, les problèmes non traités rendront votre application de plus en plus lente.

Par ailleurs, si vous trouvez que vos logs sont pollués par des informations dont vous n’avez pas besoin, voici une solution que vous pouvez utiliser pour nettoyer vos logs (utile en production comme en développement)

 

#7 – Manque de tests automatiques

Ruby on Rails fourni, par défault, un très bon outils de tests automatisés. Beaucoup de développeur Ruby On Rails écrivent beaucoup de tests sophistiqués en utilisant le “TDD” (ndt: Test Driven Development, pour Développement conduit par les tests) et le “BDD” (ndt: Business Driven Development, pour Développement conduit par le client, ex: SCRUM) et utilise de puissant frameworks de test utilisant des GEMS comme RSPEC ou CUCUMBER.

Bien qu’il soit très facile d’ajouter des tests automatisés à votre application Rails, nous pouvons être malheureusement surpris par le nombre de projets où les tests sont inexistants (ou presque). Alors que de nombreux débats existent sur la nécessité d’avoir des tests compréhensifs, il est clair qu’il faut un minimum de tests automatiques pour chaque application.

Une règle simple, pour chaque action de votre controller, il devrait y avoir au moins un test écrit. Un jour ou l’autre, un autre développeur souhaitera amélioré, modifié, upgradé votre code et ce test écrit lui permettra de vérifier que l’application reste fonctionnelle malgré tout. De plus, les tests permettront aux futurs développeurs de cette application de bien comprendre l’ensemble des fonctionnalités demandées par l’application.

#8 – Rester bloqué par des services extérieurs

L’utilisation de service Rails est rendue facile par l’intégration de ‘GEM‘ englobant leur API (Google Maps…). Mais que se passe-t-il quand un service externe est hors service ou devient très lent ?

Pour éviter d’être bloqué par ces appels, plutôt que d’appeler ces services directement dans votre application Rails pendant le traitement d’une requête, il est préférable de déplacer leurs utilisations dans des services de ‘fil d’attente’ retardée. Les ‘GEMS’ les plus populaire pour ce cas sont :

Pour le cas où il est impossible de retarder une requête dans l’un de ces services, vous devrez vous assurer que votre application gère les cas d’erreurs lorsque les services extérieures sont hors service ou trop lent à traiter votre requête. Vous devriez également tester votre application sans ces services extérieurs (par exemple, déconnectez vous d’internet tout en continuant d’utiliser votre application) pour vérifier qu’aucune situation bloquante n’intervient.

 #9 – Rester marié aux migrations existantes

Le mecanisme de migration de base de données de Rails permet de créer plusieurs instructions pour automatique ajouter ou supprimer des tables ou des colonnes de votre base de données. Depuis que ces fichiers sont nommés de manière séquentielle (du plus ancien au plus récent), il est possible de les utiliser depuis le début pour obtenir une base de données vide ayant le même schémas que la base de données de production. C’est aussi un excellent moyen de gérer les changements de votre base de données pour éviter les problèmes avec Rails.

Ceci fonctionnant parfaitement au début d’un projet, le processus de création peut prendre plus de temps, parfois, les migrations sont mal placées, insérées au mauvaise endroit ou introduites depuis d’autres applications rails utilisant la même base de données.

Rails fournit une représentation de votre schéma de base de données dans un fichier ‘db/schema.rb‘ qui est mis à jour à chaque migration. Ce fichier peut également être mis à jour sans migration grâce à la commande ‘rake dv:shema:dump‘.

Lorsque les migrations sont devenues ingérables ou trop longues à créer ou créent un mauvais schémas de base de données, les développeurs ne devraient pas avoir peut de nettoyer les vieilles migrations, créer un nouveau schémas et recommencer depuis ce point. Créer un nouvel environnement de développement requerra la requête ‘rake db:schema:load‘ plutôt que ‘rake db:migrate‘.

Par ailleurs, cette erreur fait l’objet d’un article dans les guides de Ruby On Rails.

#10 – Vérifier des données sensibles dans les fichiers sources

Le framework Ruby On Rails rend facile la création d’applications sécurisées de différents types d’attaques. Par exemple, Rails créé un “secret token” pour sécuriser la session avec un navigateur. Même si ce ‘token’ est stocké dans un fichier (‘config/secrets.yml’), et que ce fichier est lu selon une variable d’environnement (production, staging…), les versions précédents de Rails créaient ce ‘token’ dans ce fichier : ‘config/initializers/secret_token.rb’. Ce fichier fait parti intégrante de votre code source et n’importe qui ayant accès à vos sources (Github…) peut compromettre tous les utilisateurs de votre application.

Soyez certain que ces fichiers sont exclus de votre répertoire de configuration (.gitignore pour les utilisateurs de GIT). Votre serveur pourra récupérer ces données via des variables d’environnement ou depuis un autre mecanisme comme celui proposé par le ‘GEM’ dotenv.

Conclusion

Rails est un framworks puissant qui cache beaucoup de détails moches nécessaires pour développer de bonnes application web. Depuis que ces outils rendent le développement d’application plus rapide, les développeurs doivent faire attention aux erreurs fréquentes pour être certain que leurs applications sont facilement maintenable et extensible.

Les développeurs doivent également prendre conscience que ces erreurs peuvent ralentir leurs applications et les rendre vulnérables. Etudier ce frameworks est primordiale pour en comprendre son architecture et sa conception.

Note du traducteur

Si vous souhaitez faire appel à un développeur freelance ruby on rails expérimenté, contactez moi.

Soyez Sociable ! Partagez !
Published inRubyOnRails
  • hello
    article très très intéressant !

    merci pour la traduction de l’originale (et pour la découverte par la même occasion)

    pour ma part j’aime bien ajouter des librairies maison dans le répertoire lib pour justement gérer certains thèmes sans alourdir les models

    et bien vu pour l’optimisation du requetage et les vérifs dans le log, on a trop tendance à ouvrir les logs d’appli rails uniquement lorsque ca crash 😀

banner