Última atualização:

Revisão, comentários e críticas ao livro Refactoring, por Martin Fowler

Nawarian
Nawarian livro

Eu li este livro pela primeira vez há alguns anos, e esta já é minha terceira leitura do mesmo título. Eu recomendo DEMAIS ler este livro e tirar suas próprias conclusões, citações e o que mais for.

Este artigo vai com certeza enviesar a forma como você vai ler o livro Refactoring, mas o conteúdo e qualidade do livro não mudam. E caso você tenha dificuldade com leituras longas, com certeza vai ser útil pra ti ler este artigo primeiro.

Esta é uma literatura fundamental para toda pessoa que trabalha com desenvolvimento de software. Eu desconfiaria de qualquer dev sênior que nunca tenha ouvido falar do termo Refactoring (refatoração).

Conteúdos deste artigo

Revisão e ideias centrais

A estrutura do livro

O livro possui duas partes: a primeira apresenta a teoria de refatoração e alguns exemplos práticos enquanto a segunda parte é só um catálogo de refatorações.

Dentre os exemplos Martin Fowler mostra alguns códigos – a última edição vem com códigos em JavaScript, bem feios por sinal – e explica, passo a passo, como torná-los mais legíveis e mais fáceis de testar.

O catálogo de refatoração mostra não só o motivo daquela refatoração, mas também dá um nome à elas. Isto é poderozíssimo porque cria um vocabulário comum entre pessoas desenvolvedoras. Da mesma forma que você entende facilmente o que é uma classe do tipo Factory, o livro busca criar compreensão sobre o que significa a refatoração Extrair Método.

Ler os exemplos do livro é uma experiência mágica porque o autor não apenas explica o que está acontecendo linha a linha, mas também faz referência aos nomes das refatorações que utilizou de seu próprio catálogo.

Neste artigo eu não vou focar no catálogo em si, mas sim na parte teórica. Se você tem curiosidade sobre o catálogo, dê uma olhada na apresentação da incrível Jucy Cabrera (@jucycabrera) fez ano passado no Locaweb PHP Community Summit.

A apresentação da Jucy começa no minuto 02:03:00

O que é refatoração?

Refatoração, de acordo com Martin Fowler, é o ato de alterar um código sem mudar o seu comportamento observável.

Por exemplo, a função a seguir recebe dois parâmetros e retorna um inteiro, contendo a soma destes dois parâmetros:

function sum(int a, int b): int
{
for (; a < b; a++) return a
}

Não há nada de errado com este código, exceto pelo fato de fazer pouco sentido fazer uma soma desta forma. Mas de toda forma, se chamarmos a função desta forma sum(10, 10) receberemos o valor 20, e se chamarmos assim sum(20, 30) receberemos o valor 50.

Alterar o código da função acima de forma que ao receber os mesmos parâmetros ela retornará os mesmos valores é uma refatoração. Vejamos:

function sum(int a, int b): int
{
return a + b
}

Porém alterar a assinatura da função, por exemplo, não é uma refatoração. Ou adicionar lógicas à ela. O seguinte snippet não pode ser considerado refatoração, porque muda a assinatura da função:

function sum(int a, int b): float
{
if (a < 0 || b < 0) throw 'Dont be so negative'
return parseFloat(a + b)
}

Ao alterar a função acima desta forma nós deixamos de somente refatorar o código e passamos a reescrevê-lo por conta de três alterações principais:

  1. O tipo de retorno mudou de int para float;
  2. Adicionamos uma lógica que lança exceção;
  3. Utilizamos a função parseFloat() que pode alterar fundamentalmente o valor esperado

Note também que "refatoração" depende muito do nível de abstração a partir do qual observamos. Uma reescrita a nível de função pode ser uma refatoração a nível de módulo.

Um ponto importantíssimo citado no livro fala sobre as oportunidades de corrigir um erro ou mal funcionamento do software. Corrigir um bug ou adicionar uma funcionalidade só porque "está fácil" ou na cara não é refatorar e deveria ser evitado.

👉  Refatorar é alterar um código sem mudar o seu comportamento observável.

Por que refatorar?

O livro desenvolve a ideia de que todo software perde qualidade com o tempo e sua arquitetura fica obsoleta. Alguns dos motivos eu descrevo abaixo:

  • O ambiente de desenvolvimento (linguagem de programação, framework, bibliotecas, infraestrutura...) evolui com o tempo e algumas decisões de arquitetura ficam obsoletas;
  • Ao adicionar código seja para corrigir bugs ou adicionar funcionalidades, nós adicionamos dependências e débito técnico que se acumula com o tempo

