Code Buggy Rails : les 10 erreurs les plus courantes commises par les développeurs de rails

Publié: 2022-03-11

Ruby on Rails («Rails») est un framework open source populaire, basé sur le langage de programmation Ruby qui s'efforce de simplifier et de rationaliser le processus de développement d'applications Web.

Rails est construit sur le principe de la convention sur la configuration. En termes simples, cela signifie que, par défaut, Rails suppose que ses développeurs experts suivront les conventions "standard" des meilleures pratiques (pour des choses comme le nommage, la structure du code, etc.) et, si vous le faites, les choses fonctionneront pour vous "auto -magiquement" sans que vous ayez besoin de préciser ces détails. Si ce paradigme a ses avantages, il n'est pas non plus sans écueils. Plus particulièrement, la « magie » qui se produit dans les coulisses du cadre peut parfois conduire à des faux-semblants, à la confusion et à « qu'est-ce qui se passe ? » types de problèmes. Il peut également avoir des ramifications indésirables en matière de sécurité et de performances.

Par conséquent, bien que Rails soit facile à utiliser, il n'est pas difficile d'en faire un mauvais usage. Ce didacticiel examine 10 problèmes Rails courants, y compris comment les éviter et les problèmes qu'ils causent.

Erreur courante n° 1 : Mettre trop de logique dans le contrôleur

Rails est basé sur une architecture MVC. Dans la communauté Rails, nous parlons depuis un moment de fat model, skinny controller, pourtant plusieurs applications Rails récentes dont j'ai hérité ont violé ce principe. Il est trop facile de déplacer la logique de vue (qui est mieux hébergée dans un assistant), ou la logique de domaine/modèle, dans le contrôleur.

Le problème est que l'objet contrôleur commencera à violer le principe de responsabilité unique, rendant les modifications futures de la base de code difficiles et sujettes aux erreurs. Généralement, les seuls types de logique que vous devriez avoir dans votre contrôleur sont :

  • Gestion des sessions et des cookies. Cela peut également inclure l'authentification/l'autorisation ou tout traitement de cookie supplémentaire que vous devez effectuer.
  • Sélection du modèle. Logique pour trouver le bon objet de modèle compte tenu des paramètres transmis à partir de la requête. Idéalement, cela devrait être un appel à une méthode de recherche unique définissant une variable d'instance à utiliser ultérieurement pour rendre la réponse.
  • Gestion des paramètres de demande. Collecte des paramètres de requête et appel d'une méthode de modèle appropriée pour les rendre persistants.
  • Rendu/redirection. Rendu du résultat (html, xml, json, etc.) ou redirection, selon le cas.

Bien que cela repousse encore les limites du principe de responsabilité unique, c'est en quelque sorte le strict minimum que le framework Rails nous demande d'avoir dans le contrôleur.

Erreur courante n° 2 : mettre trop de logique dans la vue

Le moteur de template Rails prêt à l'emploi, ERB, est un excellent moyen de créer des pages avec un contenu variable. Cependant, si vous ne faites pas attention, vous pouvez bientôt vous retrouver avec un gros fichier qui est un mélange de code HTML et Ruby qui peut être difficile à gérer et à maintenir. C'est aussi un domaine qui peut conduire à de nombreuses répétitions, conduisant à des violations des principes DRY (ne vous répétez pas).

Cela peut se manifester de plusieurs façons. L'un est l'utilisation excessive de la logique conditionnelle dans les vues. À titre d'exemple simple, considérons un cas où nous avons une méthode current_user disponible qui renvoie l'utilisateur actuellement connecté. Souvent, il y aura des structures logiques conditionnelles comme celle-ci dans les fichiers de vue :

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

Une meilleure façon de gérer quelque chose comme ça est de s'assurer que l'objet renvoyé par current_user est toujours défini, que quelqu'un soit connecté ou non, et qu'il réponde aux méthodes utilisées dans la vue de manière raisonnable (parfois appelé null objet). Par exemple, vous pouvez définir l'assistant current_user dans app/controllers/application_controller comme ceci :

 require 'ostruct' helper_method :current_user 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

