Aikido

FreeCodeCamp qualité du code : les règles qui peuvent améliorer toute base de code

Introduction

FreeCodeCamp est plus qu'une plateforme d'apprentissage ; c'est une grande base de code open-source avec des milliers de contributeurs et des millions de lignes de JavaScript et TypeScript. La gestion d'un projet aussi complexe nécessite des modèles architecturaux cohérents, des conventions de nommage strictes et des tests approfondis pour éviter les régressions et maintenir la fiabilité.

Dans cet article, nous présentons les règles de revue de code les plus impactantes extraites de FreeCodeCamp. Chaque règle démontre comment une conception soignée, un flux de données cohérent et une gestion robuste des erreurs peuvent aider les grands projets à rester organisés et à réduire le risque de bogues subtils au fil du temps.

Les défis

Le maintien de la qualité du code dans FreeCodeCamp est un défi en raison de son importante base de code JavaScript et TypeScript et de ses milliers de contributeurs. Garantir des modèles cohérents, un flux de données prévisible et des fonctionnalités fiables nécessite des règles de révision structurées et des vérifications automatisées.

Les principaux défis à relever sont les suivants : modèles de codage incohérents, modules hérités étroitement couplés, couverture de test inégale, dérive de la documentation et volume élevé de demandes d'extraction.

Des normes claires, une validation automatisée et un examen minutieux du code permettent de maintenir la base de code à jour, stable et évolutive au fur et à mesure que le projet se développe.

L'importance de ces règles

Des règles cohérentes de révision du code améliorent la maintenabilité en imposant une structure de module uniforme, des conventions de dénomination et des flux de données prévisibles, ce qui rend les tests plus fiables et les dépendances plus faciles à suivre.

Ils améliorent également la sécurité grâce à la validation des entrées, à la gestion des erreurs et aux effets secondaires contrôlés, tout en accélérant l'intégration en aidant les nouveaux contributeurs à comprendre rapidement les responsabilités des modules et les points d'intégration.

Contexte de transition vers ces règles

Ces règles sont extraites du dépôt de FreeCodeCamp et des demandes d'extraction, reflétant des problèmes récurrents tels qu'un flux de données peu clair, une gestion des erreurs manquante et des tests incohérents qui ont un impact sur la stabilité et la maintenabilité.

Chaque règle met en évidence un écueil concret, explique son impact sur les performances, la clarté ou la fiabilité, et inclut des exemples ❌ non conformes vs ✅ conformes en JavaScript ou TypeScript.

1. Éviter la surutilisation d'un type dans TypeScript

Évitez d'utiliser n'importe quel type dans TypeScript. Définissez toujours des types précis et explicites pour les variables, les paramètres des fonctions et les valeurs de retour afin de garantir la sécurité des types et d'éviter les erreurs d'exécution.

❌ Non conforme :

laisser userData : any = fetchUserData() ;

✅ Conforme :

interface UserData {
  id: string;
  name: string;
  email: string;
}

let userData: UserData = fetchUserData();

Pourquoi c'est important : L'utilisation de any désactive la vérification de type de TypeScript, ce qui peut entraîner des erreurs d'exécution et réduire la maintenabilité et la fiabilité du code. Les types explicites rendent le code plus sûr et plus facile à comprendre pour les autres développeurs.

2. Préférer les noms de variables descriptifs aux abréviations

Utilisez toujours des noms de variables clairs et descriptifs. Évitez les abréviations ou les noms cryptiques qui obscurcissent la signification du code.

❌ Non conforme :

const usr = getUser() ;

✅ Conforme :

const user = getUser() ;

Pourquoi c'est important : Des noms de variables descriptifs facilitent la lecture, la compréhension et la maintenance du code. Une mauvaise dénomination peut perturber les développeurs et augmenter le risque d'introduction de bogues.

3. Éviter les boucles ou les conditionnelles profondément imbriquées

Refondre le code pour éviter les imbrications profondes dans les boucles ou les conditionnelles. Utiliser des retours anticipés ou des fonctions d'aide pour aplanir la logique.

❌ Non conforme :

 if (user) {
   if (user.isActive) {
     if (user.hasPermission) {
      // Effectuer l'action
 }
  }
}

✅ Conforme :

 if (!user) return;
