Aikido

Come mantenere le funzioni concise: scrivere codice facile da mantenere

Leggibilità

Regola

Rispetta le funzioni concise.
Le funzioni sono difficili da comprendere, verificare, e manutenere.

Lingue supportate: 45+

Introduzione

Le funzioni che si estendono su centinaia di righe combinano più responsabilità, rendendo difficile comprendere il loro funzionamento senza leggere ogni singola riga. Le funzioni lunghe, in genere, gestiscono più aspetti contemporaneamente, quali la convalida, la logica di business, la trasformazione dei dati e la gestione degli errori, il tutto in un unico punto. Ciò viola il principio della responsabilità singola e genera codice difficile da testare, da debuggare e da modificare senza compromettere il comportamento esistente.

Perché è importante

Manutenibilità del codice: le funzioni lunghe richiedono agli sviluppatori di tenere a mente un contesto più ampio per comprenderne il comportamento. Modificare una parte comporta il rischio di comprometterne un’altra, poiché tutta la logica è strettamente interconnessa. La correzione dei bug diventa rischiosa, poiché è difficile prevedere effetti collaterali indesiderati.

Complessità dei test: testare una funzione di 200 righe significa coprire tutti i possibili percorsi di esecuzione del codice in un unico test, il che richiede una configurazione complessa e numerosi casi di test. Le funzioni più piccole possono essere testate in modo indipendente con test unitari mirati, rendendo le suite di test più veloci e affidabili.

Esempi di codice

❌ 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;
}

Perché è sbagliato: questa funzione gestisce la convalida, i calcoli, le operazioni sul database, le e-mail e l'inventario. Per eseguire i test è necessario simulare tutte le dipendenze. Qualsiasi modifica alla logica fiscale o alla convalida richiede la modifica dell'intera funzione.

✅ 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;
}

Perché è importante: Ogni funzione ha una responsabilità ben definita. validateOrder() e calculateTotal() può essere testato in modo indipendente senza simulazioni. createOrder() isola la logica del database. Le operazioni relative alle e-mail e all'inventario non bloccano la creazione degli ordini e gli errori vengono gestiti separatamente.

Conclusione

Fai evolvere le API attraverso modifiche incrementali: aggiungi nuovi campi, nuovi endpoint e parametri opzionali. Quando le modifiche che comportano incompatibilità sono inevitabili, ricorri al versioning delle API per far funzionare contemporaneamente la versione precedente e quella nuova. Indica chiaramente i tempi e fornisci guide alla migrazione prima di rimuovere i campi obsoleti.

Domande frequenti

Hai delle domande?

Come posso suddividere le funzioni lunghe?

Identificare le diverse responsabilità all'interno della funzione. Estrarre la validazione in funzioni separate. Trasformare i calcoli in funzioni pure. Spostare le operazioni di I/O (database, chiamate API) in funzioni dedicate. Ogni funzione estratta dovrebbe avere un unico scopo ben definito e un nome descrittivo.

Le funzioni di piccole dimensioni non comportano un sovraccarico e non compromettono le prestazioni?

I compilatori e gli interpreti moderni eseguono in linea le funzioni di piccole dimensioni, eliminando il sovraccarico delle chiamate. L'impatto sulle prestazioni è trascurabile rispetto ai vantaggi in termini di manutenibilità. Effettua un'analisi delle prestazioni prima di ottimizzare. Un codice leggibile è più facile da ottimizzare in seguito, quando si individuano i colli di bottiglia effettivi.

E che dire delle funzioni con molti passaggi sequenziali?

I passaggi sequenziali suggeriscono un flusso di lavoro che può essere suddiviso in funzioni più piccole. Crea delle funzioni di supporto per ogni passaggio e chiamale in sequenza da una funzione di coordinamento. In questo modo il flusso di lavoro risulta leggibile e ogni passaggio può essere testato in modo indipendente.

Come gestisco le funzioni che richiedono molti parametri dopo l'estrazione?

Passare oggetti contenenti i parametri correlati anziché lunghi elenchi di parametri. Oppure valutare se le funzioni estraute debbano essere metodi di una classe che gestisce uno stato condiviso. Se una funzione richiede 6 o più parametri, ciò potrebbe indicare un’astrazione inadeguata o la mancanza di strutture dati.

È opportuno estrarre le funzioni anche se vengono chiamate solo una volta?

Sì, se l'estrazione migliora la leggibilità. Una funzione estratta con un nome appropriato documenta ciò che fa un blocco di codice meglio dei commenti. Un'estrazione isolata è utile quando chiarisce una logica complessa o riduce i livelli di nidificazione nella funzione padre.

Metti in sicurezza ora

Metti in sicurezza il tuo codice, il cloud e il runtime in un unico sistema centralizzato.
Trova e risolvi le vulnerabilità rapidamente e automaticamente.

Nessuna carta di credito richiesta | Risultati della scansione in 32 secondi.