Cela vous permettrait alors de remplacer l'exemple de code de vue précédent par cette simple ligne de code :

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

Quelques bonnes pratiques Rails supplémentaires recommandées :

  • Utilisez les dispositions de vue et les partiels de manière appropriée pour encapsuler les éléments qui se répètent sur vos pages.
  • Utilisez des présentateurs/décorateurs comme la gemme Draper pour encapsuler la logique de création de vue dans un objet Ruby. Vous pouvez ensuite ajouter des méthodes dans cet objet pour effectuer des opérations logiques que vous auriez autrement pu mettre dans votre code de vue.

Erreur courante n° 3 : mettre trop de logique dans le modèle

Étant donné les conseils pour minimiser la logique dans les vues et les contrôleurs, le seul endroit restant dans une architecture MVC pour mettre toute cette logique serait dans les modèles, n'est-ce pas ?

Eh bien, pas tout à fait.

De nombreux développeurs Rails commettent cette erreur et finissent par tout coller dans leurs classes de modèles ActiveRecord , ce qui conduit à des fichiers mongo qui non seulement violent le principe de responsabilité unique, mais sont également un cauchemar de maintenance.

Des fonctionnalités telles que la génération de notifications par e-mail, l'interfaçage avec des services externes, la conversion vers d'autres formats de données, etc., n'ont pas grand-chose à voir avec la responsabilité principale d'un modèle ActiveRecord qui ne devrait guère faire plus que rechercher et conserver des données dans une base de données.

Donc, si la logique ne doit pas aller dans les vues, ni dans les contrôleurs, ni dans les modèles, eh bien, où doit-elle aller ?

Entrez les anciens objets Ruby simples (PORO). Avec un framework complet comme Rails, les nouveaux développeurs sont souvent réticents à créer leurs propres classes en dehors du framework. Cependant, déplacer la logique du modèle vers les PORO est souvent exactement ce que le médecin a ordonné pour éviter les modèles trop complexes. Avec les PORO, vous pouvez encapsuler des éléments tels que les notifications par e-mail ou les interactions API dans leurs propres classes plutôt que de les coller dans un modèle ActiveRecord .

Donc, avec cela à l'esprit, d'une manière générale, la seule logique qui devrait rester dans votre modèle est :

  • Configuration d' ActiveRecord (c.-à-d. relations et validations)
  • Méthodes de mutation simples pour encapsuler la mise à jour d'une poignée d'attributs et les enregistrer dans la base de données
  • Accéder aux wrappers pour masquer les informations du modèle interne (par exemple, une méthode full_name qui combine les champs first_name et last_name dans la base de données)
  • Requêtes sophistiquées (c'est-à-dire plus complexes qu'une simple find ) ; d'une manière générale, vous ne devriez jamais utiliser la méthode where , ou toute autre méthode de construction de requête similaire, en dehors de la classe de modèle elle-même
En relation: 8 questions d'entretien essentielles sur Ruby on Rails

Erreur courante n° 4 : Utiliser des classes d'assistance génériques comme dépotoir

