Refatoração: a Arte de Lapidar Código

A refatoração é o processo de melhorar a estrutura interna do código sem alterar seu comportamento externo. Este artigo explora como essa prática é fundamental para criar soluções de qualidade.

img-2


A refatoração é o processo de alterar o código fonte de uma maneira que não altere seu comportamento externo e ainda melhore a sua estrutura interna. É uma técnica disciplinada de limpar e organizar o código, e por consequência, minimizar a chance de introduzir novos bugs.
— Martin Fowler

Do dicionário, lapidar: tornar perfeito o que é rudimentar, tosco, bruto; aperfeiçoar. Essa definição revela o que buscamos quando pensamos em refatoração, realizar mudanças no código em busca de perfeição e beleza.


Desenvolvedor: um artesão digital


O que é um desenvolvedor de software senão um artesão digital em sua busca por inspiração para criar soluções. Não estamos interessados em criar somente coisas que funcionam, mas sim coisas com qualidade. Nem sempre o resultado vem como esperado e, diferente de um artesão convencional, podemos nos dar ao luxo de entregar algo e voltar a qualquer momento para fazer alterações.

Como dito no início do artigo, buscamos melhorias que tragam beleza ao nosso código e devemos lembrar que a beleza reside na simplicidade. Logo devemos buscar meios de simplificar o nosso código, talvez sem perceber o valor que isso agrega.

Um código simples é facil de ser compreendido, quer seja por outro artesão ou por nós mesmos daqui a semanas ou meses, como disse Kent Beck, o código precisa “revelar suas intenções”. 

Um código que pode ser entendido com facilidade pode, na mesma medida, ser modificado e aprimorado com menos chances de efeitos colaterais. Sim, estou falando de bugs, e sei que chamei sua atenção – gotcha!

É preciso admitir a incômoda verdade que nem sempre acertamos o design na primeira tentativa, não importa o seu nível de senioridade em desenvolvimento de software. A primeira versão do código que criamos pode não ser o nosso código mais limpo. Dependendo das circunstâncias, estamos mais preocupados em fazê-lo funcionar, deixando para um segundo momento um olhar mais crítico, verificando se violamos algum princípio SOLID ou se deixamos de lado algum design pattern.

Existem situações em que, antes de adicionar novas funcionalidades ao software, você percebe a necessidade de modificá-lo, a fim de facilitar a entrada do novo código.


Make the change easy, then make the easy change.
— Kent Beck


Por que mexer no que está funcionando?

img-4


Se você desenvolve software e nunca se perguntou isso preciso lhe dar os parabéns, você ama o que faz e gosta das coisas bem feitas. Mas as chances de alguém ter essa dúvida não são pequenas. E não lhes tiro a razão, o porquê das coisas deve ser o motor das nossas decisões.

Se eu tivesse a missão de convencer o meu chefe sobre a importância da refatoração, eu apresentaria o estudo de caso a seguir que foi extraído do livro Clean Architecture, onde podemos analisar dados reais de uma empresa real.

O primeiro gráfico apresenta o crescimento da equipe de engenharia, e logo em seguida, um gráfico com a produtividade, medida em linhas de código. No segundo gráfico, observamos que, embora cada entrega conte com um número cada vez maior de desenvolvedores, a produtividade começa a atingir um ponto de estagnação.

img-6

Gráfico 1 – Crescimento da equipe de engenharia

img-8


Gráfico 2 – Produtividade ao longo do mesmo período


Era esperado que mais linhas de código fossem criadas com a chegada de novos desenvolcedores, certo? Você pode pensar que outras variáveis podem ter influenciado, como falta de motivação e dedicação do time, mas o que foi observado é que o total de horas extras não diminuiu no período. 

Olhando para o gráfico de custo por linha de código ficamos mais perplexos pois o custo aumentou 40 vezes entre as entregas 1 e 8.

img-10


Gráfico 3 – Custo por linha de código em função do tempo


Mas, se você quer mesmo assustar o board da empresa, apresente o próximo gráfico, que mostra a folha de pagamento mensal do desenvolvimento para o mesmo período. O que iniciou em algumas centenas de milhares de dólares chegou, na oitavo entrega, à casa dos 20 milhões, sem previsão de mudança de cenário.

img-12

Gráfico 4 – Folha de pagamento mensal do desenvolvimento por release


Este é um retrato de um sistema onde o prazo de entrega sempre foi a prioridade. Desenvolvedores foram adicionados na esperança de entregar mais código em menos tempo e nunca houve uma real preocupação com qualidade do código. Com o tempo, adicionar funcionalidades ficou mais difícil, a maior parte do esforço era empregado em correções, e sobrava pouco tempo para adicionar recursos importantes.


Metodologias ágeis


O conceito ágil é frequentemente confundido pelas pessoas que associam agilidade com rapidez. Definir prazos apertados esperando ser ágil está muito longe daquilo que é este método. Mas, se o mesmo for aplicado de maneira adequada, pode-se ter a impressão de as coisas estão evoluindo realmente rápido, como se os desenvolvedores estivessem acelerando o ritmo para fazer as entregas. 

Onde a agilidade faz a diferença de fato é na reação à mudanças, nas tomadas de decisões e na correção do rumo do projeto, quando necessário.