si (!user.isActive) retour;
si (!user.hasPermission) retour;

// Exécute l'action
processUserAction(user) ;

Pourquoi c'est important : Une logique profondément imbriquée est difficile à suivre, à maintenir et à tester. Elle complique l'écriture des tests unitaires, en particulier pour les cas négatifs et les premiers échecs. L'aplatissement du flux de contrôle avec des retours précoces rend le code plus facile à raisonner, améliore la couverture des tests et réduit le risque de bogues cachés dans les cas limites.

4. Assurer une gestion cohérente des erreurs dans l'ensemble du code

Mettez toujours en œuvre une gestion cohérente des erreurs. Utilisez des fonctions d'erreur centralisées ou des modèles standardisés pour traiter les exceptions de manière uniforme.

❌ Non conforme :

try {
  // Some code
} catch (e) {
  console.error(e);
}

✅ Conforme :

try {
  // Some code
} catch (error) {
  logError(error);
  throw new CustomError('An error occurred', { cause: error });
}

En quoi cela est-il important ? Une gestion cohérente des erreurs facilite le débogage, prévient les comportements inattendus et garantit la fiabilité de l'application.

5. Éviter le codage en dur des valeurs de configuration

Ne pas coder en dur des valeurs spécifiques à l'environnement telles que des URL, des ports ou des secrets. Utilisez toujours des fichiers de configuration ou des variables d'environnement.

❌ Non conforme :

const apiUrl = 'https://api.example.com';

✅ Conforme :

const apiUrl = process.env.API_URL ;

Pourquoi cela est-il important ? Les valeurs codées en dur réduisent la flexibilité, rendent le code moins sûr et compliquent le déploiement dans différents environnements. L'utilisation de configurations garantit la maintenabilité et la sécurité.

6. Faire en sorte que les fonctions se concentrent sur une seule responsabilité

Veillez à ce que chaque fonction accomplisse une tâche unique et bien définie. Évitez les fonctions qui assument des responsabilités multiples, car cela peut entraîner une certaine confusion et des difficultés de maintenance.

❌ Non conforme :

function processUserData(user) {
  const validatedUser = validateUser(user);
  saveUserToDatabase(validatedUser);
  sendWelcomeEmail(validatedUser);
}

✅ Conforme :

function validateUser(user) {
  // logique de validation
}

function saveUserToDatabase(user) {
  // logique de sauvegarde
}

function sendWelcomeEmail(user) {
  // logique d'envoi d'email
}

Pourquoi cela est-il important ? Les fonctions ayant une responsabilité unique sont plus faciles à tester, à déboguer et à maintenir. Elles favorisent la réutilisation du code et améliorent la lisibilité.

7. Éviter d'utiliser des nombres magiques

Remplacer les nombres magiques par des constantes nommées afin d'améliorer la clarté du code et la maintenabilité.

❌ Non conforme :

constante surface = longueur * 3.14159 * rayon * rayon ;

✅ Conforme :

const PI = 3.14159;
const surface = longueur * PI * rayon * rayon ;

Pourquoi cela est-il important ? Les nombres magiques peuvent obscurcir la signification du code et rendre les modifications futures sujettes à erreur. Les constantes nommées fournissent un contexte et réduisent le risque d'introduire des bogues.

8. Minimiser l'utilisation de variables globales

Limiter l'utilisation de variables globales pour réduire les dépendances et les conflits potentiels dans la base de code.

❌ Non conforme :

let user = { name: 'Alice' };

function greetUser() {
  console.log(`Hello, ${user.name}`);
}

✅ Conforme :

function greetUser(user) {
  console.log(`Hello, ${user.name}`);
}

const user = { name: 'Alice' };
greetUser(user);

Pourquoi cela est-il important ? Les variables globales peuvent créer des dépendances cachées et des effets secondaires imprévisibles. Elles rendent difficile la traçabilité de l'origine des données ou de leur évolution au sein de la base de code. Le passage explicite de données par le biais de paramètres de fonction permet de maintenir un flux de données clair et contrôlé, ce qui améliore la modularité, le débogage et la maintenabilité à long terme.

9. Utiliser des modèles littéraux pour la concaténation de chaînes de caractères

Préférer les modèles littéraux à la concaténation de chaînes pour une meilleure lisibilité et de meilleures performances.