Em vez de estressar a arquitetura e design ao extremo até o ponto de não podermos mais adicionar nenhuma funcionalidade, Martin Fowler propõe que a gente melhore a qualidade de nossos softwares com frequência.

Refatoração nos permite evoluir e atualizar a arquitetura de design do software constantemente. É importante refatorar porque desta forma evitamos a degradação do design do software de forma gradual em vez de investir em grandes correções e reescritas.

Quando refatorar?

O livro explicita dois momentos essenciais para executar uma refatoração: antes e depois de escrever alguma alteração no código.

Refatoração antes de alterar o código

Quando se investiga um bug, refatorar aquele pedaço de código é uma técnica muito útil para entender o que aquele código faz. E quando é necessário cobrir o código com testes, fica muito mais fácil. Normalmente durante a refatoração, o bug e sua correção ficam mais claros.

Por extensão, quando se precisa entender melhor o que determinado trecho de código faz, a refatoração também é uma ferramenta incrível.

👉  Sempre que você precisar escrever um trecho novo de código, refatore antes.

Refatoração depois de alterar o código

Quando a sua atualização estiver pronta para ser entregue, é o momento perfeito para refatorar mais uma vez. Este momento é a sua oportunidade perfeita para revisar suas alterações e garantir que a próxima pessoa que visitar aquele código vai entender com facilidade o que foi escrito. Provavelmente vai ser você quem vai visitar este código da próxima vez de toda forma, seja gentil consigo 😉.

Como eu já comentei no meu artigo sobre estilo de código para iniciantes, um comportamento chave de toda pessoa desenvolvedora experiente é escrever código para pessoas em vez de para computadores.

Outras oportunidades para refatoração após escrever o código podem aparecer durante um Code Review.

O que testes têm a ver com refatoração?

A definição que o livro dá sobre refatoração é: alterar um código sem mudar o seu comportamento observável.

Eu só conheço uma forma de provar que o software não passou a se comportar de forma diferente: através de testes. De preferência, testes automatizados.

É essencial que um código esteja coberto com testes antes de passar por uma refatoração. Do contrário você pode estar introduzindo bugs silenciosos sem sequer saber.

👉  Se você não tinha cobertura de testes antes de refatorar, você não está fazendo uma refatoração mas sim uma reescrita.

Refatorar sempre, em pequenos passos

Em resumo, devemos refatorar sempre que possível. Refatorar antes de escrever um código para que este possa receber as nossas alterações sem muita dificuldade, e depois de escrever um código como quem dá um acabamento ao trabalho.

Como eu comentei no artigo sobre como evitar programar por coincidência, a refatoração é o mise en place da pessoa programadora. Não deveria ser opcional, mas sim parte do nosso fluxo normal de trabalho.

É importante notar que o code review pode ficar muito confuso quando muitas alterações são feitas por conta de refatoração. Minha recomendação é refatorar sempre em pequenos passos, e testar o software após cada passo. O ideal é fazer um commit para cada refatoração aplicada.

👉  Para cada refatoração, faça um commit. Isso te ajuda a se organizar e a organizar o Code Review mais tarde.

Críticas ao livro Refactoring, por Martin Fowler

Todos sabemos que Martin Fowler é uma referência internacional quando se fala de desenvolvimento de software, mas ninguém é infalível.

Aqui, eu deixo algumas críticas minhas sobre o livro: são pontos de divergência ou apenas comentários que eu tenho e acho importante prestarmos atenção.

Quando falamos de desenvolvimento de software, quase tudo depende. Então é importante manter estas críticas em mente para evitar dogmatismo em torno do tema.

Definição de refatoração ao pé da letra não dá

Como eu comentei anteriormente, refatoração pode acontecer em diferentes níveis de abstração. Alterar a assinatura de uma função é considerado reescrita naquele escopo, mas ao observarmos um módulo pode ser considerado refatoração.

Da mesma forma, é possível reescrever partes de um módulo enquanto fazemos uma refatoração a nível de API REST.

O importante aqui é sempre refatorar somente com cobertura de testes e a pequenos passos incrementais. Quanto maior o seu nível de abstração, mais importante é mover-se a passos pequenos.

O que eu devo dizer ao meu gerente?

O livro diz brevemente como você, enquanto pessoa desenvolvedora, deveria justificar o tempo gasto em refatoração ao gerente/cliente. Afinal, refatoração consome mais tempo do que simplesmente escrever a sua funcionalidade.

O livro nos instrui a simplesmente omitir a refatoração ao nosso cliente. "O que dizer? Nada!". A justificativa do livro é que a refatoração vai facilitar a preparação do ambiente para que você faça as alterações que precisa.

