Aikido

Como manter as funções concisas: escrever código de fácil manutenção

Legibilidade

Regra

Manter funções concisa.
Funções funções longas são difíceis de compreender, testar, e manter.

Idiomas suportados: 45+

Introdução

As funções que abrangem centenas de linhas misturam várias responsabilidades, tornando difícil compreender o que a função faz sem ler todas as linhas. Funções longas normalmente lidam com várias preocupações, como validação, lógica comercial, transformação de dados e tratamento de erros, tudo em um só lugar. Isso viola o princípio da responsabilidade única e cria um código difícil de testar, depurar e modificar sem quebrar o comportamento existente.

Porque é importante

Manutenção do código: As funções longas exigem que os programadores tenham mais contexto na sua cabeça para compreender o comportamento. A modificação de uma parte corre o risco de quebrar outra porque toda a lógica está interligada. As correcções de erros tornam-se arriscadas, uma vez que os efeitos secundários não intencionais são difíceis de prever.

Complexidade dos testes: Testar uma função de 200 linhas significa cobrir todos os caminhos de código possíveis num teste, exigindo uma configuração complexa e numerosos casos de teste. As funções mais pequenas podem ser testadas de forma independente com testes unitários específicos, tornando os conjuntos de testes mais rápidos e fiáveis.

Exemplos de código

Não 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;
}

Porque é que está errado: Esta função lida com validação, cálculo, operações de base de dados, correio eletrónico e inventário. Os testes requerem a simulação de todas as dependências. Qualquer alteração à lógica ou validação fiscal requer a modificação de toda esta função.

Conformidade:

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

Porque é que isto é importante: Cada função tem uma responsabilidade clara. validateOrder() e calculateTotal() podem ser testadas de forma independente, sem simulações. createOrder() isola a lógica da base de dados. As operações de correio eletrónico e de inventário não bloqueiam a criação de encomendas e as falhas são tratadas separadamente.

Conclusão

Evolua as APIs através de alterações aditivas: adicione novos campos, adicione novos pontos finais, adicione parâmetros opcionais. Quando as alterações de rutura forem inevitáveis, utilize o controlo de versões da API para executar versões antigas e novas em simultâneo. Elimine os campos antigos com prazos claros e guias de migração antes de os remover.

FAQs

Tem perguntas?

Como é que eu decomponho funções longas?

Identificar responsabilidades distintas dentro da função. Extrair a validação para funções separadas. Transferir os cálculos para funções puras. Transferir as operações de E/S (base de dados, chamadas API) para as suas próprias funções. Cada função extraída deve ter um objetivo claro e único com um nome descritivo.

As funções pequenas não acrescentam despesas gerais e prejudicam o desempenho?

Os compiladores e intérpretes modernos incorporam pequenas funções, eliminando a sobrecarga de chamadas. O impacto no desempenho é insignificante quando comparado com os benefícios de manutenção. Crie perfis antes de otimizar. O código legível é mais fácil de otimizar mais tarde, quando se identificam os verdadeiros estrangulamentos.

E no caso de funções com muitos passos sequenciais?

As etapas sequenciais sugerem um fluxo de trabalho que pode ser dividido em funções mais pequenas. Crie funções auxiliares para cada etapa e chame-as em sequência a partir de uma função coordenadora. Isto torna o fluxo de trabalho legível e cada passo pode ser testado de forma independente.

Como é que lido com funções que necessitam de muitos parâmetros após a extração?

Passe objectos que contenham parâmetros relacionados em vez de longas listas de parâmetros. Ou considere se as funções extraídas devem ser métodos de uma classe que contém estado partilhado. Se uma função necessitar de mais de 6 parâmetros, isso pode indicar uma abstração deficiente ou a ausência de estruturas de dados.

Devo extrair funções mesmo que só sejam chamadas uma vez?

Sim, se a extração melhorar a legibilidade. Uma função extraída bem nomeada documenta o que um bloco de código faz melhor do que os comentários. A extração pontual é valiosa quando clarifica a lógica complexa ou reduz os níveis de aninhamento na função principal.

Obter segurança gratuitamente

Proteja seu código, nuvem e tempo de execução em um sistema central.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados do scan em 32secs.