Cette erreur est en quelque sorte un corollaire de l'erreur n ° 3 ci-dessus. Comme indiqué, le framework Rails met l'accent sur les composants nommés (c'est-à-dire le modèle, la vue et le contrôleur) d'un framework MVC. Il existe d'assez bonnes définitions des types de choses qui appartiennent aux classes de chacun de ces composants, mais parfois nous pourrions avoir besoin de méthodes qui ne semblent correspondre à aucun des trois.

Les générateurs Rails créent de manière pratique un répertoire d'assistance et une nouvelle classe d'assistance pour accompagner chaque nouvelle ressource que nous créons. Il devient cependant trop tentant de commencer à intégrer toute fonctionnalité qui ne correspond pas formellement au modèle, à la vue ou au contrôleur dans ces classes d'assistance.

Bien que Rails soit certainement centré sur MVC, rien ne vous empêche de créer vos propres types de classes et d'ajouter des répertoires appropriés pour contenir le code de ces classes. Lorsque vous disposez de fonctionnalités supplémentaires, réfléchissez aux méthodes qui se regroupent et trouvez de bons noms pour les classes qui contiennent ces méthodes. L'utilisation d'un cadre complet comme Rails n'est pas une excuse pour laisser de côté les bonnes pratiques de conception orientée objet.

Erreur courante #5 : Utiliser trop de gemmes

Ruby et Rails sont pris en charge par un riche écosystème de gemmes qui fournissent collectivement à peu près toutes les capacités auxquelles un développeur peut penser. C'est idéal pour créer rapidement une application complexe, mais j'ai également vu de nombreuses applications gonflées où le nombre de gemmes dans le Gemfile de l'application est disproportionné par rapport aux fonctionnalités fournies.

Cela provoque plusieurs problèmes de Rails. L'utilisation excessive de gems rend la taille d'un processus Rails plus grande que nécessaire. Cela peut ralentir les performances en production. En plus de la frustration des utilisateurs, cela peut également entraîner le besoin de configurations de mémoire de serveur plus importantes et des coûts d'exploitation accrus. Il faut également plus de temps pour démarrer des applications Rails plus volumineuses, ce qui ralentit le développement et allonge la durée des tests automatisés (et en règle générale, les tests lents ne sont tout simplement pas exécutés aussi souvent).

Gardez à l'esprit que chaque gemme que vous apportez dans votre application peut à son tour avoir des dépendances sur d'autres gemmes, et celles-ci peuvent à leur tour avoir des dépendances sur d'autres gemmes, et ainsi de suite. L'ajout d'autres gemmes peut donc avoir un effet cumulatif. Par exemple, l'ajout de la gemme rails_admin apportera 11 gemmes supplémentaires au total, soit une augmentation de 10 % par rapport à l'installation de base de Rails.

Au moment d'écrire ces lignes, une nouvelle installation de Rails 4.1.0 inclut 43 gemmes dans le fichier Gemfile.lock . C'est évidemment plus que ce qui est inclus dans Gemfile et représente toutes les gemmes que la poignée de gemmes Rails standard apportent en tant que dépendances.

Considérez soigneusement si les frais généraux supplémentaires en valent la peine lorsque vous ajoutez chaque gemme. Par exemple, les développeurs ajouteront souvent avec désinvolture la gemme rails_admin car elle fournit essentiellement une belle interface Web à la structure du modèle, mais ce n'est vraiment pas beaucoup plus qu'un outil de navigation de base de données sophistiqué. Même si votre application nécessite des utilisateurs administrateurs avec des privilèges supplémentaires, vous ne voulez probablement pas leur donner un accès brut à la base de données et vous seriez mieux servi en développant votre propre fonction d'administration plus simple qu'en ajoutant ce joyau.

Erreur courante #6 : Ignorer vos fichiers journaux

Bien que la plupart des développeurs Rails connaissent les fichiers journaux par défaut disponibles pendant le développement et en production, ils ne prêtent souvent pas suffisamment attention aux informations contenues dans ces fichiers. Alors que de nombreuses applications s'appuient sur des outils de surveillance des journaux comme Honeybadger ou New Relic en production, il est également important de garder un œil sur vos fichiers journaux tout au long du processus de développement et de test de votre application.

Comme mentionné précédemment dans ce tutoriel, le framework Rails fait beaucoup de « magie » pour vous, en particulier dans les modèles. La définition d'associations dans vos modèles facilite l'insertion de relations et permet d'avoir tout à disposition de vos vues. Tout le SQL nécessaire pour remplir vos objets de modèle est généré pour vous. C'est génial. Mais comment savez-vous que le SQL généré est efficace ?

Un exemple que vous rencontrerez souvent est appelé le problème de requête N+1. Bien que le problème soit bien compris, la seule véritable façon de l'observer consiste à examiner les requêtes SQL dans vos fichiers journaux.

Supposons, par exemple, que vous ayez la requête suivante dans une application de blog typique où vous afficherez tous les commentaires pour un ensemble sélectionné de publications :

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

Lorsque nous examinons le fichier journal d'une requête qui appelle cette méthode, nous voyons quelque chose comme ce qui suit, où une seule requête est effectuée pour obtenir les trois objets post, puis trois autres requêtes sont effectuées pour obtenir chacun des commentaires de ces objets :

 Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-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)

