Aikido

Comment garder les fonctions concises : écrire du code maintenable

Lisibilité

Règle

Conserver fonctions concis.
Les fonctions fonctions sont difficiles difficiles difficiles à comprendre, test, et de maintenance.

Langues prises en charge : 45+

Introduction

Les fonctions s'étendant sur des centaines de lignes mélangent plusieurs responsabilités, ce qui rend difficile de comprendre ce que fait la fonction sans lire chaque ligne. Les fonctions longues gèrent généralement plusieurs préoccupations comme la validation, la logique métier, la transformation des données et la gestion des erreurs, le tout au même endroit. Cela viole le principe de responsabilité unique et crée un code difficile à tester, à déboguer et à modifier sans altérer le comportement existant.

Pourquoi c'est important

Maintenabilité du code : Les fonctions longues exigent des développeurs qu'ils retiennent plus de contexte pour comprendre le comportement. Modifier une partie risque d'en casser une autre car toute la logique est entrelacée. Les corrections de bugs deviennent risquées car les effets secondaires imprévus sont difficiles à prédire.

Complexité des tests : Tester une fonction de 200 lignes signifie couvrir tous les chemins de code possibles en un seul test, nécessitant une configuration complexe et de nombreux cas de test. Les fonctions plus petites peuvent être testées indépendamment avec des tests unitaires ciblés, rendant les suites de tests plus rapides et plus fiables.

Exemples de code

❌ Non conforme :

async function processOrder(orderData) {
    if (!orderData.items?.length) throw new Error('Items required');
    if (!orderData.customer?.email) throw new Error('Email required');
    const subtotal = orderData.items.reduce((sum, item) => 
        sum + (item.price * item.quantity), 0);
    const tax = subtotal * 0.08;
    const total = subtotal + tax + (subtotal > 50 ? 0 : 9.99);
    const order = await db.orders.create({
        customerId: orderData.customer.id,
        total: total
    });
    await emailService.send(orderData.customer.email, `Order #${order.id}`);
    await inventory.reserve(orderData.items);
    return order;
}

Pourquoi c'est incorrect : Cette fonction gère la validation, le calcul, les opérations de base de données, les e-mails et l'inventaire. Les tests nécessitent de simuler toutes les dépendances. Toute modification de la logique fiscale ou de la validation nécessite de modifier l'intégralité de cette fonction.

✅ Conforme :

function validateOrder(orderData) {
    if (!orderData.items?.length) throw new Error('Items required');
    if (!orderData.customer?.email) throw new Error('Email required');
}

function calculateTotal(items) {
    const subtotal = items.reduce((sum, item) => 
        sum + (item.price * item.quantity), 0);
    return subtotal + (subtotal * 0.08) + (subtotal > 50 ? 0 : 9.99);
}

async function createOrder(customerId, total) {
    return await db.orders.create({ customerId, total });
}

async function processOrder(orderData) {
    validateOrder(orderData);
    const total = calculateTotal(orderData.items);
    const order = await createOrder(orderData.customer.id, total);
    
    // Non-critical operations in background
    emailService.send(orderData.customer.email, `Order #${order.id}`).catch(console.error);
    
    return order;
}

Pourquoi c'est important : Chaque fonction a une responsabilité claire. validateOrder() et calculateTotal() peut être testé indépendamment sans mocks. createOrder() isole la logique de la base de données. Les opérations d'e-mail et d'inventaire ne bloquent pas la création de commandes, et les échecs sont gérés séparément.

Conclusion

Faire évoluer les API par des modifications additives : ajouter de nouveaux champs, de nouveaux endpoints, des paramètres optionnels. Lorsque des changements cassants (breaking changes) sont inévitables, utilisez le versioning d'API pour exécuter simultanément les anciennes et les nouvelles versions. Dépréciez les anciens champs avec des échéanciers clairs et des guides de migration avant de les supprimer.

FAQ

Des questions ?

Comment décomposer les fonctions longues ?

Identifiez les responsabilités distinctes au sein de la fonction. Extrayez la validation dans des fonctions séparées. Isolez les calculs dans des fonctions pures. Déplacez les opérations d'E/S (base de données, appels API) dans leurs propres fonctions. Chaque fonction extraite doit avoir un objectif clair et unique avec un nom descriptif.

Les petites fonctions n'ajoutent-elles pas de surcoût et ne nuisent-elles pas aux performances ?

Les compilateurs et interpréteurs modernes inlinent les petites fonctions, éliminant la surcharge d'appel. L'impact sur les performances est négligeable comparé aux avantages en termes de maintenabilité. Profilez avant d'optimiser. Le code lisible est plus facile à optimiser plus tard lorsque vous identifiez les goulots d'étranglement réels.

Qu'en est-il des fonctions avec de nombreuses étapes séquentielles ?

Des étapes séquentielles suggèrent un workflow qui peut être décomposé en fonctions plus petites. Créez des fonctions d'assistance pour chaque étape et appelez-les séquentiellement depuis une fonction coordinatrice. Cela rend le workflow lisible et chaque étape testable indépendamment.

Comment gérer les fonctions qui nécessitent de nombreux paramètres après extraction ?

Privilégiez le passage d'objets regroupant des paramètres liés plutôt que de longues listes de paramètres. Vous pouvez également envisager si les fonctions extraites devraient être des méthodes d'une classe gérant un état partagé. Si une fonction requiert plus de 6 paramètres, cela pourrait révéler une abstraction déficiente ou des structures de données manquantes.

Dois-je extraire des fonctions même si elles ne sont appelées qu'une seule fois ?

Oui, si l'extraction améliore la lisibilité. Une fonction extraite bien nommée documente mieux ce qu'un bloc de code fait que les commentaires. L'extraction ponctuelle est précieuse lorsqu'elle clarifie une logique complexe ou réduit les niveaux d'imbrication dans la fonction parente.

Sécurisez-vous maintenant.

Sécuriser votre code, votre cloud et votre runtime dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Pas de carte de crédit requise | Résultats du scan en 32 secondes.