2.19 Refatoração e dívida técnica
Visão geral e motivação
Refatorar é mudar a estrutura interna do código sem mudar o que ele faz por fora. Você renomeia uma variável, divide uma função longa, extrai uma classe, colapsa um emaranhado de condicionais em algo que um leitor consegue seguir, e o programa se comporta exatamente como antes. Essa última parte é toda a disciplina. A refatoração preserva o comportamento por definição, e no momento em que você também muda o comportamento deixa de estar refatorando: está fazendo duas coisas arriscadas ao mesmo tempo e escondendo cada uma atrás da outra. Este capítulo trata esses dois atos como separados de propósito, porque a confusão entre eles é onde a maioria das refatorações dá errado.
Numa equipe grande isso importa mais do que para um desenvolvedor solitário, porque o código que você está limpando é um código que centenas de outras pessoas leem, de que dependem e que têm medo de tocar. A refatoração é como uma base de código compartilhada continua habitável ao longo de anos e da rotatividade de pessoal. Ela se liga diretamente à construção de software (capítulo 2.9), onde se define a qualidade da codificação do dia a dia, e à estratégia de testes (capítulo 2.4), que é a rede de segurança que torna a refatoração segura de modo geral. Liga-se também à questão mais difícil da dívida técnica: o custo acumulado de atalhos, designs envelhecidos e limpeza adiada que tornam toda mudança futura mais lenta. A refatoração é a principal forma de pagar essa dívida, então os dois temas pertencem a um só capítulo.
Em contextos corporativos e governamentais o que está em jogo sobe. Esses sistemas são de longa vida, muitas vezes com décadas, e frequentemente sujeitos a regimes de auditoria e de controle de mudanças que tratam qualquer mudança de código como um evento governado. Você não pode simplesmente reescrever um sistema de benefícios para cidadãos num fim de semana prolongado. Você o moderniza em passos pequenos, reversíveis e baseados em evidências, que é exatamente o que a refatoração disciplinada oferece. Coordenar esse trabalho entre muitas equipes e sistemas de longa vida (capítulo 10.4) é um dos desafios definidores da engenharia em larga escala, e errar nisso é como as organizações acabam congeladas, incapazes de mudar um software que já não entendem.
Princípios fundamentais
- A refatoração preserva o comportamento. Se você está mudando o que o código faz, isso é uma mudança separada, feita separadamente.
- Uma suíte de testes confiável é a pré-condição para uma refatoração segura, não um extra opcional.
- Trabalhe em passos pequenos, nomeados e reversíveis, e mantenha o código funcionando depois de cada um.
- Torne a dívida técnica visível e acompanhada e depois financie o pagamento como capacidade estável e não como heroísmo.
- Refatore o código que você já está mudando, onde a limpeza compensa.
- Nem toda dívida vale a pena pagar. O código estável, raramente tocado ou prestes a ser aposentado pode ser deixado em paz.
- Meça a qualidade interna para informar o julgamento, nunca como meta a ser manipulada.
Recomendações
Mantenha a refatoração e a mudança de comportamento estritamente separadas
Decida antes de começar qual dos dois você está fazendo e nunca misture os dois num único commit. Quando você refatora, os testes que passavam antes devem passar depois, inalterados, porque o comportamento observável não se moveu. Quando você muda o comportamento, faça isso como seu próprio commit com seus próprios testes. A razão é prática: se uma mudança mista quebra algo, você não consegue dizer se a sua reestruturação introduziu o bug ou a sua mudança de comportamento, e na revisão de código (capítulo 2.5) um revisor não consegue raciocinar de forma limpa sobre nenhuma das metades. O hábito que funciona é a regra dos dois chapéus de Martin Fowler: você está sempre usando o chapéu da refatoração ou o chapéu da funcionalidade, sabe qual e troca deliberadamente. Commits separados também tornam legível o histórico do controle de versões, de modo que um engenheiro que faz a bisseção de uma falha possa pular com confiança os commits de refatoração pura.
Estabeleça uma rede de segurança confiável antes de reestruturar
Refatorar sem testes é só editar e torcer. Antes de reestruturar qualquer coisa de consequência, você precisa de uma suíte em que confie para pegar uma mudança de comportamento se você a causar, que é o argumento central da estratégia de testes (capítulo 2.4). Para código que já tem boa cobertura, rode os testes, refatore em passos pequenos e rode-os de novo depois de cada passo. Para código legado sem testes, a jogada honesta é escrever primeiro testes de caracterização. Um teste de caracterização não afirma o que o código deveria fazer. Ele captura o que o código de fato faz agora, incluindo suas esquisitices, de modo que qualquer mudança de comportamento apareça como um teste que falha. Michael Feathers popularizou essa abordagem para exatamente a situação em que as grandes organizações vivem: código que funciona, importa e não tem testes. Depois que o comportamento atual está fixado, você pode refatorar por baixo dele com segurança e só então mudar o comportamento por cima.
Aprenda a reconhecer code smells e a aplicar pequenas refatorações nomeadas
Um code smell é um sinal de superfície de que algo por baixo pode precisar de atenção: uma função que cresceu demais, uma classe que sabe demais, lógica duplicada, uma lista longa de parâmetros, nomes que mentem sobre o que fazem. Um smell é uma dica, não um veredicto, então você investiga em vez de obedecer às cegas. A resposta é uma pequena refatoração nomeada do catálogo de Fowler: Extrair Função, Renomear Variável, Mover Método, Substituir Condicional por Polimorfismo e dezenas de outras. O valor de usar movimentos nomeados é que cada um é pequeno, compreendido, mecanicamente seguro e muitas vezes apoiado diretamente pela sua IDE. Você compõe grandes melhorias com muitos passos minúsculos e confiáveis, mantendo o código verde o tempo todo, em vez de dar um grande salto que não consegue verificar.
Prefira a refatoração oportunista e reserve campanhas para a necessidade estrutural real
A maior parte da refatoração deve ser oportunista, incorporada ao trabalho que você já está fazendo. A regra do escoteiro a resume: deixe o código um pouco mais limpo do que o encontrou. Quando você toca um arquivo para acrescentar uma funcionalidade ou corrigir um bug, já entende aquele canto, e pequenas limpezas ali se acumulam com o tempo sem precisar da permissão de ninguém nem de um orçamento separado. As campanhas planejadas de refatoração, em que uma equipe para o trabalho de funcionalidades para reestruturar uma grande área, às vezes são necessárias, mas são caras, difíceis de agendar diante da pressão de produto e arriscadas se a área é mal testada. Reserve as campanhas para problemas estruturais que a limpeza oportunista não alcança e defenda o caso com evidências sobre o custo de mudança que você está pagando. Prefira o gotejar constante de pequenas limpezas: é mais durável que a ocasional reescrita heroica.
Use o padrão figueira-estranguladora para grandes mudanças estruturais
Quando um subsistema inteiro precisa ser substituído, não tente uma reescrita em big-bang que corre por um ano e se integra no fim: é assim que os projetos de modernização morrem. Use o padrão figueira-estranguladora, nomeado por Martin Fowler a partir da trepadeira que cresce ao redor de uma árvore e gradualmente a substitui. Você põe uma fachada na frente do sistema antigo, encaminha uma fatia de funcionalidade por vez para código novo atrás dessa fachada, verifica em produção e repete até o sistema antigo estar totalmente cercado e poder ser removido. Cada fatia é pequena, entregável e reversível, então o risco permanece limitado e o valor chega continuamente. Um primo próximo, a ramificação por abstração, faz o mesmo dentro de uma única base de código: você introduz uma camada de abstração sobre aquilo que quer substituir, constrói a nova implementação atrás dela enquanto as duas coexistem, migra os consumidores gradualmente e apaga a implementação antiga quando nada mais depende dela. Ambos permitem que um sistema legado evolua continuando vivo, que é o único tipo de modernização que a maioria das grandes organizações consegue de fato bancar.
Trate a dívida técnica como um portfólio e torne-a visível
A metáfora da dívida, cunhada por Ward Cunningham, separa duas coisas: o principal (o próprio código bagunçado ou atalho) e os juros (o esforço extra que toda mudança futura paga por causa dele). Nem toda dívida é igual. O quadrante de Fowler a classifica em dois eixos: deliberada versus inadvertida, e prudente versus imprudente. A dívida prudente-deliberada (“entregamos agora e limpamos na próxima sprint, e sabemos o custo”) é uma decisão de negócio legítima. A dívida imprudente-inadvertida (“o que é um padrão de projeto?”) é só dano. O trabalho de gestão, que se liga à tomada de decisão e à governança (capítulo 1.5) e ao seu tratamento da dívida como portfólio, é tornar a dívida visível para que possa ser raciocinada: acompanhe os itens significativos onde o trabalho vive, etiquete o código e registre os juros que você está pagando para que a quitação dispute capacidade com base em evidências e não em quem reclama mais alto. A dívida que você não vê, você não consegue gerenciar.
Financie a quitação como capacidade estável, não como heroísmo
O modo de falha é tratar a limpeza como algo que você fará “quando as coisas se acalmarem”, o que nunca acontece. O padrão durável é uma capacidade fixa e protegida para a quitação: uma fatia explícita de cada ciclo, ou um acordo permanente de que a limpeza acompanha o trabalho de funcionalidades na mesma área. O que não funciona é a sprint heroica periódica em que alguém queima um fim de semana para consertar tudo, porque é insustentável, não revisada e costuma se desfazer sozinha. A capacidade estável mantém baixos os pagamentos de juros e evita o ciclo de expansão e queda em que a dívida se acumula até uma crise forçar uma reescrita cara. Isso é tanto um compromisso de gestão quanto uma prática de engenharia, e pertence a como você planeja a manutenção de software (capítulo 3.7) ao longo da vida de um sistema.
Meça a qualidade interna, mas não deixe a medida virar a meta
Você pode medir a qualidade interna com sinais como a complexidade ciclomática (uma contagem de caminhos independentes numa função), a duplicação, a cobertura de testes, a taxa de falha de mudanças e quanto tempo as mudanças levam nas áreas que você suspeita. Esses números são úteis para identificar onde a dívida se concentra e para acompanhar uma tendência ao longo do tempo. O perigo é a lei de Goodhart: quando uma medida vira meta, deixa de medir qualquer coisa real. Imponha um número de cobertura e você obtém testes que não afirmam nada. Recompense pontuações baixas de complexidade e você obtém lógica espalhada por mais funções para driblar a métrica. Use as métricas para iniciar conversas e localizar pontos quentes e nunca ligue uma métrica de qualidade a um portão que as pessoas têm motivação para manipular.
Saiba quando não refatorar
A refatoração é um investimento, e algum código nunca o devolverá. Se um módulo é estável, raramente tocado e entendido o bastante para ser mudado nas raras ocasiões em que é preciso, limpá-lo é esforço gasto por juros que você não estava pagando. Se o código está destinado à aposentadoria, refatorá-lo é polir algo que você está prestes a jogar fora. A disciplina é gastar o seu orçamento de limpeza onde a mudança é frequente e dolorosa, que é onde reduzir os juros de fato se acumula, e deixar em paz os cantos quietos.
Compromissos: prós e contras
| Abordagem | Prós | Contras |
|---|---|---|
| Refatoração oportunista (regra do escoteiro) | Barata, contínua, sem orçamento separado, acumula ao longo do tempo | Cobertura desigual. Os arquivos quentes melhoram enquanto os frios apodrecem |
| Campanha planejada de refatoração | Corrige problemas estruturais que a limpeza não alcança | Cara. Compete com funcionalidades. Arriscada sem bons testes |
| Figueira-estranguladora / ramificação por abstração | Incremental, reversível, mantém o sistema no ar, limita o risco | No papel, mais lenta que uma reescrita. Exige disciplina para terminar |
| Reescrita em big-bang | Folha em branco. Sem restrições legadas | Alta taxa de fracasso. Longo tempo até o valor. Lacunas de comportamento |
| Dívida prudente deliberada | Entrega valor agora. Pagamento explícito e planejado | Vira imprudente se o pagamento nunca é agendado |
| Qualidade controlada por métricas | Objetiva, visível, pega a deriva cedo | Convida à manipulação. Pune a nuance. Pode degradar a qualidade real |
A tensão central é velocidade agora versus capacidade de mudança depois, e é real. Entregar um atalho pode ser a decisão certa quando o prazo é genuíno e a dívida é prudente e acompanhada. O erro é fingir que a dívida é gratuita, ou deixá-la se acumular de forma invisível até o sistema ficar caro demais para mudar. Resolva isso tornando a troca explícita todas as vezes: nomeie a dívida, estime os juros, decida deliberadamente e registre a decisão para que a quitação possa ser agendada e não esquecida. Uma equipe que toma emprestado conscientemente e paga com constância continua rápida por anos. Uma equipe que toma emprestado às cegas para.
Perguntas para discutir com sua equipe
Como evitamos que a refatoração e a mudança de comportamento vazem para o mesmo commit, e a nossa revisão de fato impõe isso? Esta é a disciplina fundamental do capítulo inteiro, e é a mais violada sob pressão de prazo, porque parece eficiente “aproveitar para limpar isto enquanto estou aqui” e entregar tudo junto. O custo cai depois: quando um commit misto quebra a produção, ninguém consegue dizer se foi a reestruturação ou a funcionalidade que o causou, e uma bisseção pelo seu histórico deixa de ser confiável. Leve um punhado de pull requests recentes e verifique com honestidade quantos misturaram os dois chapéus. A consideração concorrente é o atrito, já que dividir o trabalho em commits separados dá um pouco mais de esforço de início. A resposta deve moldar suas convenções de commit e a sua lista de verificação de revisão.
Onde está a nossa dívida técnica, quanto de juros estamos pagando sobre ela e quem decide o que é quitado? A maioria das equipes não consegue responder a isso, e esse é o problema real, porque a dívida que você não vê é gerida por quem reclama mais alto e não por onde o custo realmente está. Torná-la visível significa acompanhar os itens significativos, etiquetar o código e reunir evidências sobre quais áreas tornam as mudanças lentas e propensas a falhas. A atração concorrente é que cada hora gasta com quitação é uma hora não gasta em funcionalidades, então a decisão precisa ser uma decisão de portfólio tomada com a liderança, ligando-se a como vocês governam o trabalho de engenharia (capítulo 1.5). Leve os seus dados de falha de mudanças e a lista dos arquivos que todos temem tocar. A resposta deve virar uma capacidade de quitação protegida e estável, não uma vaga intenção de limpar quando as coisas se acalmarem.
Quais partes da nossa base de código devemos deliberadamente não refatorar, e como saberíamos? Refatorar tudo é um fracasso tanto quanto refatorar nada, porque o esforço gasto limpando código estável, raramente tocado ou prestes a ser aposentado são juros pagos sobre um empréstimo que você não devia. O julgamento é real: um módulo pode parecer feio e ainda assim ser o lugar errado para investir se ninguém jamais o altera. Leve os seus dados de frequência de mudança ao lado dos sinais de complexidade, porque a interseção de alta rotatividade e alta complexidade é onde a limpeza se acumula, enquanto o código de baixa rotatividade geralmente é melhor deixado em paz. O risco concorrente é que “vamos deixar assim” vire desculpa para nunca tocar em nada difícil. A resposta deve dar a vocês uma lista explícita de pontos quentes que valem investimento e a permissão de ignorar os cantos quietos.
Confiamos de fato na nossa suíte de testes o bastante para refatorar o código que mais precisamos mudar, e onde teríamos de escrever testes de caracterização primeiro? Uma rede de segurança em que você não confia transforma a refatoração em editar e torcer, e numa equipe grande o código mais assustador costuma ser o menos testado, que é exatamente onde a limpeza mais compensaria. Leve os dados de cobertura e de falha de mudanças dos seus pontos quentes e seja honesto sobre quais módulos críticos não dariam nenhum aviso se uma reestruturação mudasse o comportamento. A consideração concorrente é que escrever testes de caracterização para código legado é um trabalho lento e pouco glamoroso que não entrega funcionalidade, então é fácil adiá-lo para sempre. Em sistemas corporativos e governamentais sob auditoria e controle de mudanças, esses testes fixados são também a evidência de que uma mudança preservou o comportamento, então financiá-los é ao mesmo tempo uma medida de segurança e de conformidade. A resposta deve nomear quais áreas recebem um arcabouço de testes antes de alguém tocá-las.
Quando um subsistema genuinamente precisa ser substituído, como decidimos entre uma abordagem incremental de figueira-estranguladora e uma reescrita, e quem tem autoridade para dizer não à reescrita? A reescrita em big-bang é a opção mais sedutora e mais propensa a fracasso sobre a mesa, porque uma folha em branco sempre parece mais barata no papel do que conviver com as restrições antigas. Para uma grande organização, o caminho incremental (uma fachada, uma fatia por vez, verificada em produção) mantém o sistema vivo e limita o risco, mas é mais lento, exige disciplina para terminar e compete com o apetite por um recomeço. Leve o mapa de frequência de mudança do subsistema, uma estimativa honesta de quanto tempo uma reescrita correria antes de entregar valor e as lacunas de comportamento que uma reescrita paralela teria de fechar. Em contextos governamentais e regulamentados, uma reescrita de vários anos que se integra no fim raramente sobrevive à auditoria, então a resposta deve adotar por padrão a figueira-estranguladora ou a ramificação por abstração e tratar qualquer reescrita como exceção que precisa ser defendida com evidências.
Como usamos métricas de qualidade interna para encontrar onde a dívida se concentra sem deixar que um número vire uma meta que as pessoas manipulam? Métricas como complexidade, duplicação, cobertura e taxa de falha de mudanças são a única forma de uma grande organização enxergar através de um código que nenhuma pessoa lê por inteiro, e ainda assim, no momento em que uma delas é ligada a um portão ou a uma avaliação de desempenho, a lei de Goodhart assume e o número deixa de medir qualquer coisa real. Leve exemplos de onde uma métrica já conduz o comportamento e pergunte se ela está iniciando conversas ou premiando em silêncio testes que não afirmam nada e lógica espalhada por funções para driblar um limite. A atração concorrente é que a liderança quer um número simples de painel, e “use o julgamento” é uma venda mais difícil que uma barra verde. Em contextos corporativos e governamentais em que as métricas alimentam relatórios de governança, deixem explícito que os sinais de qualidade informam o investimento e localizam pontos quentes mas nunca servem de portão para indivíduos. A resposta deve traçar uma linha firme entre medir para aprender e medir para julgar.
Perspectiva por setor
Startup. Com um punhado de engenheiros e nenhuma pista sobrando, refatore apenas de forma oportunista: use um chapéu por commit para que o histórico continue bissectável e mantenha uma lista curta e honesta dos atalhos que você tomou de propósito. Não lance campanhas de limpeza nem faça polimento de módulos estáveis. Gaste a sua escassa atenção no arquivo que todos temem e escreva testes de caracterização apenas onde uma mudança de fato assusta. A dívida deliberada e visível é aceitável nesse estágio. A dívida imprudente e invisível é o que mata você.
Pequena empresa. Sem especialista dedicado em plataforma ou ferramentas e com orçamento apertado, apoie-se no que a sua IDE e o ecossistema da linguagem oferecem de graça: movimentos automáticos de renomear e extrair, um linter e um sinal básico de cobertura. Trate a maior parte da dívida como algo que você gerencia no curso do trabalho normal e não como algo que contrata um consultor para consertar, e prefira comprar bibliotecas bem mantidas a construir e depois ter de refatorar as suas. Reserve o raro esforço pago para o único sistema cuja lentidão está custando clientes diretamente.
Grande empresa. Entre muitas equipes, o problema é a governança de portfólio: um registro compartilhado de dívida, etiquetagem consistente dos pontos quentes por frequência de mudança e complexidade e uma fatia protegida da capacidade de cada equipe para a quitação, de modo que a limpeza deixe de perder para as funcionalidades por padrão. Padronize a disciplina dos dois chapéus e a prática de testes de caracterização para que qualquer engenheiro que transite entre equipes encontre as mesmas regras e use a figueira-estranguladora e a ramificação por abstração para mudanças estruturais coordenadas entre grupos. Mantenha as métricas de qualidade informativas para que localizem a dívida sem serem manipuladas nas avaliações de desempenho.
Governo. Sistemas de longa vida sob auditoria rigorosa e controle de mudanças fazem da refatoração disciplinada um ativo de conformidade, e não apenas de engenharia: manter a reestruturação estritamente separada da mudança de comportamento permite aos auditores ver exatamente quais commits alteraram o comportamento e quais apenas arrumaram. As regras de contratação e de transparência favorecem passos pequenos, reversíveis e baseados em evidências em vez de reescritas em big-bang, então adote por padrão a figueira-estranguladora com testes de caracterização documentando que o comportamento é preservado. Faça do registro de dívida e do seu plano de quitação parte do registro de manutenção do sistema para que os órgãos de supervisão recebam a rastreabilidade de que precisam.
Exemplos
Startup. Uma startup de seis pessoas entrega rápido e sabe que está assumindo dívida, então faz duas coisas baratas bem. Todo pull request usa um chapéu: os commits de refatoração são separados dos de funcionalidade, o que mantém o histórico deles bissectável mesmo em alta velocidade. E eles mantêm uma lista curta e honesta dos atalhos que tomaram de propósito, com uma nota de uma linha sobre os juros que cada um custa. Quando um módulo de pagamentos vira o arquivo que todos temem, essa lista mais o histórico de falha de mudanças defende o caso de gastar dois dias extraindo uma fronteira mais limpa. Eles escrevem testes de caracterização para fixar o comportamento atual, refatoram por baixo com os movimentos de renomear e extrair da IDE e nunca tocam nos módulos estáveis que ninguém muda. A dívida que carregam é deliberada e visível, então nunca vira do tipo imprudente.
Grande empresa. Uma empresa global de logística opera um sistema de pedidos de quinze anos que muitas equipes mudam toda semana. Em vez de uma reescrita, adotam o padrão figueira-estranguladora: uma fachada fica na frente do monólito, e uma capacidade delimitada por vez é redirecionada para novos serviços atrás dela, verificada em produção antes de a próxima fatia começar. Coordenar isso entre equipes e um sistema de longa vida (capítulo 10.4) é a parte difícil, então mantêm um registro compartilhado de dívida, etiquetam pontos quentes por frequência de mudança e complexidade e reservam uma fatia fixa da capacidade de cada equipe para a quitação. As métricas de qualidade interna informam onde olhar mas nunca servem de portão para a avaliação de desempenho de ninguém, o que mantém os números honestos. Em dois anos o monólito encolhe com constância e nenhuma mudança isolada jamais arrisca o sistema inteiro.
Governo. Uma agência tributária nacional precisa modernizar uma plataforma de apuração de décadas sob regras rigorosas de auditoria e de controle de mudanças, em que toda mudança de código é um evento governado e baseado em evidências. Uma reescrita em big-bang é impossível, então eles usam a ramificação por abstração: uma camada de abstração é introduzida sobre o motor de cálculo legado, uma nova implementação é construída atrás dela e os consumidores são migrados uma regra tributária por vez, cada migração documentada como uma pequena mudança reversível com testes de caracterização provando que o comportamento permanece inalterado. Como a refatoração é mantida estritamente separada de qualquer mudança de comportamento legislativo, os auditores conseguem ver exatamente quais commits alteraram o comportamento e quais apenas reestruturaram. O registro de dívida e o seu plano de quitação passam a fazer parte do registro de manutenção do sistema (capítulo 3.7), dando aos órgãos de supervisão a rastreabilidade de que precisam.
Justificativa de negócio: motivações, ROI e TCO
O retorno da refatoração e da quitação de dívida é a capacidade sustentada de mudar o software de forma barata, e na maioria dos sistemas a maior parte do custo do ciclo de vida é manutenção, então é aqui que o custo total de propriedade em boa parte se decide. Os juros da dívida técnica são pagos na moeda que a liderança já acompanha: entrega mais lenta, taxa de falha de mudanças mais alta, mais tempo para se recuperar de incidentes e engenheiros que evitam o código mais assustador. Quando você torna a dívida visível e financia a quitação de forma estável, reduz o custo de toda mudança futura nas áreas que mais importam e evita o padrão de expansão e queda em que a dívida negligenciada força uma reescrita de emergência cara.
O custo de adoção é modesto e em grande parte cultural: estabelecer a disciplina dos dois chapéus, construir a rede de segurança onde você precisa refatorar, manter um registro de dívida e proteger uma fatia estável de capacidade para a quitação. O custo da negligência se acumula em silêncio. Os juros correm a cada mudança até a velocidade colapsar e a organização se ver congelada, incapaz de modificar com segurança um sistema que já não entende, que é o desfecho mais caro de todos. Para defender o caso junto à liderança, ligue a dívida diretamente às métricas de entrega com que ela já se importa e apresente a quitação como uma decisão de portfólio com retorno mensurável, e não como engenheiros pedindo tempo para arrumar.
Antipadrões e armadilhas
- Misturar refatoração com mudança de comportamento: um commit faz as duas coisas, de modo que uma quebra não pode ser atribuída e o histórico perde a confiabilidade.
- Refatorar sem rede de segurança: reestruturar código sem testes e torcer, que é editar por fé.
- A reescrita em big-bang: substituir de uma vez um sistema que funciona, um padrão com alta taxa de fracasso e longo tempo até o valor.
- Refatoração como fim de semana heroico: limpeza sem revisão e insustentável que se desfaz sozinha em vez de capacidade estável.
- Dívida invisível: atalhos que ninguém acompanha, de modo que a quitação é conduzida pelo volume de reclamações e não pelo custo real.
- Manipular métricas de qualidade: atingir uma meta de cobertura ou de complexidade enquanto a qualidade real cai, porque a medida virou a meta.
- Refatorar o código errado: polir módulos estáveis ou prestes a ser aposentados enquanto os verdadeiros pontos quentes continuam custando.
- Refatoração perpétua: reestruturação sem fim que nunca entrega valor, o espelho de nunca limpar.
Modelo de maturidade
- Nível 1, Iniciar: A refatoração é ad hoc e reativa, muitas vezes misturada com mudança de comportamento no mesmo commit. Não há rede de segurança confiável, a dívida técnica é invisível e não acompanhada e a limpeza acontece apenas em ocasionais explosões heroicas ou nem acontece.
- Nível 2, Desenvolver: Algumas equipes separam a refatoração da mudança de comportamento e se apoiam em testes onde existem, e refatorações nomeadas e testes de caracterização aparecem em bolsões. A prática é inconsistente entre as equipes, a dívida é discutida e às vezes registrada, e a quitação compete ad hoc com as funcionalidades e em geral perde.
- Nível 3, Padronizar: A disciplina dos dois chapéus, os testes de caracterização para código legado e as pequenas refatorações nomeadas são documentados e esperados em toda a organização. A dívida é acompanhada num registro compartilhado que separa principal de juros, e uma capacidade protegida para a quitação é planejada a cada ciclo e imposta na revisão.
- Nível 4, Gerenciar: A dívida e a limpeza são medidas e controladas com dados em relação a linhas de base. Você acompanha a frequência de mudança e a complexidade para localizar pontos quentes, observa a taxa de falha de mudanças e o prazo de mudança nas áreas refatoradas e registra os juros que cada item significativo custa, de modo que as decisões de quitação repousam em evidências e as decisões de abandonar ou investir são tomadas por tendências e não pelo volume de reclamações. Os sinais de qualidade informam o investimento sem serem ligados a portões que as pessoas possam manipular.
- Nível 5, Orquestrar: A dívida é gerida como um portfólio continuamente reequilibrado e integrado ao planejamento de produto e de manutenção em toda a organização. A mudança estrutural usa rotineiramente a figueira-estranguladora e a ramificação por abstração, coordenadas entre equipes, a quitação é contínua e ajustada a onde a mudança é frequente e dolorosa, e a prática se adapta conforme o sistema e o seu quadro de riscos mudam, de modo que o código de longa vida continue mutável ao longo de décadas.
Ideias para discussão
- Qual é a regra real e imposta da sua equipe para manter a refatoração separada da mudança de comportamento, e onde ela se quebra sob pressão de prazo?
- Como você decide, com evidências, qual código merece limpeza e qual é melhor deixar em paz?
- Onde os testes de caracterização permitiriam refatorar com segurança uma área legada que você hoje evita?
- Para a sua próxima grande modernização, como seria uma abordagem de figueira-estranguladora, e que fachada ou abstração você introduziria primeiro?
- Quem é o responsável pelo registro de dívida técnica, e como a quitação de fato ganha capacidade contra o trabalho de funcionalidades?
Principais conclusões
- A refatoração preserva o comportamento. Mantenha-a estritamente separada da mudança de comportamento, em commits separados.
- Uma suíte de testes confiável é a pré-condição para uma refatoração segura, e os testes de caracterização dão uma ao código legado.
- Trabalhe em passos pequenos, nomeados e reversíveis, prefira a limpeza oportunista e use a figueira-estranguladora ou a ramificação por abstração para grandes mudanças estruturais.
- Torne a dívida técnica visível, separe principal de juros e financie a quitação como capacidade estável e não como heroísmo.
- Meça a qualidade interna para orientar o julgamento, nunca como meta a manipular, e não refatore código que é estável ou destinado à aposentadoria.
Referências e leitura complementar
- Martin Fowler, Refactoring: Improving the Design of Existing Code, second edition
- Michael Feathers, Working Effectively with Legacy Code
- Ward Cunningham, The WyCash Portfolio Management System (OOPSLA 1992 experience report, origin of the debt metaphor)
- Martin Fowler, “TechnicalDebtQuadrant” and “StranglerFigApplication” (martinfowler.com)
- Kent Beck, Tidy First? A Personal Exercise in Empirical Software Design
- Steve McConnell, Code Complete: A Practical Handbook of Software Construction
- Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship