Bonnes pratiques de code
Taille des lignes
Les lignes sont par défaut de 80 colonnes si Prettier est utilisé. C’est un héritage des consoles qui ne pouvaient afficher que 80 colonnes. Cela fait un peu court avec les écrans dont nous disposons aujourd’hui.
Les lignes de plus de 120 colonnes doivent être évitées, et les lignes de plus de 140 colonnes proscrites.
Pour VS Code, le paramètre suivant permet de montrer ces deux limites dans la vue éditeur :
"editor.rulers": [120, 140],Taille des fonctions/méthodes
Une fonction (ou méthode) ne devrait pas faire plus de 20 lignes.
Une fonction - un but
Une fonction ne doit faire qu’une chose, et le faire bien, et idéalement être testée.
Le nom de la fonction doit être explicite quant à sa raison d'être.
Gestion des erreurs
Ne jamais ignorer silencieusement les erreurs
// ❌ Mal
try {
await fetchData()
} catch {
// silencieux
}
// ✅ Bien
try {
await fetchData()
} catch (error) {
logger.error(error, 'Erreur lors de la récupération des données')
throw error
}Utiliser des erreurs typées
Créer des classes d'erreurs spécifiques plutôt que d'utiliser des Error génériques :
class NotFoundError extends Error {
constructor(resource: string, id: string | number) {
super(`${resource} avec l'id ${id} non trouvé`)
this.name = 'NotFoundError'
}
}Async/await
Toujours utiliser async/await plutôt que .then()/.catch()
// ❌ Mal
function getUser(id: number) {
return fetch(`/users/${id}`)
.then(res => res.json())
.then(data => data)
.catch(err => console.error(err))
}
// ✅ Bien
async function getUser(id: number) {
const response = await fetch(`/users/${id}`)
return response.json()
}Paralléliser les opérations indépendantes
// ❌ Mal : séquentiel inutilement
const users = await getUsers()
const posts = await getPosts()
// ✅ Bien : parallèle
const [users, posts] = await Promise.all([
getUsers(),
getPosts(),
])Ordre des imports
Les imports doivent être ordonnés de manière cohérente. La configuration ESLint recommandée (cf. ESLint) gère cela automatiquement. L'ordre recommandé est :
- Modules Node.js natifs (
node:fs,node:path) - Packages externes (
fastify,vue) - Modules internes (alias
@/,~/) - Modules relatifs (
./,../)
Chaque groupe doit être séparé par une ligne vide.
Constantes magiques
Les valeurs « magiques » (nombres ou chaînes sans contexte) doivent être extraites dans des constantes nommées :
// ❌ Mal
if (password.length < 8) { ... }
setTimeout(fn, 86400000)
// ✅ Bien
const MIN_PASSWORD_LENGTH = 8
const ONE_DAY_MS = 24 * 60 * 60 * 1000
if (password.length < MIN_PASSWORD_LENGTH) { ... }
setTimeout(fn, ONE_DAY_MS)Choix et veille des dépendances
Avant d'ajouter une dépendance (bibliothèque, framework, action GitHub, image Docker…), il faut systématiquement évaluer :
- La version courante — toujours utiliser la dernière version stable majeure, jamais
@latestou@masteren production. - La popularité — nombre de téléchargements hebdomadaires, nombre de stars GitHub.
- La fréquence de mise à jour — un projet sans commit depuis 12 mois est un signal d'alerte.
- La maintenance — nombre et ancienneté des issues ouvertes, réactivité des mainteneurs, présence d'un
SECURITY.md. - La taille — impact sur le bundle final (bundlephobia.com pour les packages npm).
Outils utiles :
| Outil | Usage |
|---|---|
| npm trends | Comparer la popularité et l'activité de packages npm |
| Bundlephobia | Vérifier la taille d'un package avant de l'installer |
| Snyk Advisor | Score de santé global (maintenance, sécurité, communauté) |
| Socket.dev | Détection de risques dans la chaîne d'approvisionnement |
| GitHub Dependabot | Alertes automatiques de mises à jour et de vulnérabilités |
Référencer @master ou @main dans une GitHub Action est un risque de sécurité.
Toujours épingler les actions sur un tag de version (ex. actions/checkout@v6).
Retours anticipés (early return)
Privilégier les retours anticipés pour éviter l'imbrication excessive :
// ❌ Mal
function processUser(user: User | null) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
// traitement...
}
}
}
}
// ✅ Bien
function processUser(user: User | null) {
if (!user) return
if (!user.isActive) return
if (!user.hasPermission) return
// traitement...
}