La capacité de chargement rapide d' ActiveRecord dans Rails permet de réduire considérablement le nombre de requêtes en vous permettant de spécifier à l'avance toutes les associations qui vont être chargées. Cela se fait en appelant la méthode includes (ou preload ) sur l'objet Arel ( ActiveRecord::Relation ) en cours de construction. Avec includes , ActiveRecord garantit que toutes les associations spécifiées sont chargées en utilisant le nombre minimum possible de requêtes ; par exemple:

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

Lorsque le code révisé ci-dessus est exécuté, nous voyons dans le fichier journal que tous les commentaires ont été collectés dans une seule requête au lieu de trois :

 Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-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)

Beaucoup plus efficace.

Cette solution au problème N+1 n'est vraiment destinée qu'à servir d'exemple du type d'inefficacités qui peuvent exister « sous le capot » dans votre application si vous n'y prêtez pas suffisamment attention. Le point à retenir ici est que vous devriez vérifier vos fichiers journaux de développement et de test pendant le développement pour vérifier (et corriger !) les inefficacités dans le code qui construit vos réponses.

L'examen des fichiers journaux est un excellent moyen d'être informé des inefficacités de votre code et de les corriger avant que votre application ne passe en production. Sinon, vous ne serez peut-être pas au courant d'un problème de performances Rails résultant jusqu'à ce que votre système soit mis en ligne, car l'ensemble de données avec lequel vous travaillez en développement et en test est susceptible d'être beaucoup plus petit qu'en production. Si vous travaillez sur une nouvelle application, même votre ensemble de données de production peut commencer petit et votre application semblera fonctionner correctement. Cependant, à mesure que votre jeu de données de production grandit, des problèmes Rails comme celui-ci entraîneront une exécution de plus en plus lente de votre application.

Si vous constatez que vos fichiers journaux sont obstrués par un tas d'informations dont vous n'avez pas besoin, voici certaines choses que vous pouvez faire pour les nettoyer (les techniques fonctionnent pour le développement ainsi que pour les journaux de production).

Erreur courante #7 : Manque de tests automatisés

Ruby et Rails fournissent par défaut de puissantes capacités de test automatisé. De nombreux développeurs Rails écrivent des tests très sophistiqués en utilisant les styles TDD et BDD et utilisent des frameworks de test encore plus puissants avec des gemmes comme rspec et cucumber.

Malgré la facilité avec laquelle il est facile d'ajouter des tests automatisés à votre application Rails, j'ai été très désagréablement surpris par le nombre de projets dont j'ai hérité ou rejoint où il n'y avait littéralement aucun test écrit (ou au mieux, très peu) par le précédent équipe de développement. Bien qu'il y ait beaucoup de débats sur l'exhaustivité de vos tests, il est assez clair qu'au moins certains tests automatisés devraient exister pour chaque application.

En règle générale, il devrait y avoir au moins un test d'intégration de haut niveau écrit pour chaque action dans vos contrôleurs. À un moment donné dans le futur, d'autres développeurs Rails voudront très probablement étendre ou modifier le code, ou mettre à niveau une version Ruby ou Rails, et ce cadre de test leur fournira un moyen clair de vérifier que la fonctionnalité de base de l'application est travaillant. Un avantage supplémentaire de cette approche est qu'elle fournit aux futurs développeurs une délimitation claire de la collection complète de fonctionnalités fournies par l'application.

Erreur courante n° 8 : bloquer les appels vers des services externes

Les fournisseurs tiers de services Rails facilitent généralement l'intégration de leurs services dans votre application via des gemmes qui enveloppent leurs API. Mais que se passe-t-il si votre service externe tombe en panne ou commence à fonctionner très lentement ?

Pour éviter de bloquer ces appels, plutôt que d'appeler ces services directement dans votre application Rails pendant le traitement normal d'une requête, vous devez les déplacer vers une sorte de service de mise en file d'attente des tâches en arrière-plan lorsque cela est possible. Certaines gemmes populaires utilisées dans les applications Rails à cette fin incluent :

  • Travail retardé
  • Resqué
  • Sidekiq