Mas, como nem tudo são flores, trabalhando com métodos ágeis podemos nos deparar com os seguintes cenários:

  • Os requisitos podem sofrer mudanças e essas mudanças podem trazer novas visões sobre a solução.
  • Iniciar a sprint sem os requisitos estarem claramente definidos. Isso, em alguns casos, faz parte da essência do próprio framework, que busca errar o mais cedo possível para fazer correções.

Nesses casos a refatoração é uma ferramenta útil e necessária, dada a característica ágil da metodologia, que busca entregas pequenas admitindo falhas que devem ser corrigidas em seguida.

Quer saber mais sobre o mundo ágil? Vale a pena ler os excelentes artigos sobre como aplicar o manifesto ágil à constução de um mindset ágil e sobre o funcionamento de uma equipa Scrum.


Quando refatorar?


O ideal é que a refatoração aconteça dentro de um período razoável de tempo, próximo ao momento da criação do código, o que também é conhecido como refatoração tempestiva. Porém, antes de começar a refatorar existem alguns requisitos que precisam ser observados. 

Se você é um leitor atento notou que a palavra “comportamento” aparece diversas vezes ao longo do artigo e, deve ter imaginado que ela é uma peça importante da refatoração. Mas como garantir que o comportamento do código se mantenha inalterado quando lidamos com projetos com grande volume de código, fluxos e cenários? É aí que entram os testes.

O primeiro requisito, antes de pensar em refatorar, é a presença de testes unitários, que coincide com a primeira regra do Simple Design de Kent Beck. Eles são essenciais para garantir que o comportamento do software permaneça inalterado, funcionando como um contrato e fornecendo tranquilidade para o desenvolvedor fazer o que é preciso. 

É claro que não basta ter testes, é necessária uma boa cobertura de testes. Digamos que 2%, 5% ou 8% de cobertura de testes é melhor do que nada, mas são números muito baixos que não trarão segurança para afirmar que tudo está funcionando como deveria, ou que bugs não foram adicionados.

Para garantir que a refatoração foi bem sucedida, é necessário que todos os testes unitários continuem passando com sucesso, sem ter que modificar os testes, é claro.

img-14



Quando não refatorar

Em tese, todo o código ruim deveria ser refatorado, mas existem algumas exceções onde podemos avaliar se o esforço vale a pena. Quando lidamos com software legado, por exemplo, precisamos determinar se vale a pena refatorá-lo ou deprecá-lo. 

Você pode analisar, por exemplo, se as versões da linguagem e das bibliotecas estão muito desatualizadas. A ausência de testes unitários pode indicar a má qualidade do código e a prensença de muitos bugs.

Em alguns casos, o esforço para refatorar um software com uma grande quantidade de problemas pode ser equivalente ao de escrever um novo software.


Como medir o seu código


Complexidade Ciclomática e Complexidade Cognitiva são métricas usadas para determinar a qualidade de um código. Realizar essa análise manualmente é trabalhoso, por isso o mais indicado é fazer uso de ferramentas que realizam esse trabalho de maneira automatizada. A ideia aqui é apresentar conceitos superficiais sobre essas métricas para termos uma ideia do que elas representam.


Complexidade Ciclomática


A Complexidade Ciclomática é uma medida de quão difícil é testar uma unidade de código.

Ela é indicada para medir a complexidade de partes do código, como métodos e rotinas, e não do código como um todo. Em suma, ela mede a quantidade máxima de caminhos independentes no código.

O seu valor tem relação direta com o número de cenários de testes necessários para obter uma cobertura de 100% do método. Um teste com um número elevado de cenários indica que o método é complexo e difícil de ler.

img-16

Complexidade Cognitiva


É uma medida de quão difícil é entender uma unidade de código. Ela mede a quantidade de quebras do fluxo linear de leitura, ponderada pelo nível de aninhamento dessas quebras. 

Ela foca na segunda regra do Simple Design de Kent Beck, que é revelar a intenção do código, e tem uma baixa pontuação quando o código é mais expressivo.

img-18

                              Complexidade cognitiva – hadouken code


Ferramentas de análise de código


Além da nossa capacidade de perceber problemas no código, podemos contar com ferramentas que facilitam muito a nossa vida como o Sonarqube, JArchitect, ESLint, etc.

Usando o Sonarqube como exemplo, ele fornece uma visão geral do status do código de um projeto com diversos indicadores.

Um indicador muito interessante no Sonarqube é o de dívida técnica, que estima o tempo aproximado que seria necessário para refatorar o seu código.

img-20


Refatoração: a arte de lapidar código – considerações finais


Imaginem um sábio, no topo de um monte, criando uma definição para a refatoração. Ele diria que “refatorar é mudar sem mudar” – típico desses personagens que escondem conhecimento em frases enigmáticas, e que não se importam muito se você vai entender o significado. Mas espero que este artigo tenha lhe ajudado o suficiente para que a mensagem tenha feito sentido para você, que a ideia por trás da refatoração é mudar o código sem alterar a sua essência, o seu comportamento, é buscar a simplicidade em prol da manutenibilidade. 

E talvez esse conceito não lhe seja totalmente desconhecido. Se você alguma vez substituiu um número mágico por uma constante ou o nome de uma variável por outro nome mais significativo, adivinha? Você refatorou o seu código. Cheers!