Ruby est un langage à typage dynamique. Vous ne pouvez pas imposer de types à vos signatures de méthodes, vos variables locales, ni à quoi que ce soit d'autre. Et oui, c'est précisément ce que des outils comme Sorbet et RBS cherchent à résoudre — mais laissez-moi vous convaincre avant d'y avoir recours.

J'écris du Rails en production depuis un moment, et j'en suis venu à croire que de bonnes conventions de nommage peuvent couvrir 80 % du chemin. Pas de gems, pas d'annotations de types, pas d'étape de build. Juste de la rigueur.

Convention plutôt que configuration (même pour les noms de variables)

L'une des choses que j'aime le plus dans Rails, c'est sa philosophie de « convention plutôt que configuration ». Nous nous appuyons déjà sur le nommage pour communiquer l'intention partout : noms de modèles, noms de tables, helpers de routes. Pourquoi ne pas appliquer la même rigueur à nos variables ?

Considérez une méthode comme celle-ci :

def invite_to_party(user)
  # ...
end

Personne qui lit ce code ne s'attend à ce que user soit une String ou un Integer. Tout le monde comprend que c'est une instance de User. C'est presque trop simple à mentionner, mais réfléchissez-y : cette minuscule convention fait déjà le travail d'une annotation de type. Aucun outillage requis, juste une compréhension partagée intégrée dans le nom.

Le suffixe _h pour les hashes

C'est là que les choses deviennent intéressantes : quand vous commencez à manipuler des données brutes. Disons que vous récupérez des attributs utilisateur depuis une API JSON externe :

user_h = Faraday.get("https://api.mypartner.com/api/v1/users/17")
# => { "first_name" => "John", "last_name" => "Doe" }

Ce petit suffixe _h change tout. Désormais, partout où user_h est passé dans votre codebase, chaque développeur qui le touche sait deux choses d'un coup d'œil : il contient des données utilisateur, et c'est un Hash — pas une instance du modèle User. Aucune confusion, plus besoin de remonter la variable jusqu'à son origine.

Vous pouvez étendre ce pattern à d'autres types : user_ids pour un tableau d'identifiants, user_json pour une chaîne JSON brute, etc. L'idée est d'encoder le type dans le nom pour que le code se documente lui-même.

Nommer les clés étrangères avec intention

Cette approche brille particulièrement avec les associations ActiveRecord. Prenez la relation classique livre/auteur :

class Book < ApplicationRecord
  belongs_to :author, foreign_key: :author_id
end

class Author < ApplicationRecord
  has_many :books
end

Simple. Mais imaginons maintenant que l'auteur soit en réalité un enregistrement dans la table users. C'est là que beaucoup de codebases commencent à devenir confuses — vous voyez author_id et vous n'avez aucune idée qu'il pointe vers un User. Mais si vous l'écrivez de façon un peu plus explicite :

class Book < ApplicationRecord
  belongs_to :author_user, foreign_key: :author_user_id, class_name: "User"
end

En intégrant le nom de la classe dans l'association et la clé étrangère, vous ne laissez aucune place au doute. Votre futur vous-même — et chaque développeur qui héritera de ce code — sait immédiatement qu'author_user_id référence la table users.

Le nommage comme système de types

Toutes les quelques années, la même conversation refait surface : « Ruby a besoin du typage statique. Regardez ce que TypeScript a fait pour JavaScript — ça sauvera Ruby aussi. » Et bien sûr, TypeScript a changé la donne pour JS. Mais Ruby n'est pas JavaScript.

Le typage statique a un coût — en verbosité, en surcharge d'outillage, en friction avec le langage au lieu de travailler avec lui. Et ce coût est nettement plus difficile à justifier en Ruby, où le duck typing et la métaprogrammation ne sont pas des cas particuliers, mais des principes de conception fondamentaux. Annoter des types par-dessus un langage conçu pour être fluide et expressif revient souvent à aller à contre-courant.

Les bonnes conventions de nommage ne détecteront pas tous les bugs qu'un vérificateur de types attraperait. Mais elles sont gratuites, sans friction, et fonctionnent avec Ruby plutôt que d'essayer d'en faire quelque chose qu'il n'est pas.