Fundamentos de Manutenção de Software
7 Dívida Técnica 🔗
Shipping first time code is like going into debt. ― Ward Cunningham (1992)
O capítulo começa definindo o conceito de dívida técnica e reforçando que ele é principalmente uma metáfora para comunicação entre desenvolvedores e profissionais de negócio (Seção 7.1). Em seguida, apresentamos os principais tipos de dívida técnica, incluindo dívida técnica de design e arquitetura, requisitos, implementação, testes e implantação (Seção 7.2). Depois, esclarecemos que nem toda dívida técnica é intencional, ou seja, existe também a possibilidade de assumir dívida técnica de forma não planejada (Seção 7.3). Em seguida, descrevemos estratégias para documentar, gerenciar e priorizar dívida técnica (Seção 7.4). Para concluir, apresentamos duas outras metáforas que têm como objetivo incentivar práticas de garantia de manutenibilidade, conhecidas como Teoria das Janelas Quebradas e Regra dos Escoteiros (Seção 7.5).
7.1 Introdução 🔗
Um cenário recorrente em Engenharia de Software é o seguinte: usuários sempre querem novas funcionalidades nos sistemas, bem como que seus bugs sejam resolvidos o mais rapidamente possível. Essas demandas, inevitavelmente, chegam aos gerentes e líderes de desenvolvimento que, de forma natural, tendem a repassá-las para os times de desenvolvedores. O próximo passo dessa história também é previsível: com prazos apertados e sob constante pressão, os times de desenvolvimento podem tomar atalhos para resolver os pedidos dos gerentes e dos usuários. Para piorar, esses atalhos ficam ocultos no código por um bom tempo, antes de começarem a causar problemas.
Em 1992, Ward Cunningham, que também é o inventor da wiki e um dos proponentes do Manifesto Ágil, conseguiu propor uma metáfora mais elegante para esse problema, a qual chamou de dívida técnica (technical debt, link). No parágrafo do seu artigo do qual retiramos a citação que abre este capítulo, Cunningham declarou o seguinte:
Entregar código que ainda não está pronto é como assumir uma dívida. Um pouco de dívida acelera o desenvolvimento, desde que seja paga rapidamente com uma reescrita. […]. O perigo surge quando a dívida não é paga. Cada minuto gasto em código que não está totalmente correto conta como juros dessa dívida.
Portanto, Cunningham comparou a entrega de código que ainda não está pronto (first time code, no original) com o ato de assumir uma dívida financeira. Mais tarde, o adjetivo técnica foi incorporado à metáfora para caracterizar a natureza da dívida à qual Cunningham se referia. Na verdade, nessa época, Cunningham tentava justificar uma refatoração no sistema de gerência de carteiras de investimento no qual estava trabalhando. Como seus chefes eram do ramo financeiro, a metáfora de dívida lhe pareceu uma boa estratégia.
Ao comparar o problema de design do seu sistema a uma dívida, ele queria alertar gerentes e líderes que soluções de programação ou de design inadequadas possuem um custo ou, melhor, implicam em uma dívida. E ele também completou o seu raciocínio: como qualquer outra dívida, uma dívida técnica também cobra juros, na forma de cada minuto extra que desenvolvedores gastam a mais para entender, modificar e evoluir um código que não foi escrito de uma forma ideal.
Em 2016, um grupo de cientistas reunidos em um ciclo de seminários chamado de Dagstuhl, realizado na Alemanha, também propôs uma definição, um pouco mais detalhada, para o conceito de dívida técnica:
Em sistemas de software, dívida técnica é um conjunto de construções de projeto ou de implementação que são convenientes no curto prazo, mas que estabelecem um contexto técnico que pode tornar mudanças futuras mais custosas ou mesmo impossíveis. Dívida técnica representa um passivo real ou potencial cujo impacto se limita às qualidades internas do sistema, principalmente manutenibilidade e facilidade de evolução.
Essa definição deixa claro que, apesar do adjetivo técnico ser amplo, a dívida que nos interessa é aquela que vai impactar negativamente a facilidade de manter e evoluir um sistema de software. Dentre os problemas que podem ser causados por uma dívida técnica, podemos mencionar:
- Aumento do tempo para implementação de novas funcionalidades.
- Aumento do número de bugs.
- Aumento do tempo de correção de bugs.
- Aumento do tempo de treinamento de novos desenvolvedores (onboarding).
- Aumento do tempo para atualizar bibliotecas e frameworks.
- Aumento de problemas de segurança, desempenho, etc.
- Aumento da dependência de desenvolvedores mais experientes.
Tal como as dívidas financeiras, uma dívida técnica possui três componentes:
O principal inicial é a economia imediata de tempo que se obteve ao usar um atalho no desenvolvimento, de forma a implementar uma funcionalidade ou corrigir um bug de forma mais rápida.
Os juros são o tempo extra que foi gasto para manter ou evoluir o sistema devido à existência da dívida técnica.
O principal atual é o tempo que será necessário para pagar totalmente a dívida, isto é, para adotar uma solução técnica ideal.
A seguir vamos dar um exemplo para ilustrar esses conceitos.
Exemplo 1: Em um determinado sistema, uma funcionalidade F1 (por exemplo, Cadastro de Alunos) foi implementada de forma apressada em apenas um dia. A implementação ideal demandaria mais dois dias (principal inicial). Meses depois, uma funcionalidade F2 (por exemplo, Matrícula de Alunos) teve que ser implementada, sendo que F1 e F2 são relacionadas e acessam os mesmos dados. A implementação de F2 levou 5 dias. Porém, se não houvesse a dívida técnica, ela poderia ter levado apenas 4 dias. Logo, gastou-se um dia a mais para implementar a nova funcionalidade (juros da dívida técnica). Um ano depois, um líder solicitou uma avaliação de quanto tempo seria necessário para liquidar completamente a dívida técnica. Os desenvolvedores estimaram então que precisariam de 3 dias (principal atual).
Por outro lado, nem toda dívida (técnica ou não) é ruim. Vamos primeiro dar um exemplo do mundo real e depois um exemplo da área de software.
Exemplo 2: Suponha que você tem um sacolão. E que contraiu uma dívida para comprar um furgão, com o objetivo de fazer entregas e, evidentemente, vender mais. Logo, essa dívida pode ser boa, principalmente se o incremento mensal de vendas for suficiente para pagar as parcelas da sua dívida. Em resumo, a dívida foi um investimento que você fez para ampliar o negócio e lucrar mais. Essa comparação é interessante porque, assim como uma dívida financeira feita por uma empresa, dívida técnica, fundamentalmente, é uma decisão de negócio. Ou seja, a pergunta central é sempre a seguinte: vale a pena abrir mão neste momento de qualidade técnica para ganhar mercado? Sabendo que essa falta de qualidade técnica vai nos cobrar juros no futuro, assim como ocorre com uma dívida financeira.
Exemplo 3: Suponha que você tem uma startup e precisa implementar rapidamente uma nova funcionalidade, para ganhar mercado, mesmo que isso exija o uso de vários atalhos técnicos. Com essa decisão, você sabe que estará incorrendo em uma dívida técnica. Por outro lado, a nova funcionalidade vai atrair novos usuários e gerar mais receita. Com esse ganho adicional, você poderá investir em uma solução robusta e tecnicamente adequada, ou seja, pagar o principal de sua dívida técnica.
Antes de terminar esta introdução, é importante ressaltar que dívida técnica é um termo que foi criado para facilitar a comunicação entre o time técnico e o time gerencial. Ou seja, o público-alvo da mensagem são líderes e gerentes. É uma maneira de alertá-los de que software precisa ser continuamente revisitado e melhorado, caso contrário os problemas técnicos vão se acumular a ponto de quase paralisar ou tornar muito arriscada qualquer mudança, inclusive a correção de bugs críticos. Em outras palavras, o que queremos é criar um termo que seja de fácil entendimento para líderes que não estão mergulhados no dia a dia do trabalho técnico de desenvolvedores de software.
Por isso mesmo, na lista dos problemas causados por dívida técnica acima, sempre falamos de pontos que são de fácil entendimento e que devem ser do conhecimento do time gerencial e executivo de uma organização. Por exemplo, mais bugs, mais problemas de segurança, maior tempo de onboarding, maior risco para implementar mudanças em requisitos, etc. Pelo mesmo motivo, evitamos falar de problemas internos do projeto, como falta de testes, alto acoplamento, baixa coesão, falta de organização modular, complexidade do código, etc. Evidentemente, todos esses problemas internos são muito importantes, porém dívida técnica é um instrumento para encapsulá-los em uma metáfora de mais fácil entendimento e com potencial de gerar uma preocupação mais imediata no time gerencial, pois estamos nos endividando!
7.2 Tipos de Dívida Técnica 🔗
Nesta seção, vamos comentar sobre os principais tipos de dívida técnica: design e arquitetura, requisitos, implementação, testes e implantação (deployment). Para isso, vamos apresentar as causas de cada uma dessas dívidas (por exemplo, práticas inadequadas) e, principalmente, as consequências (ou dores) de tais causas.
Dívida de Design e Arquitetura: Esse é o principal tipo de dívida técnica e, na verdade, foi o tipo que levou Ward Cunningham a cunhar o termo, quando tentava convencer seus gerentes a realizar uma refatoração de maior porte no sistema financeiro que desenvolvia, implementado em uma linguagem chamada Smalltalk. Esse tipo de dívida pode surgir, por exemplo, pela ausência de uma arquitetura planejada, pela existência de módulos que não seguem a arquitetura definida, pela duplicação excessiva de código, pelo forte acoplamento entre os módulos de um sistema e pela falta de documentação. Como consequência, a dívida de design e arquitetura tende a desestimular refatorações, aumentar o esforço e o risco de manutenções e dificultar a implementação de testes automatizados. Com menos testes, aumentam as chances de bugs não serem detectados durante o desenvolvimento e, portanto, chegarem aos usuários finais. Veja que essas consequências são capazes de despertar a atenção dos gerentes e executivos de organizações que dependem de software para seu funcionamento.
Dívida de Requisitos: Esse tipo de dívida surge quando os requisitos não são devidamente priorizados e validados, quando os POs/PMs não têm capacidade de explicá-los com clareza ou não estão disponíveis para isso, quando existe uma tendência de tornar os requisitos mais complexos do que o necessário e quando há mudanças constantes nos requisitos. Como consequência, o software resultante pode não gerar valor para os clientes, tornar-se difícil de usar, apresentar diversos bugs e acumular problemas de usabilidade, privacidade, desempenho, entre outros.
Dívida de Implementação: Essa dívida pode surgir, por exemplo, devido à ausência de regras claras para nomes e formatação do código, pela falta de comentários, pelo uso de algoritmos não adequados e pela escrita de código mais complexo do que o necessário. Como consequência, o código torna-se mais difícil de compreender, o que aumenta o esforço cognitivo necessário para realizar atividades de manutenção.
Dívida de Testes: Esse tipo de dívida aparece quando não existem testes automatizados, quando os testes existentes possuem baixa cobertura, apresentam comportamento não-determinístico (flaky), são muito lentos ou frágeis, gerando muitos falsos positivos. Como consequência, aumentam os riscos de regressões e de refatorações.
Veja que existe um ciclo vicioso entre dívida de testes e de design e
arquitetura, conforme ilustrado na próxima figura. A falta de testes
automatizados desestimula desenvolvedores a refatorar o código, pois
pode-se quebrar
algo que está funcionando ou introduzir algum
bug. Com menos refatorações, o design e a arquitetura tendem a se
deteriorar; uma arquitetura deteriorada impacta negativamente a
testabilidade, ou seja, a facilidade de escrever testes para um sistema.
Assim, retroalimentamos, de forma negativa, o ciclo.
Dívida de Implantação: Esse tipo de dívida surge quando não há um sistema de controle de versões, quando o processo de implantação (deployment) não é automatizado e quando não se usa integração contínua. Ou seja, branches de funcionalidades costumam ter uma duração muito longa, causando diversos conflitos de integração. Como consequência, o feedback dos usuários torna-se mais lento, aumentando as chances de se insistir em funcionalidades que não vão gerar valor. Existem também mais chances de bugs chegarem em produção, devido à existência de passos e verificações manuais.
Literatura Científica: Em 2015, Neil Ernst e colegas da Universidade Carnegie Mellon realizaram uma pesquisa com 536 profissionais da área de Engenharia de Software para entender a percepção deles sobre dívida técnica (link). Uma das perguntas da pesquisa pedia para os participantes ranquearem as causas de dívida técnica em sistemas de software. A seguir listamos os resultados, incluindo o percentual de participantes que indicaram a causa nas três primeiras posições do ranking (os percentuais mostrados são aproximações extraídas de um gráfico de barras do artigo):
- Decisões inadequadas de arquitetura: 55,1%
- Código excessivamente complexo: 30,3%
- Falta de documentação de código: 25,7%
- Testes inadequados: 25,7%
- Tecnologias obsoletas: 23,0%
- Automação de testes insuficiente: 22,1%
- Dependências entre módulos: 21,1%
- Duplicação de código ou edições repetitivas: 18,4%
- Dependências do código de equipes externas: 16,5%
- Processo de implantação inadequado: 14,7%
- Dependências de pacotes externos: 14,3%
- Código obsoleto: 13,8%
- Infraestrutura ineficiente de gerência de configuração ou build: 13,8%
- Outros: 6,4%
Como podemos ver, além do primeiro item, diversos outros são também relacionados com problemas de design e arquitetura.
Mundo Real: No livro Software Engineering at Google, relata-se um caso de dívida técnica enfrentado logo no início da empresa, envolvendo o servidor Web que hospeda a página de pesquisa do Google, chamado Google Web Server (GWS). Segundo os autores do livro, “as novas releases do GWS estavam sempre cheias de bugs e estava levando cada vez mais tempo para entrar em produção. Os membros da equipe tinham pouca confiança ao fazer mudanças no serviço e frequentemente só descobriam que algo estava errado quando funcionalidades paravam de funcionar em produção. Em certo momento, mais de 80% das implantações continham bugs que afetavam usuários e precisavam ser revertidos.” Ou seja, todos esses problemas são consequências clássicas de dívida técnica. Os líderes do projeto identificaram, então, que eles poderiam ser mitigados com a implantação de uma cultura de testes automatizados e de integração contínua. Depois dessa decisão, o GWS passou a ter dezenas de milhares de testes e novos releases do servidor são colocados em produção todos os dias, com um número muito pequeno de bugs.
7.3 Dívida Técnica Não Planejada 🔗
Até este momento, tratamos de dívida técnica planejada, também conhecida como intencional ou deliberada. Nesse caso, temos consciência de que estamos abrindo mão de uma boa prática ou técnica relacionada com arquitetura, design, requisitos, implementação, testes ou implantação. Porém, precisamos avançar rápido e, depois, vamos pagar essa dívida, ou seja, vamos adotar a solução técnica da qual abrimos mão de forma deliberada. Em outras palavras, dívida técnica planejada é uma dívida estratégica, tomada para atender rapidamente um objetivo de negócio (mas que depois deve ser paga, como qualquer dívida financeira).
Porém, existe também uma dívida técnica não planejada, também conhecida como não intencional ou não deliberada. Por exemplo, esse tipo de dívida surge devido a evoluções nos requisitos ou nas tecnologias usadas por sistemas de software. Então, não assumimos essa dívida de forma planejada e estratégica. Ela surgiu devido a fatores que não estavam sob nosso controle e que não podiam ser previstos.
A seguir, vamos descrever três causas comuns de dívida técnica não planejada: novos requisitos, melhor entendimento do domínio e novas bibliotecas e frameworks.
Novos Requisitos: Dívida técnica não planejada pode ocorrer devido a evoluções nos requisitos, como nos seguintes exemplos:
Inicialmente, um banco digital projetou um sistema financeiro para oferecer apenas investimentos em renda fixa, mas agora ele está também oferecendo investimentos em ações e criptomoedas.
Inicialmente, um sistema de comércio eletrônico foi pensado para vender apenas produtos próprios, mas agora também está atuando como um marketplace e vendendo produtos de terceiros.
Inicialmente, uma empresa de educação oferecia apenas cursos presenciais, mas agora ela possui bastante demanda por cursos online. Logo, seus sistemas tiveram que ser evoluídos para gerenciar esse novo formato de curso.
Nos exemplos acima, muitas das decisões arquiteturais e de modelos de dados que foram tomadas no início do desenvolvimento teriam sido diferentes se os desenvolvedores soubessem que teriam que lidar, no futuro, com novos e importantes requisitos. Por exemplo, muito da duplicação de dados e de código observada atualmente poderia ter sido evitada. Em outras palavras, olhando hoje, é fácil detectar a dívida técnica dos sistemas, mas ela surgiu devido a uma evolução nos requisitos que não poderia ser prevista no início do desenvolvimento.
Melhor Entendimento do Domínio: Muitas vezes, o
nosso entendimento do domínio do sistema melhora com o tempo. Explicando
melhor, os requisitos do sistema continuam os mesmos. E, também, a
primeira solução que tivemos não foi um atalho
, mas sim a melhor
solução que conseguimos pensar no início do projeto. Porém, com o passar
dos meses e com o amadurecimento de nosso entendimento sobre o problema,
temos agora uma proposta de design e de arquitetura melhores.
Adicionalmente, essa nova proposta vai nos permitir evoluir o sistema
com mais rapidez e com menos bugs. Logo, não migrar para ela constitui
também uma dívida técnica, pois reconhecidamente estamos, no momento,
adotando um design e arquiteturas que não são mais recomendados.
Quando usou pela primeira vez a metáfora de dívida, parece que Ward
Cunningham estava querendo se referir a dívida técnica que emerge após
um melhor entendimento do domínio de um sistema. Por exemplo, em um
vídeo publicado em 2009, ele afirma o seguinte: [naquele momento] era
importante que acumulássemos os aprendizados que obtivemos sobre a
aplicação ao longo do tempo, modificando o programa para que parecesse
que sempre soubemos o que estávamos fazendo e que sempre tivesse sido
fácil implementar o sistema.
Ou seja, com a evolução do sistema
WyCash, eles descobriram que existia um modo melhor e mais fácil de
implementá-lo, que iria reduzir os custos de manutenção. Logo, se eles
não refatorassem o sistema para adotar essa nova implementação, eles
estariam assumindo uma dívida. Mas, reforçando, quando começaram o
projeto, eles não tinham conhecimento dessa arquitetura e design
alternativos.
Dívida técnica que pode surgir devido a um melhor entendimento do
domínio de um sistema também remete ao princípio de projeto Plan to
Throw Away, definido por Frederick Brooks, em The Mythical
Man-Month. Segundo Brooks, o conceito de jogar fora a
primeira versão é apenas a aceitação do fato de que, à medida que
se aprende, o projeto deve ser modificado.
Novas Bibliotecas e Frameworks: Evoluções em bibliotecas e frameworks também podem implicar em dívida técnica, como nos seguintes exemplos:
Inicialmente, uma empresa decidiu desenvolver um sistema usando J2EE, que era uma das tecnologias mais modernas na época (início dos anos 2000). Porém, muitos dos frameworks e serviços J2EE se tornaram obsoletos e não são mais mantidos.
Inicialmente, uma empresa decidiu implementar um sistema em JavaScript, usando AngularJS 1.0, amplamente adotado na época (início dos anos 2010). Porém, esse framework Web evoluiu para novas versões com muitas breaking changes, e também surgiram outros frameworks, como React.
Logo, a dívida técnica dos sistemas que usamos como exemplo acima deve-se ao uso de tecnologias atualmente depreciadas, como J2EE e AngularJS. Devido à importância desses frameworks, eles acabaram por fazer com que a arquitetura dos sistemas também ficasse defasada. Os sistemas poderiam ter sido migrados para frameworks mais modernos, de forma a quitar a dívida técnica. Porém, nem sempre essa decisão é fácil, pois ela requer investimentos relevantes. Também é importante reforçar que essa dívida não foi planejada, pois quando o desenvolvimento dos sistemas começou, os frameworks que mencionamos constituíam o estado da arte.
Em resumo, todo sistema tende a ter um certo nível de dívida técnica, incluindo dívidas não planejadas. O importante, portanto, é gerenciar essa dívida técnica, como veremos na próxima seção, de forma a evitar que ela paralise o desenvolvimento, manutenção e evolução de um sistema.
7.4 Gerenciamento de Dívida Técnica 🔗
Existe uma diferença fundamental entre uma dívida financeira e uma dívida técnica: você consegue facilmente obter o extrato de uma dívida financeira, no qual estão informados o valor do principal, a taxa de juros, o prazo de pagamento, o valor das parcelas e o saldo devedor. Por outro lado, isso evidentemente não é possível com uma dívida técnica em manutenção de software.
Por isso, um desafio relevante consiste em dar visibilidade à dívida técnica de um sistema, para que todos — profissionais de desenvolvimento e profissionais de negócios — tomem conhecimento dela. Infelizmente, no entanto, não existe uma ferramenta capaz de produzir um extrato completo da dívida técnica de um sistema, informando o tipo da dívida, os juros que ela está cobrando, em termos de atrasos nas tarefas de manutenção, e o valor do principal, isto é, o esforço que devemos dispender para quitar a dívida. Em outras palavras, o conceito de dívida técnica é muito abrangente e aberto para permitir a criação de um algoritmo que automaticamente produza um extrato como esse.
No restante desta seção, vamos discutir alguns mecanismos que podem ser usados então para pelo menos dar visibilidade à dívida técnica de um sistema. Em seguida, vamos discutir a melhor estratégia para pagamento de dívida técnica.
7.4.1 Documentação de Dívida Técnica 🔗
Para dar visibilidade à dívida técnica de um sistema, uma solução pragmática consiste em registrar e documentar essa dívida, o que pode ser feito por meio de issues ou comentários de código, conforme descrito a seguir.
Documentação usando Issues: Alguns projetos adotam a prática de criar issues, no sistema de controle de issues (como Jira, GitHub Issues, Trello, etc.), exclusivamente para documentar dívida técnica que deve ser, em algum momento, paga. Ou seja, assim como criamos issues descrevendo bugs ou novas funcionalidades, podemos criar issues que documentam dívida técnica. Pode-se também rotular essas issues com uma etiqueta debt ou outro nome parecido, para diferenciá-las das demais issues de um projeto. A próxima figura mostra um exemplo de issue extraído do repositório da IDE de desenvolvimento VSCode.
Como podemos ver, essa issue foi criada para documentar que um determinado conceito do sistema possuía três formas de identificação diferentes, o que segundo o autor da issue torna o seu entendimento um pouco confuso. Veja também que a issue foi rotulada como debt (no canto direito e inferior da figura).
Resumindo, a ideia é a seguinte: se você é membro do time que desenvolve um sistema e frequentemente se depara com algum problema técnico (de design, arquitetura, requisitos, implementação, etc.) que torna o seu trabalho mais lento e sujeito a bugs, você pode abrir uma issue para documentá-lo. Em algum momento, o time pode decidir, junto com o PO/PM, pagar essa dívida técnica em algum sprint. Ou seja, assim como bugs e novas funcionalidades, o pagamento de dívida técnica também pode compor o backlog de um sprint.
Alguns autores, como Philippe Kruchten e colegas, em um livro dedicado a gerenciamento de dívida técnica (link), recomendam que a documentação de dívida técnica deve ser mais completa do que uma história de usuário. Especificamente, eles recomendam usar a técnica dos 5W’s (What, Where, Why, When e Who), conforme descrito a seguir:
- What: definir qual é a dívida.
- Where: qual parte do sistema é afetada pela dívida.
- Why: porque é importante pagar a dívida, quais problemas ela está causando.
- When: quando devemos pagar a dívida ou qual a sua prioridade.
- Who: quem será responsável pelo pagamento.
Vamos dar um exemplo de uma descrição de dívida técnica, cobrindo esse 5W’s:
O sistema de caixa de supermercados (where) possui poucos comandos de logging (what). Por isso, a correção de bugs está levando dias; muitas vezes, essas correções falham e os bugs são reabertos, gerando retrabalho (why). Portanto, o time de automação de ponto de venda (who) está recomendando resolver esse problema com urgência (when).
Documentação usando Comentários: Conforme comentamos no Capítulo 3, comentários de código também podem ser usados para documentar dívida técnica. Normalmente, esses comentários começam com palavras como TODO, FIXME ou HACK. A seguir mostramos um exemplo:
// TODO: Substituir o parâmetro "String cep" das funções abaixo
// por um tipo "CEP" (que está implementado no pacote "utils")
Literatura Científica: Em 2022, junto com Laerte Xavier e João Eduardo Montandon, realizamos um estudo com 52 desenvolvedores de sistemas de código aberto que usavam ambas as práticas descritas nesta seção para documentar dívida técnica (link). Ou seja, em pelo menos um sistema, eles já haviam aberto issues para documentar dívida técnica e também usado comentários com esse fim. Perguntamos então quando cada tipo de solução deveria ser usado. Segundo as respostas que obtivemos, comentários devem ser usados para documentar dívida técnica com escopo local e bem definido, de baixa prioridade e que requer pouco esforço para ser corrigida. Assim, o comentário funciona como um lembrete para corrigir a dívida. Por outro lado, deve-se usar issues para documentar dívida técnica cujo pagamento requer discussão com outros desenvolvedores, possui maior nível de complexidade, maior prioridade e que precisa ser rastreada e gerenciada ao longo do tempo.
7.4.2 Questionários sobre Dívida Técnica 🔗
Outro instrumento para gerenciamento de dívida técnica consiste em realizar um inventário periódico dela. No entanto, dada a natureza abrangente do conceito, não temos uma ferramenta capaz de identificar todos os pontos de uma base de código que possuem algum tipo de dívida técnica. Assim, uma alternativa consiste em criar questionários para levantamento da dívida técnica em um sistema, que serão respondidos pelos desenvolvedores. Basicamente, a ideia é pedir para os desenvolvedores explicitarem as maiores dificuldades técnicas que enfrentam atualmente na manutenção e evolução de um sistema. Ou seja, esses questionários podem possuir perguntas simples, como as seguintes:
Descreva os tipos de dívida técnica que mais impactam o nosso sistema.
Para cada uma delas, informe sua importância (em uma escala, de 1 a 5).
Para cada uma delas, rascunhe uma proposta de solução.
Esse questionário pode funcionar melhor em empresas maiores. Em empresas menores, ele pode ser substituído por uma reunião para discutir dívida técnica, ou seja, uma espécie de retrospectiva com o único objetivo de realizar um levantamento da dívida técnica de um sistema.
Mundo Real: Em um artigo publicado em 2023, dois desenvolvedores do Google mencionam que a empresa possui um questionário trimestral sobre satisfação dos desenvolvedores (link). Esse questionário frequentemente inclui perguntas como as seguintes:
Sua equipe incorreu deliberadamente em dívida técnica nos últimos três meses?
Com que frequência assumir uma dívida técnica foi a decisão certa?
Quanto sua equipe investiu para reduzir a dívida técnica existente?
Como você avalia o processo da sua equipe para gerenciar dívida técnica?
Veja então que o objetivo dessas perguntas é dar visibilidade para a dívida técnica e, então, permitir que gestores de desenvolvimento e de negócios tomem decisões de forma mais informada.
7.4.3 Pagamento de Dívida Técnica 🔗
Para priorizar o pagamento de dívida técnica, podemos recorrer a uma analogia com dívidas financeiras. Suponha que você tenha três dívidas com diferentes taxas de juros: uma com 10% ao ano, outra com 9% ao ano e outra com 7% ao ano. Naturalmente, a melhor estratégia é quitar primeiro a dívida com maior taxa de juros, pois é a que cresce mais rapidamente e, portanto, gera maior custo ao longo do tempo.
No contexto de manutenção de software, essa lógica pode ser traduzida
da seguinte forma: deve-se priorizar o pagamento da dívida técnica nas
partes do sistema que são modificadas com frequência, pois é nelas que a
dívida cobra mais juros
, isto é, requer maior esforço e assume-se
mais risco a cada nova mudança. Já a dívida presente em partes mais
estáveis do código continua existindo como principal, mas tende a não
cobrar juros significativos, pois esse código quase nunca precisa ser
entendido e modificado.
7.5 Outras Metáforas 🔗
Nesta seção, vamos apresentar duas outras metáforas que também têm como objetivo preservar a manutenibilidade de um sistema. Essas metáforas (ou recomendações) são conhecidas pelos seguintes nomes: Teoria das Janelas Quebradas e Regra dos Escoteiros.
7.5.1 Teoria das Janelas Quebradas 🔗
A Teoria das Janelas Quebradas é famosa entre criminologistas, policiais e autoridades da área de segurança. Proposta em 1982 por dois criminologistas — James Wilson e George Kelling —, ela defende o seguinte:
Se uma das janelas de uma casa for quebrada e não for consertada, as pessoas que passam na rua podem achar que a casa está abandonada e que ninguém se importa com ela. Então, alguém pode decidir jogar uma pedra e quebrar mais alguns vidros… Depois de um tempo, muitas janelas vão estar quebradas, o telhado pode também começar a ter problemas e uma sensação de abandono vai tomar conta da casa e até mesmo de sua vizinhança.
A mensagem que a teoria tenta passar é que devemos ter uma tolerância bastante limitada com problemas aparentemente pequenos. Caso contrário, eles podem se acumular, criando uma espiral de abandono e descontrole.
Apesar de mais discutida entre sociólogos, a Teoria das Janelas Quebradas aplica-se também a manutenção de software. Um dos primeiros livros a fazer essa conexão foi The Pragmatic Programmer, de Andrew Hunt e David Thomas. Veja o que os autores dizem:
Não deixe
janelas quebradas(projetos ruins, decisões erradas ou código ruim) sem reparos. Conserte cada um desses problemas assim que ele for descoberto. Nós já vimos sistemas bem projetados e funcionais se deteriorarem rapidamente quando as janelas começam a quebrar. Existem diversos fatores que podem contribuir para a deterioração de sistemas de software, mas a negligência acelera esse processo mais rapidamente do que qualquer outro fator.
7.5.2 Regra dos Escoteiros 🔗
A Regra dos Escoteiros foi descrita por Robert Martin no seu livro Clean Code. Segundo ele, os escoteiros norte-americanos têm uma regra simples que pode ser adaptada para o contexto de manutenção de software. A regra é a seguinte:
Deixe o acampamento mais limpo do que você o encontrou.
Ou seja, se você está corrigindo um bug ou implementando uma nova funcionalidade e se depara com algum código de má qualidade, você não deve simplesmente fechar os olhos. Em vez disso, procure refatorar esse código, para entregá-lo mais limpo do que o encontrou. Você deve fazer isso mesmo que não seja o autor do código com problema.
A Regra dos Escoteiros relaciona-se perfeitamente com o conceito de
refatorações oportunistas. A ideia é que refatorações
devem ser uma atividade contínua, realizadas no meio
de
atividades de correção de bugs e de implementação de novas
funcionalidades.
Literatura Científica: Em um trabalho que publicamos em 2016, junto com Danilo Silva e Nikolaos Tsantalis, sobre motivações para refatorações (link), diversos desenvolvedores nos reportaram que têm a preocupação de deixar o código mais limpo, após uma tarefa de manutenção. Eles não mencionaram explicitamente a Regra dos Escoteiros, mas suas respostas estão alinhadas com o propósito da regra. Veja exemplos de respostas:
Quando eu estava corrigindo uma exceção, eu percebi que deveria adicionar o mesmo código em dois lugares. Em vez de fazer isso, eu extraí esse código para um método e apliquei a correção apenas nele.
O método [em que eu estava trabalhando] era muito grande e não cabia na minha tela. Então eu extraí uma parte dele.
Esses refactorings foram aplicados para reusar código. Eu sempre tento fazer isso, porque quando existe muita redundância torna-se terrivelmente mais complicado manter o código no futuro.
Bibliografia 🔗
Ward Cunningham. The WyCash Portfolio Management System. Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), 1992.
Philippe Kruchten, Robert Nord, Ipek Ozkaya. Managing Technical Debt: Reducing Friction in Software Development Addison-Wesley, 2019.
Steve McConnell. Managing Technical Debt. White Paper, Construx Software, 2008.
Neil Ernst, Rick Kazman, Julien Delange. Technical Debt in Practice: How to Find It and Fix It. MIT Press, 2021.
José Laerte Pires Xavier Júnior. Documenting and Managing Self-Admitted Technical Debt Using Issues. PhD Thesis, PPGCC/DCC/UFMG, 2022.
Exercícios 🔗
1. Descreva: (a) uma causa importante de dívida técnica; (b) uma consequência positiva de dívida técnica; (c) uma consequência negativa de dívida técnica.
2. De forma genérica, responda qual dívida técnica deve ser paga primeiro em um sistema.
3. Descreva um exemplo de uma dívida técnica de arquitetura que seja: (a) planejada; (b) não planejada.
4. A princípio, não existe nenhuma ferramenta capaz de medir e identificar precisamente a dívida técnica de um sistema (porque o conceito é bastante abrangente, cobrindo desde questões de requisitos até questões de implantação). No entanto, descreva pelo menos duas ferramentas que podem ajudar, de forma parcial, a identificar dívida técnica em um sistema.
5. Assim como fizemos na Seção 7.4.1, documente uma dívida técnica hipotética, de algum sistema, usando a técnica dos 5W’s (What, Where, Why, When e Who)
6. Suponha que um certo sistema está com um problema importante de
desempenho (isto é, os usuários estão reclamando que algumas operações
estão demorando muito para serem realizadas). Esse problema
pode
ser caracterizado como uma dívida técnica?
7. O conceito de Quadrante de Dívida Técnica foi proposto por Martin
Fowler para explicar e organizar as diferentes visões sobre dívida
técnica. Você pode aprender mais sobre esses quadrantes no seguinte artigo.
A decisão de não vamos implementar testes porque não vemos valor
neles
, tomada por uma organização hipotética, se enquadraria em qual
dos quadrantes propostos por Fowler? Justifique.