Aikido

Cómo mantener las funciones concisas: escribiendo código mantenible

Legibilidad

Regla

Mantenga funciones concisas.
Las funciones largas funciones son difíciles de entender, prueba, y mantener.

Idiomas compatibles: 45+

Introducción

Las funciones que abarcan cientos de líneas mezclan múltiples responsabilidades, lo que dificulta entender qué hace la función sin leer cada línea. Las funciones largas suelen manejar múltiples preocupaciones como la validación, la lógica de negocio, la transformación de datos y el manejo de errores, todo en un solo lugar. Esto viola el principio de responsabilidad única y crea código difícil de probar, depurar y modificar sin romper el comportamiento existente.

Por qué es importante

Mantenibilidad del código: Las funciones largas requieren que los desarrolladores retengan más contexto en su mente para entender el comportamiento. Modificar una parte conlleva el riesgo de romper otra porque toda la lógica está entrelazada. Las correcciones de errores se vuelven arriesgadas ya que los efectos secundarios no deseados son difíciles de predecir.

Complejidad de las pruebas: Probar una función de 200 líneas implica cubrir todas las posibles rutas de código en una sola prueba, lo que requiere una configuración compleja y numerosos casos de prueba. Las funciones más pequeñas pueden probarse de forma independiente con pruebas unitarias específicas, lo que hace que las suites de pruebas sean más rápidas y fiables.

Ejemplos de código

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

Por qué está mal: Esta función gestiona la validación, el cálculo, las operaciones de base de datos, el correo electrónico y el inventario. Las pruebas requieren simular todas las dependencias. Cualquier cambio en la lógica fiscal o la validación requiere modificar toda esta función.

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

¿Por qué esto importa? Cada función tiene una responsabilidad clara. validateOrder() y calcularTotal() se puede probar de forma independiente sin mocks. createOrder() aísla la lógica de la base de datos. Las operaciones de correo electrónico y de inventario no bloquean la creación de pedidos, y los fallos se gestionan por separado.

Conclusión

Evolucione las APIs mediante cambios aditivos: añada nuevos campos, nuevos endpoints, añada parámetros opcionales. Cuando los cambios disruptivos sean inevitables, utilice el versionado de API para ejecutar versiones antiguas y nuevas de forma concurrente. Depreque campos antiguos con plazos claros y guías de migración antes de eliminarlos.

Preguntas frecuentes

¿Tiene preguntas?

¿Cómo desgloso funciones largas?

Identifica responsabilidades distintas dentro de la función. Extrae la validación en funciones separadas. Separa los cálculos en funciones puras. Mueve las operaciones de E/S (base de datos, llamadas a API) a sus propias funciones. Cada función extraída debe tener un propósito claro y único con un nombre descriptivo.

¿Las funciones pequeñas no añaden sobrecarga y perjudican el rendimiento?

Los compiladores e intérpretes modernos insertan funciones pequeñas, eliminando la sobrecarga de llamadas. El impacto en el rendimiento es insignificante en comparación con los beneficios de la mantenibilidad. Realice perfiles antes de optimizar. El código legible es más fácil de optimizar más tarde cuando se identifican cuellos de botella reales.

¿Qué pasa con las funciones con muchos pasos secuenciales?

Los pasos secuenciales sugieren un flujo de trabajo que puede dividirse en funciones más pequeñas. Cree funciones auxiliares para cada paso y llámelas en secuencia desde una función coordinadora. Esto hace que el flujo de trabajo sea legible y que cada paso sea comprobable de forma independiente.

¿Cómo gestiono las funciones que requieren muchos parámetros después de la extracción?

Pase objetos que contengan parámetros relacionados en lugar de largas listas de parámetros. O considere si las funciones extraídas deberían ser métodos de una clase que contenga un estado compartido. Si una función necesita más de 6 parámetros, podría indicar una abstracción deficiente o la falta de estructuras de datos.

¿Debería extraer funciones incluso si solo se llaman una vez?

Sí, si la extracción mejora la legibilidad. Una función extraída con un buen nombre documenta lo que hace un bloque de código mejor que los comentarios. La extracción puntual es valiosa cuando aclara lógica compleja o reduce los niveles de anidamiento en la función padre.

Asegúrate ahora.

Proteja su código, la nube y el entorno de ejecución en un único sistema central.
Encuentre y corrija vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.