01 - Nommage : la competence la plus sous-estimee
Ce que tu vas apprendre
- Pourquoi le nommage est si difficile (et si important)
- Les conventions pour les booleens, fonctions, classes et fichiers
- Pourquoi les abreviations sont toxiques
- Comment le renommage est un outil de refactoring a part entière
Prerequisites
Phil Karlton, ingenieur chez Netscape, a dit un jour : "There are only two hard things in Computer Science: cache invalidation and naming things." Cette citation a 30 ans. Elle est toujours vraie.
La semaine dernière, j'ai ouvert un fichier dans un projet que j'avais écrit moi-meme il y a huit mois. J'ai trouve ca :
typescriptconst d = getD(u, p);
if (d > 0) {
await proc(d, u);
}
Je n'avais aucune idee de ce que ce code faisait. Pas la moindre. J'ai du remonter 200 lignes pour comprendre que d etait une dette, u un utilisateur, et proc un traitement de paiement. Huit mois. Mon propre code.
Le nommage n'est pas cosmétique. C'est de la communication.
Les booleens : `is`, `has`, `can`, `should`
Un booleen represente une question oui/non. Son nom doit etre cette question.
typescript// Mauvais
const open = true;
const admin = false;
const data = true;
// Bon
const isOpen = true;
const isAdmin = false;
const hasData = true;
Les prefixes is, has, can, should rendent le code lisible comme de l'anglais :
typescriptif (user.isActive && user.hasSubscription) {
// se lit naturellement
}
if (user.canPublish && !user.shouldModerate) {
// on comprend immediatement
}
Pour les fonctions qui retournent un booleen, meme regle :
typescript// Mauvais
function check(email: string): boolean { ... }
function validate(user: User): boolean { ... }
// Bon
function isValidEmail(email: string): boolean { ... }
function hasPermission(user: User, action: string): boolean { ... }
Les fonctions : des verbes d'action
Une fonction fait quelque chose. Son nom doit etre un verbe.
typescript// Mauvais - noms ambigus
function userData(id: string) { ... }
function process() { ... }
function manager() { ... }
// Bon - verbes explicites
function fetchUserById(id: string) { ... }
function processPayment() { ... }
function createOrderManager() { ... }
Le verbe dit ce que la fonction fait. Quelques conventions courantes :
| Prefixe | Signification | Exemple |
|---|---|---|
get / fetch |
Récupérer une donnee | getUserProfile() |
set / update |
Modifier une donnee | updateEmail() |
create / build |
Creer quelque chose | createInvoice() |
delete / remove |
Supprimer | removeExpiredTokens() |
is / has / can |
Tester une condition | isExpired() |
parse / format |
Transformer | formatCurrency() |
validate / check |
Verifier | validateInput() |
handle / on |
Reagir a un événement | handleSubmit() |
Evite les verbes génériques comme process, handle, manage, do. Ils ne disent rien. "Process" quoi ? "Manage" comment ? Si tu ne trouves pas de verbe precis, c'est souvent un signe que ta fonction fait trop de choses. On en parle dans l'article sur les fonctions.
Les classes et les types : des noms, pas des verbes
typescript// Mauvais
class ManageUsers { ... }
class DoPayment { ... }
// Bon
class UserRepository { ... }
class PaymentProcessor { ... }
class InvoiceGenerator { ... }
Les suffixes courants pour les classes :
Service: logique métier (OrderService)Repository: acces aux donnees (UserRepository)Controller: point d'entree HTTP (ProductController)Factory: création d'objets (ConnectionFactory)Validator: validation (EmailValidator)
Un type ou une interface decrit une forme. Son nom doit etre un nom commun :
typescript// Bon
type User = { id: string; email: string; name: string };
type OrderStatus = "pending" | "confirmed" | "shipped";
interface PaymentGateway { charge(amount: number): Promise<Receipt>; }
Les fichiers et dossiers : coherence avant tout
La convention la plus repandue en TypeScript/JavaScript :
- Fichiers :
kebab-case(user-repository.ts,payment-service.ts) - Classes :
PascalCase(UserRepository,PaymentService) - Le fichier porte le nom de son export principal
src/
services/
user-service.ts // exporte UserService
payment-service.ts // exporte PaymentService
repositories/
user-repository.ts // exporte UserRepository
types/
order.ts // exporte Order, OrderStatus
utils/
format-currency.ts // exporte formatCurrency
L'important, c'est la coherence. Si ton projet utilise camelCase pour les fichiers, reste en camelCase. Un projet avec trois conventions différentes est pire qu'un projet avec une mauvaise convention.
Les abreviations : ne le fais pas
typescript// Mauvais
const usrMgr = new UserManager();
const evt = getEvent();
const btn = document.getElementById("submit");
const ctx = createContext();
const res = await fetch(url);
const err = validate(input);
// Bon
const userManager = new UserManager();
const event = getEvent();
const submitButton = document.getElementById("submit");
const context = createContext();
const response = await fetch(url);
const validationError = validate(input);
Les seules abreviations acceptables sont celles que tout le monde connaît : id, url, html, css, api, db. Si tu dois te demander ce que l'abreviation signifie, elle n'est pas assez connue.
Exception : les variables de boucle (i, j, k) et les callbacks courts (arr.map(x => x.id)). Le scope est tellement petit que le contexte suffit.
Les constantes : en majuscules si globales
typescript// Constantes de configuration
const MAX_RETRY_COUNT = 3;
const DEFAULT_PAGE_SIZE = 20;
const API_BASE_URL = "https://api.example.com";
// Constantes locales - pas besoin de majuscules
const taxRate = 0.2;
const discountThreshold = 100;
La convention SCREAMING_SNAKE_CASE est reservee aux constantes globales ou de configuration. Pour une constante locale dans une fonction, le camelCase suffit. Tout mettre en majuscules noie le signal dans le bruit.
Le renommage comme outil de refactoring
Renommer une variable ou une fonction est le refactoring le plus simple et le plus impactant. Ton IDE sait le faire en toute sécurité avec "Rename Symbol" (F2 dans VS Code). Pas besoin de chercher-remplacer a la main.
Voici un avant/apres réel tire d'un projet sur lequel j'ai travaille. C'est un service de gestion de places (des lieux a visiter) dont j'ai parle sur paltemps.fr :
typescript// Avant
async function proc(d: any, opts: any) {
const r = await db.query("SELECT * FROM t WHERE s = $1", [d.s]);
if (r.length === 0) return null;
const mapped = r.map((x: any) => ({
n: x.name,
v: x.val,
a: opts.a ? x.extra : undefined,
}));
return mapped;
}
// Apres
async function fetchActiveProducts(
filter: ProductFilter,
options: QueryOptions
): Promise<Product[]> {
const products = await db.query(
"SELECT * FROM products WHERE status = $1",
[filter.status]
);
if (products.length === 0) return [];
return products.map((row) => ({
name: row.name,
value: row.val,
extraInfo: options.includeExtras ? row.extra : undefined,
}));
}
Meme logique. Lisibilite complètement différente. Et ca m'a pris 10 minutes.
Les pièges classiques
Le nom qui ment :
typescript// La fonction s'appelle getUser mais elle cree un user si absent
function getUser(email: string): User {
let user = db.findByEmail(email);
if (!user) {
user = db.create({ email }); // surprise !
}
return user;
}
// Mieux
function getOrCreateUser(email: string): User { ... }
Le nom trop générique :
typescript// "data", "info", "item", "result", "value" ne disent rien
const data = await fetchUsers();
const info = parseResponse(raw);
// Sois specifique
const users = await fetchUsers();
const parsedOrder = parseResponse(raw);
Le nom avec le type dedans :
typescript// Le systeme de types fait deja ce travail
const userArray: User[] = [];
const nameString: string = "Alice";
const isActiveBoolean: boolean = true;
// Laisse TypeScript parler
const users: User[] = [];
const name = "Alice";
const isActive = true;
Résumé
- Les booleens commencent par
is,has,can,should - Les fonctions commencent par un verbe d'action precis
- Les classes sont des noms avec des suffixes explicites
- Les fichiers suivent une convention unique dans tout le projet
- Les abreviations sont interdites sauf pour
id,url,htmlet consorts - Le renommage est le refactoring le plus simple et le plus rentable
- Un bon nom elimine le besoin d'un commentaire
Article précédent : 00 - Introduction
Article suivant : 02 - Fonctions
Sources
- Robert C. Martin, "Clean Code", Chapter 2: Meaningful Names - https://www.oreilly.com/library/view/clean-code-a/9780136083238/
- Google TypeScript Style Guide - Naming - https://google.github.io/styleguide/tsguide.html#naming-style
- Kevlin Henney, "Seven Ineffective Coding Habits of Many Programmers" (talk) - https://www.youtube.com/watch?v=ZsHMHukIlJY