Dans les cas où il est impossible ou impossible de déléguer le traitement à une file d'attente de tâches en arrière-plan, vous devrez vous assurer que votre application dispose de suffisamment de dispositions de gestion des erreurs et de basculement pour les situations inévitables où le service externe tombe en panne ou rencontre des problèmes. . Vous devez également tester votre application sans le service externe (peut-être en supprimant du réseau le serveur sur lequel se trouve votre application) pour vérifier qu'elle n'entraîne pas de conséquences imprévues.

Erreur courante n° 9 : se marier avec les migrations de bases de données existantes

Le mécanisme de migration de base de données de Rails vous permet de créer des instructions pour ajouter et supprimer automatiquement des tables et des lignes de base de données. Étant donné que les fichiers contenant ces migrations sont nommés de manière séquentielle, vous pouvez les lire depuis le début des temps pour amener une base de données vide sur le même schéma que la production. C'est donc un excellent moyen de gérer les modifications granulaires du schéma de base de données de votre application et d'éviter les problèmes Rails.

Bien que cela fonctionne certainement bien au début de votre projet, au fil du temps, le processus de création de la base de données peut prendre un certain temps et parfois les migrations sont égarées, insérées dans le désordre ou introduites à partir d'autres applications Rails utilisant le même serveur de base de données.

Rails crée une représentation de votre schéma actuel dans un fichier appelé db/schema.rb (par défaut) qui est généralement mis à jour lors de l'exécution des migrations de base de données. Le fichier schema.rb peut même être généré lorsqu'aucune migration n'est présente en exécutant la tâche rake db:schema:dump . Une erreur courante de Rails consiste à vérifier une nouvelle migration dans votre référentiel source mais pas le fichier schema.rb mis à jour en conséquence.

Lorsque les migrations sont devenues incontrôlables et prennent trop de temps à s'exécuter, ou ne créent plus la base de données correctement, les développeurs ne doivent pas avoir peur d'effacer l'ancien répertoire de migrations, de vider un nouveau schéma et de continuer à partir de là. La configuration d'un nouvel environnement de développement nécessiterait alors un rake db:schema:load plutôt que le rake db:migrate sur lequel s'appuient la plupart des développeurs.

Certaines de ces questions sont également abordées dans le Rails Guide.

Erreur courante n° 10 : vérifier les informations sensibles dans les référentiels de code source

Le framework Rails facilite la création d'applications sécurisées insensibles à de nombreux types d'attaques. Une partie de cela est accomplie en utilisant un jeton secret pour sécuriser une session avec un navigateur. Même si ce jeton est maintenant stocké dans config/secrets.yml et que ce fichier lit le jeton à partir d'une variable d'environnement pour les serveurs de production, les versions antérieures de Rails incluaient le jeton dans config/initializers/secret_token.rb . Ce fichier est souvent archivé par erreur dans le référentiel de code source avec le reste de votre application et, lorsque cela se produit, toute personne ayant accès au référentiel peut désormais facilement compromettre tous les utilisateurs de votre application .

Vous devez donc vous assurer que votre fichier de configuration de référentiel (par exemple, .gitignore pour les utilisateurs de git) exclut le fichier avec votre jeton. Vos serveurs de production peuvent alors récupérer leur jeton à partir d'une variable d'environnement ou d'un mécanisme comme celui fourni par le gem dotenv.

Résumé du didacticiel

Rails est un cadre puissant qui cache de nombreux détails laids nécessaires pour créer une application Web robuste. Bien que cela rende le développement d'applications Web Rails beaucoup plus rapide, les développeurs doivent faire attention aux erreurs potentielles de conception et de codage, pour s'assurer que leurs applications sont facilement extensibles et maintenables au fur et à mesure de leur croissance.

Les développeurs doivent également être conscients des problèmes qui peuvent rendre leurs applications plus lentes, moins fiables et moins sécurisées. Il est important d'étudier le cadre et de s'assurer que vous comprenez parfaitement les compromis d'architecture, de conception et de codage que vous faites tout au long du processus de développement, afin de garantir une application de haute qualité et hautes performances.

En relation : Quels sont les avantages de Ruby on Rails ? Après deux décennies de programmation, j'utilise Rails