❌ Non conforme :

const message = 'Hello, ' + user.name + ' ! Vous avez ' + user.notifications + ' nouvelles notifications.';

✅ Conforme :

const message = `Hello, ${user.name}! You have ${user.notifications} new notifications.`;

Pourquoi c'est important : Les littéraux de modèle offrent une syntaxe plus propre et améliorent la lisibilité, en particulier lorsqu'il s'agit de chaînes de caractères complexes ou de contenus multilignes.

10. Mettre en œuvre une validation appropriée des entrées

Validez toujours les entrées des utilisateurs afin d'éviter que des données non valides n'entrent dans le système et de renforcer la sécurité.

❌ Non conforme :

function processUserInput(input) {
  // logique de traitement
}

✅ Conforme :

function validateInput(input) {
  if (typeof input !== 'string' || input.trim() === '') {
    throw new Error('Invalid input');
  }
}

function processUserInput(input) {
  validateInput(input);
  // processing logic
}

Pourquoi c'est important : La validation des entrées est essentielle pour éviter les erreurs, garantir l'intégrité des données et se protéger contre les failles de sécurité telles que les attaques par injection.

11. Maintien d'une modification logique par demande de retrait

Veillez à ce que chaque demande d'extraction (PR) mette en œuvre un seul changement logique ou une seule fonctionnalité ; évitez de combiner des corrections, des remaniements et des ajouts de fonctionnalités sans rapport entre eux dans une seule PR.

❌ Non conforme :

# "Fix login + update homepage"
--- auth.js
+ if (!user) throw new Error('User not found');

--- HomePage.js
- <button>Start</button>
+ <button>Begin Journey</button>

✅ Conforme : (diff)

# PR 1: Fix login validation
+ if (!user) throw new Error('User not found');

# PR 2: Update homepage button
+ <button>Begin Journey</button>

Pourquoi c'est important : Les PR de petite taille et ciblés simplifient l'examen du code, réduisent le risque d'effets secondaires involontaires et accélèrent les cycles de fusion. Les outils d'intelligence artificielle peuvent détecter si des fichiers, des modules ou des domaines sans rapport les uns avec les autres sont modifiés dans la même PR, ce que les linters ne peuvent pas déterminer.

12. Utiliser des noms alignés sur le domaine pour les API et les services

Nommer les API, les services et les modules en fonction du domaine d'activité (par exemple, challengeService.createSubmission et non handler1.doIt) ; les noms doivent clairement refléter l'entité et l'action.

❌ Non conforme :

// backend/services/handler.js
export async function doIt(data) {
  return await process(data);
}

// routes/index.js
router.post('/submit', handler.doIt);

✅ Conforme :

// backend/services/challengeService.js
export async function createSubmission({ userId, challengeId, answer }) {
  return await challengeModel.create({ userId, challengeId, answer });
}

// routes/challenges.js
router.post('/submissions', challengeService.createSubmission);

Pourquoi c'est important : Le nommage aligné sur le domaine rend le code auto-documenté, aide à la clarté pour les nouveaux contributeurs et s'aligne sur la logique d'entreprise. Seule une IA consciente du contexte sémantique (noms d'entités, couches de services) peut détecter un nommage générique ou mal aligné entre les modules.

13. S'assurer que les tests couvrent les défaillances et les cas limites

Rédiger des tests non seulement pour le "chemin heureux", mais aussi pour les conditions d'erreur, les cas extrêmes et les valeurs limites ; confirmer que chaque module critique est soumis à des tests positifs et négatifs.

❌ Non conforme :

describe('login', () => {
  it('should succeed with correct credentials', async () => { … });
});

✅ Conforme :

describe('login', () => {
  it('should succeed with correct credentials', async () => { … });
  it('should fail with incorrect password', async () => { … });
  it('should lock account after 5 failed attempts', async () => { … });
});

Pourquoi cela est-il important ? La logique critique de l'entreprise s'interrompt souvent lorsque des cas limites sont manqués, comme des entrées non valides, des dépassements de délai ou des échecs d'ouverture de session. Le fait de tester à la fois les succès et les échecs permet de s'assurer que l'application se comporte de manière fiable dans des conditions réelles et d'éviter les régressions qui sont difficiles à détecter ultérieurement.

