Pedro Sousa
← Artigos

Artigo · Blog

Você não refatorou o código. Você moveu o problema de lugar.

Refatorar não é reorganizar código. É mudar o comportamento interno sem mudar o comportamento externo, e a maioria das refatorações que acontecem em projetos reais não faz isso. Elas só trocam onde o problema mora.

Você não refatorou o código. Você moveu o problema de lugar.

Num app financeiro onde trabalhei, a tela de extrato tinha um ViewController com 900 linhas. Todo mundo sabia que estava errado. Todo mundo concordava que precisava de refatoração.

Três meses depois, o ViewController tinha 200 linhas. Mas o projeto tinha um TransactionViewModel com 700 linhas, um TransactionUseCase com 400 linhas, e um TransactionRepository que fazia parse de JSON, tratava erro de rede e ainda aplicava regras de negócio.

O código estava "limpo". A arquitetura estava "correta". E o problema original estava intacto, só redistribuído entre mais arquivos.

Isso não é refatoração. É reorganização com nomenclatura nova.


A ilusão da estrutura certa

A maioria dos refactors que eu vi ao longo de 21 anos de carreira resolve o problema errado.

O desenvolvedor vê um método com 150 linhas e extrai em 4 métodos menores. Cada método agora tem 30 linhas. O código ficou mais legível? Talvez. O problema foi resolvido? Depende do que era o problema.

Se o problema era responsabilidade misturada, extrair métodos sem redefinir responsabilidades só cria métodos menores com responsabilidades ainda misturadas.

Se o problema era dependência acoplada, mover código para outro arquivo sem quebrar a dependência só esconde o acoplamento atrás de uma camada de indireção.

Clean Architecture não resolve acoplamento. Ela oferece uma estrutura onde acoplamento é mais fácil de evitar. A diferença importa.

Eu também cai nessa armadilha. Na XP, quando a base de código cresceu junto com a base de clientes, o reflexo foi criar camadas. Use case aqui, repository ali, mapper no meio. A estrutura ficou bonita. Mas algumas dependências cruzavam camadas de formas que a arquitetura não deveria permitir, e ninguém percebia porque os arquivos estavam todos nos lugares certos.


O problema real por trás da maioria dos refactors mal feitos

Refatorar exige que você entenda o que o código está fazendo de verdade, não o que o nome do método diz que ele faz.

Esse é o passo que a maioria pula.

É mais rápido olhar pra um ViewController gordo, extrair responsabilidades aparentes em classes separadas, e declarar que está feito. É mais lento sentar com o código, entender cada decisão que foi tomada, descobrir por que aquela lógica bizarra está ali, e só então decidir o que mover e o que reescrever.

A diferença entre os dois é a diferença entre um refactor que dura e um refactor que cria o próximo problema.

Na OLX, quando assumi o módulo de Conta para elevar a cobertura de testes de 65% para 87%, o primeiro instinto foi reorganizar. Estrutura nova, arquivos menores, tudo separado direitinho. Mas o que realmente fez os testes funcionarem foi entender quais partes do código tinham estado implícito que ninguém tinha documentado. Reorganizar sem resolver isso teria produzido testes que testavam a estrutura, não o comportamento.


O padrão que eu chamo de Refatoração Cosmética

Você reconhece pelo sintoma: o pull request tem 40 arquivos modificados, a descrição diz "cleanup e organização", os testes continuam passando porque nenhum comportamento mudou de verdade, e três semanas depois o próximo desenvolvedor abre um bug que existia antes do refactor.

Refatoração Cosmética é quando você muda o que o código parece sem mudar o que o código faz.

Ela é sedutora porque é imediata. Você vê resultado. O diff parece impressionante. O código fica mais legível. Mas o problema de design, que era o que justificava o refactor, continua vivo.

O sinal mais claro de Refatoração Cosmética é quando, depois do refactor, você ainda não consegue testar uma unidade de lógica sem instanciar metade do sistema. Testabilidade é o proxy mais honesto de design. Se o design melhorou, testar ficou mais fácil. Se testabilidade não mudou, o design não mudou.


O que funciona na prática

A decisão mais efetiva que aprendi a tomar antes de qualquer refactor é simples: escreva o teste que você quer poder escrever depois.

Não o teste que você consegue escrever hoje. O teste que descreveria o comportamento que essa unidade deveria ter, de forma isolada, sem dependência de infraestrutura.

Se você não consegue nem escrever esse teste antes de refatorar, você ainda não entendeu o problema o suficiente para resolvê-lo.

Isso parece TDD mas não é exatamente TDD. É usar o teste como ferramenta de design antes de tocar no código de produção. O teste revela acoplamento que a leitura do código esconde.

Na prática, o que durou nos projetos que participei foi refatoração em pequenos passos verificáveis. Não reescrever, não reorganizar em massa. Um comportamento isolado por vez, com teste cobrindo antes de mover.

O refactor que levei mais a sério na minha carreira foi numa migração de VIPER para MVVM num módulo de reputação na OLX. O que salvou a migração foi ter testes de comportamento antes de começar. Eles diziam se alguma coisa havia quebrado, não se a estrutura estava bonita.


O trade-off honesto

Refatorar devagar, com testes, em passos pequenos, é mais lento no curto prazo. Às vezes muito mais lento.

Num time com pressão de entrega, um sprint inteiro gasto em refatoração sem feature nova é difícil de justificar. E às vezes a Refatoração Cosmética é a escolha certa porque o código vai ser descartado em seis meses de qualquer forma.

O problema é quando você aplica Refatoração Cosmética em código que vai durar anos, e chama isso de melhoria de arquitetura.

A decisão que sobrevive ao tempo não é a que usa o pattern mais sofisticado. É a que resolve o problema certo com a complexidade mínima necessária.

Eu já vi sistema com Clean Architecture impecável que era impossível de manter porque cada feature nova exigia tocar em seis camadas. E já vi código sem nome de arquitetura nenhum que sobreviveu dez anos porque as dependências estavam sob controle.


O que eu faria diferente hoje

Quando eu era mais novo, refatorava pela estrutura. Movia código para os lugares arquiteturalmente corretos e achava que estava feito.

Hoje eu refatoro pela testabilidade e pela capacidade de mudança. A pergunta não é "esse código está no lugar certo?". A pergunta é "se esse comportamento precisar mudar amanhã, quantos arquivos eu preciso abrir?".

Se a resposta for mais de dois ou três, o design tem um problema, independente de quantas camadas estão bem nomeadas.


A maioria dos refactors não falha porque o desenvolvedor escolheu o pattern errado. Falha porque o desenvolvedor reorganizou o que era visível sem resolver o que era invisível.

Código limpo é consequência de dependências sob controle. Não o contrário.

E toda vez que você termina um refactor sem conseguir testar uma unidade de lógica de forma isolada, o problema ainda está lá. Agora só mora num endereço diferente.