Joshua Kerievsky iniciou uma discussão no grupo Refactoringdo Yahoo! com o seguinte post:
Nos últimos anos tenho ouvido algumas pessoas dizerem que só se deve refatorar quando se está trabalhando em uma história com esse propósito. Eu nunca concordei com essa noção, pois penso que há momentos em que você simplesmente precisa pagar parte do débito técnico. Durante vários dias, eu e os meus colegas temos refatorado nosso código de eLearning. Não há nenhum história associada a essa atividade. Nós simplesmente estávamos com um débito técnico maior do que gostaríamos, e consideramos agora como uma boa hora para pagá-lo. Havia um Singleton pernicioso que desempenhava um papel central no nosso código e agora estamos eliminando-o, porque isso irá permitir muito mais melhorias de design no nosso código. Nos sentimos bem fazendo esse trabalho. Ele irá nos deixar numa posição muito mais fácil para trabalhar nas novas histórias.
Entretanto, nós continuamos entregando algo toda semana - poucas correções e muitas refatorações. Como sempre, os nossos microtestes automatizados e testes de história nos ajudam a ter bastante confiança e coragem.
Enfim, eu gostaria de compartilhar isso, uma vez que pode levar a uma discussão interessante.
Dale Emery tentou se esclarecer sobre o contexto em que Josh estava inserido:
Dale: Eu suspeito que os conselhos gerais existem para desencorajar o pessoal técnico a tomar decisões de negócio. A decisão de pagar débito técnico exige uma sólida compreensão dos impactos técnicos e no negócio. Se os tomadores de decisão não tem um conhecimento sólido de ambos, a decisão torna-se pouco confiável. O seu é um caso especial em que esse perigo é reduzido.
Josh: Sim, o nosso caso é especial. No entanto, eu diria que geralmente é uma coisa muito boa que o pessoal técnico e de negócios trabalhem em estreita colaboração, de tal forma que este tipo de decisão relacionada a débito técnico seja comum a todos. E sim, nós não queremos incentivar os desenvolvedores a simplesmente decidir gastar várias semanas refatorando sem nenhuma que haja uma decisão conjunta dentro da comunidade relacionada ao projeto.
Dale: Parece-me (corrija-me se as minhas hipóteses estiverem erradas) que seu cliente é altamente técnico, e muitos, se não a totalidade, do seu pessoal técnico compreende o seu negócio a fundo. Além disso, o seu cliente é você. Quando você toma a decisão de pagar o débito técnico, você está fazendo isso com pleno conhecimento do impacto no negócio e, provavelmente, devido ao seu pleno conhecimento do impacto no negócio.
Josh: Sim, a decisão de pagar o débito técnico agora, é impulsionada por
- timing - acabamos de entregar um release muito importante para o nosso maior cliente, e é hora de sair um pouco do "trem das funcionalidades".
- futuro - temos mais funcionalidades por vir e uma experiência prática com este código diz que o débito técnico só nos deixará mais lento.
- linguagem ubíqua - nós temos uma metáfora musical maravilhosa no nosso código e ... ainda há alguns remanescentes da nossa metáfora anterior (livros) espalhadas pelo código.
Com esse entendimento do contexto de Josh (e, mais ainda, ao longo de toda essa longa discussão) Adam Sokra sugeriu:
Então. Você não está trabalhando com iterações fixas. Você desenvolve incrementalmente e lança com a maior frequência possível. Às vezes você tem um conjunto de histórias nas quais você está trabalhando, e tenta entregá-las rapidamente aos clientes. Outras vezes você está apenas tentando melhorar o design do que você tem. Você é tanto o cliente quanto um programador.
Isto parece muito com os bons projetos open source que já encontrei, e muito pouco com qualquer projeto Scrum ou XP. Eu não acho que haja algo de errado com o que você está fazendo, mas não sei se é muito útil para as pessoas que estão tentando entender como fazer refatoração em um contexto ágil.
Um dos principais conceitos de Scrum e XP é que estamos num jogo das necessidades de pessoal de negócios (não-técnicas) contra as necessidades da time (técnicas). Queremos ter certeza de que as coisas técnicas são feitas de forma proficiente, mas também queremos maximizar o controle que o negócio tem sobre o que é produzido, e quando é produzido.
O que você está descrevendo é um ambiente em que esta dicotomia não existe. Você é livre para decidir quais funcionalidades você deseja adicionar, quando você deseja adicioná-las, e quando fazer uma pausa na entrega de funcionalidades para concentrar-se em questões puramente técnicas.
Então, Adam sugeriu que o contexto de Josh é bem diferente da maioria dos projetos; sendo a diferença mais importante a ausência da disputa de comunicação, compreensão, e prioridades nos aspectos técnicos e não técnicos.
Ron Jeffries sugeriu que a quantidade de refatoração que devemos fazer é uma decisão de negócios. Refatoração é um investimento que não tem valor de imediato. Ele também discorda de que seja uma decisão binária: "não refatorar nada" ou "parar e refatorar":
Há uma hipótese aqui que precisa ser explicitada: a de que, de alguma forma, às vezes é melhor parar ou "avançar lentamente" para limpeza.
Parece óbvio para várias pessoas que tal coisa pode acontecer, que o código pode ficar tão ruim que a única opção que se tem é limpá-lo, após interromper ou reduzir o progresso na criação de funcionalidades.
Isso não é óbvio para mim. Os números não batem. Quando limpamos o código, o retorno disso só vai ser visto no futuro. Alguns podem ver esse retorno amanhã, e alguns podem não ver esse retorno por semanas ou meses. Em nenhum caso, o retorno é visto de imediato.
Toda refatoração que atrasa a evolução das funcionalidades é um investimento no futuro. O que precisa ser descoberto é se, como e quando, esse investimento vale a pena.
Ron então sugere uma maneira de determinar quando a refatoração é um investimento que vale a pena:
Quando é melhor [refatorar], e por quê? Existem muitos caminhos possíveis para o futuro, que levam ao aumento do valor das funcionalidades ao longo do tempo. Podemos descrever dois deles:
1. Não refatorar. O valor das funcionalidades cresce cada vez mais lentamente, talvez até mesmo comece a diminuir quando a injeção de erros superar a injeção de funcionalidades.
2. Parar o desenvolvimento de funcionalidades e refatorar. O valor das funcionalidades PARA DE CRESCER, por um tempo. Depois, ele começa a crescer novamente. Assumimos que, uma vez que agora o código está melhor, vai crescer mais rapidamente do que antes. No entanto, a necessidade de funcionalidades aumenta, e haverá um intervalo antes de compensar o tempo gasto na refatoração. Depois disso, assumimos que vamos começar a desenvolver mais rapidamente do que se não tivéssemos feito nenhuma refatoração.
Então o que podemos concluir, comparando esses dois casos? Em primeiro lugar, a decisão de não refatorar resulta no lançamento de mais funcionalidades por algum tempo, mas em menos funcionalidades no longo prazo. Em segundo lugar, para saber quando começa a valer a pena refatorar, precisamos saber alguns números: quanto tempo vai levar a refatoração, e qual será o seu impacto na velocidade? E, quanto tempo levará até que o código fique ruim novamente, o que por sua vez resultará na redução da velocidade?
É inteiramente possível parar, refatorar, atrasar algumas funcionalidades, e acabar piorando o código, ficando em um loop eterno, sem nunca perceber nenhum benefício. Esperamos que isso seja improvável ... e é, se as pessoas forem suficientemente qualificadas ... o que faz parte do meu ponto acima, que o seu conselho é bom para os experts.
No entanto, estes dois pontos finais mostram que a própria estratégia de parar e refatorar pode falhar. Existe outra estratégia que pode funcionar melhor? Existe.
Vamos imaginar uma espécie de "acelerador de refatoração", AR. No caso 1, foi de AR=0, não refatorar. No caso 2, foi setado para AR=1. O que acontece com as configurações entre esses dois valores?
Antes de tudo, como o valor das funcionalidades se dá em função do AR? Existe algum número x, com 0 < x <1 tal que, se AR < x, o aumento do valor das funcionalidades sempre diminui. Nós não estamos refatorando suficientemente, o código se deteriora, perdemos cada vez mais. Com AR = x, o aumento do valor das funcionalidades permanece constante. Nós não aumentamos, nem diminuímos a velocidade.
Agora, se estabelecermos o acelerador de refatoração acima de x, RA> x, o que acontece? Podemos ir mais rápido, ou será que sempre ficaremos mais lentos? Sabemos que em um ponto, RA = 1, a velocidade vai para zero (mas podemos acelerar mais tarde).
A resposta depende do formato da curva da velocidade em função da limpeza do código. Sabemos que uma limpeza conduz a uma maior velocidade, e (eu acho) sabemos que os primeiros esforços para limpeza tem um bom efeito proporcional, enquanto os esforços nos "detalhes de polimento" não acrescentam muito.
O que eu acredito que pode acontecer ... e não há absolutamente nenhuma prova de que não pode acontecer ... é que, deixando o AR apenas um pouco acima de x, aumentamos cada vez mais a criação de novas funcionalidades. Se isso for verdade, então esta estratégia vai entregar valor uniformemente mais rápido do que a estratégia de parar para refatorar.
Portanto, se isto for verdade - e eu tenho convicção de que seja - a estratégia de parar para refatorar nunca é o melhor um para um time experiente em refatoração.
O ponto-chave que Ron coloca aqui é que a estratégia de "parar e refatorar" NUNCA é a melhor escolha para um time competente em refatoração.
Este longo relatório é apenas um vislumbre de um debate muito envolvente. Esta não é uma nova questão, como Josh explica no início da conversa. Na realidade, este repórter escreveu um editorial sobre o assunto chamado Refatoração é um desperdício necessário dois anos atrás, e o tema de refatoração foi coberto pela InfoQ em múltiplas ocasiões. A comunidade não chegou a um consenso sobre este tema.