Enquanto eu concordo com a justificativa, eu discordo sobre precisarmos nos calar sobre o esforço de refatoração. Da mesma forma como um profissional de saúde precisa usar máscara e luvas, nós precisamos refatorar.

Como já eu escrevi antes, refatoração é o mese en place da pessoa programadora. Não há necessidade de justificar a necessidade de refatorar um código, mas é necessário falar sobre e encorajar outras pessoas a fazerem o mesmo.

Dados duvidosos

Apesar de o livro trazer uma retórica bem convincente sobre refatoração, motivos para adotar a prática e como convencer seus colegas, a amostragem de dados é no mínimo... duvidosa.

No capítulo 2, o autor mostra um gráfico onde compara o montante de funcionalidades entregues com o tempo em projetos com bom design vs. projetos com mal design. De acordo com o gráfico, projetos com bom design tem crescimento linear, projetos com mal design têm uma tendência quase constante de entregas.

Gráfico: funcionalidades x tempo, comparando bom design e mal design.
Gráfico: funcionalidades x tempo, comparando bom design e mal design.

Mas o autor não apresenta nenhum dado concreto ou explica como chegou àquele gráfico.

Pessoalmente eu não vejo muito problema sobre falta de dados, as práticas se justificam por si só. Mas na minha opinião, toda pessoa quando falar sobre engenharia de software, deveria evitar fazer afirmações desta natureza sem dados concretos e disponíveis.

O que fazer com códigos legados?

O autor não dá muita informação sobre como lidar com sistemas legados e indica o livro Working Effectively with Legacy Code.

Na minha opinião esta indicação é horrível. Este livro é terrível e induz quem lê a fazer exatamente o contrário de refatorar: dar manutenção ao mal design do sistema.

Eu recomendo a leitura crítica do livro Working Effectively with Legacy Code, não acho que devemos ignorar esta literatura. Mas tenho MUITAS ressalvas com este título e recomendo atenção dobrada ao aplicar as dicas descritas nele.

Um exemplo mais em linha com o livro Refactoring, na minha opinião, é o livro Modernizing Legacy PHP Applications do Paul M. Jones. Apesar de ser específico à aplicações PHP, o livro adota uma prática 100% em linha com a descrita pelo livro Refactoring: pequenas alterações, modernização do design e pragmatismo.

Meme: personagem rejeitando o livro "Working effectively with legacy code" e aprovando o livro "Modernizing Legacy Applications in PHP"

Eu acho especialmente estranha a indicação do livro Working Effectively with Legacy Code porque o Martin Fowler já escreveu sobre StranglerFigApplication antes, uma forma de evoluir o design da aplicação como um todo.

Desmistificação do argumento da performance

Eu achei bacana que o livro buscou desmistificar o argumento da performance a todo custo. Já vi muito code review ser recusado por códigos como o seguinte:

lista.map(...).map(...).filter(...)

A justificativa? O map itera a lista inteira, encadear dois maps é ruim para a performance a lista inteira vai ser percorrida duas vezes. E o filter ao final vai reduzir o número de itens na lista. Seria ideal colocar o filter primeiro para que a lista inteira seja menor antes do map.

Faz sentido, mas dependendo do tipo de dado que o código está lidando, não faz nenhuma diferença na prática. Performance importa, mas micro otimizações podem ser ignoradas em favor de legibilidade de código.

Mas é importante não levar isto como dogma (dogmas em geral são ruins, principalmente em desenvolvimento de software). Performance é importante e as vezes uma refatoração pode causar danos imensos em termos de performance.

Busque o equilíbrio. Mas não aceite um code review recusado que use performance como desculpa sem trazer dados. Se a discrepância de performance estiver na magnitude dos nanossegundos, quase sempre pode-se ignorar com segurança a recomendação de melhoria performance.

Uma refatoração pode desfazer a outra, e tudo bem

Algo que o livro menciona brevemente mas eu acho importante destacar é que as vezes uma refatoração vai desfazer o que outra refatoração fez antes. E isto é normal.

A refatoração é um mecanismo que te ajuda a preparar o ambiente para receber seu código. Se voltar ao estado anterior é melhor para receber seu código, então voltemos ao estado anterior sem medo ou vergonha.

Considerações finais

O livro Refactoring, do Martin Fowler, é um dos livros base para toda pessoa programadora. Se você puder levar um único parágrafo deste texto todo contigo, leve este:

Para refatorar, é essencial ter cobertura de testes. Refatore antes de alterar o software. Refatore depois de alterar o software. Encoraje outras pessoas a fazer o mesmo!

E, claro, leia o livro e tire suas próprias conclusões.

Neste artigo eu mencionei três livros. Aqui os links para cada um:

Até a próxima! 👋

 

Comentários