14. Éviter de mélanger les couches : Les composants de l'interface utilisateur ne doivent pas exécuter de logique commerciale

Garder les composants de l'interface utilisateur (React, front-end) exempts de logique commerciale et d'appels de base de données/service ; déléguer ces tâches à des services dédiés ou à des crochets.

❌ Non conforme :

// FreeCodeCamp-style
function CurriculumCard({ user, challenge }) {
  if (!user.completed.includes(challenge.id)) {
    saveCompletion(user.id, challenge.id);
  }
  return <Card>{challenge.title}</Card>;
}

✅ Conforme :

function CurriculumCard({ user, challenge }) {
  return <Card>{challenge.title}</Card>;
}

// In service:
async function markChallengeComplete(userId, challengeId) {
  await completionService.create({ userId, challengeId });
}

Pourquoi c'est important : Le fait de mélanger la logique commerciale aux composants de l'interface utilisateur brouille les frontières entre les couches et rend les changements futurs risqués. Cela oblige également les développeurs frontaux à comprendre la logique dorsale et ralentit la collaboration. En séparant les responsabilités, on s'assure que chaque couche peut évoluer indépendamment et on réduit le risque d'introduire des bogues subtils lors de la refonte.

Conclusion

En analysant le référentiel de FreeCodeCamp, nous avons extrait des règles pratiques d'examen du code qui aident à maintenir l'organisation, la lisibilité et la maintenance de sa vaste base de code. Ces 20 règles reflètent des pratiques réelles issues d'années de contributions, même si elles ne sont pas appliquées par un outil automatisé.

L'application de ces leçons à vos propres projets peut améliorer la clarté, réduire les bogues et faciliter la collaboration. Elles constituent une base solide pour faire évoluer votre code en toute sécurité, en veillant à ce qu'au fur et à mesure que le projet grandit, le code reste fiable et facile à utiliser.

L'examen du code ne se limite pas à la vérification de la syntaxe. Il s'agit de maintenir la qualité et l'intégrité de votre code au fil du temps. Apprendre d'un projet open-source mature comme FreeCodeCamp donne aux développeurs des conseils concrets pour améliorer n'importe quelle base de code.

FAQ

Vous avez des questions ?

Comment ces règles abordent-elles le flux de données et les limites des modules dans FreeCodeCamp ?

Ils assurent une gestion cohérente des entrées/sorties, un comportement prévisible des fonctions et une séparation claire des préoccupations, ce qui réduit le couplage et facilite le remaniement et le test des modules.

Comment ces règles améliorent-elles la couverture et la fiabilité des tests ?

En exigeant des tests unitaires et d'intégration pour les cas limites, la gestion des erreurs et la logique commerciale critique, ils s'assurent que les régressions sont détectées rapidement et que les tests automatisés reflètent avec précision le comportement du système.

Comment ces règles permettent-elles de gérer les codes hérités ?

Ils fournissent des modèles pour la refonte de modules étroitement couplés ou obsolètes tout en maintenant la compatibilité ascendante et en minimisant le risque de régression.

Ces règles peuvent-elles prévenir les problèmes de sécurité dans FreeCodeCamp ?

Oui. Ils appliquent la validation des entrées, la manipulation sûre des données et la gestion cohérente des erreurs, ce qui limite les risques tels que les attaques par injection, les exceptions non gérées et les fuites de données.

Comment les nouveaux contributeurs peuvent-ils bénéficier de ces règles ?

Des responsabilités claires en matière de modules, une dénomination cohérente et une gestion normalisée des erreurs permettent aux nouveaux développeurs de comprendre rapidement l'architecture et de l'intégrer en toute sécurité sans introduire de régressions.

Comment ces règles ont-elles été dérivées du FreeCodeCamp ?

Ils ont été extraits de l'analyse des demandes d'extraction, des modifications de code, des fils de discussion et des schémas récurrents qui ont eu un impact sur la maintenabilité, la lisibilité et la stabilité de la base de code.

Obtenir la sécurité gratuitement

Sécurisez votre code, votre cloud et votre environnement d'exécution dans un système central.
Trouvez et corrigez rapidement et automatiquement les vulnérabilités.

Aucune carte de crédit n'est requise | Scanner les résultats en 32sec.