- A Riot Games vê dívida técnica como código ou dados cujo custo será pago por desenvolvedores no futuro, e organiza uma linguagem comum para avaliar essa dívida com base em casos de desenvolvimento de League of Legends
- Os critérios de avaliação são três: impacto, custo de correção e contágio; em especial, contágio se refere ao grau em que a dívida se espalha ao longo do tempo para outros sistemas, dados e práticas de desenvolvimento
- Os tipos de dívida se dividem em Local Debt, presa à implementação interna; MacGyver Debt, que conecta temporariamente dois sistemas; Foundational Debt, em que suposições profundas ficam embutidas na estrutura; e Data Debt, em que conteúdo se acumula sobre falhas
- Cataclysm do Jarvan, o uso paralelo de
std::stringeAString, o uso de Lua no BlockBuilder e o bug de nomeação de parâmetros de bloco são usados como exemplos concretos de cada tipo - Dívidas com baixo contágio podem até permanecer por muito tempo sem problemas, mas dívidas com alto contágio aumentam seu custo de correção e impacto com o tempo, então é preciso cortar cedo suas rotas de propagação
Três eixos para avaliar dívida técnica
- Dívida técnica é “código ou dados cujo custo será pago por desenvolvedores no futuro”
- Para decidir se uma dívida específica deve ser corrigida agora, depois ou realisticamente deixada como está, é preciso ter critérios comuns de medição
- A Riot avalia dívida técnica em três eixos
- impact: impacto sobre jogadores e desenvolvedores
- fix cost: tempo necessário para corrigir e risco de deploy
- contagion: o quanto o problema se espalha se for deixado como está
-
impact: o custo que aparece para jogadores e desenvolvedores
- Para os jogadores, isso aparece como bugs, recursos ausentes e comportamentos inesperados
- Para os desenvolvedores, isso se acumula como atrasos de implementação, obstáculos no workflow e detalhes desnecessários que precisam ser lembrados
- Aqui, desenvolvedor inclui não só engenheiros, mas também designers, artistas de VFX e outros profissionais envolvidos na produção do jogo
- Algumas dívidas impedem engenheiros de escrever código novo, enquanto outras atrapalham designers a escrever novos scripts ou artistas de VFX a criar novas partículas
-
fix cost: tempo de implementação e risco de deploy
- O custo de correção inclui não apenas o tempo real de desenvolvimento, mas também o risco gerado ao colocar a mudança em produção
- Um erro simples em uma única função pode ser corrigido em poucos minutos, mas uma suposição profunda que afeta linhas de código do jogo inteiro pode levar semanas ou meses
- Mesmo um sistema “errado” pode já estar sendo usado como ferramenta para criar um bom jogo, de modo que corrigi-lo pode quebrar conteúdo existente
- Por exemplo, mudar a forma como o engine de scripting trata erros ou como calcula o tempo de geração de partículas pode afetar mais de 500 habilidades de mais de 140 campeões
-
contagion: o grau em que se espalha com o tempo
- contagion significa o quanto a dívida técnica se propaga para outros sistemas, dados e formas de desenvolvimento quando é deixada como está
- A propagação acontece por interfaces com sistemas problemáticos, por copiar e colar dados construídos sobre eles e por mudanças na forma de implementar novos recursos
- Uma dívida bem isolada pode ser corrigida depois sem grande diferença de custo em relação a corrigi-la agora
- Dívidas com alto contágio ficam mais difíceis de corrigir com o tempo, e o impacto também cresce à medida que mais sistemas são infectados pelo compromisso central
Local Debt: uma caixa-preta bagunçada só por dentro
- Local Debt é parecida com o modelo clássico de programação em caixa-preta
- Visto de fora, o sistema funciona de forma estável, mas sua implementação interna pode ser terrível ou confusa
- Exemplo: habilidades, camada de rede, engine de scripting
- Se não for necessário levar essa dívida interna em conta ao desenvolver sistemas ao redor, o contágio tende a ser baixo
-
Analogia do mundo real: o olho humano
- O olho humano, por sua própria estrutura, recebe imagens de cabeça para baixo, e os nervos da retina criam um ponto cego perto do centro de cada olho
- O centro visual do cérebro inverte os dados e preenche o ponto cego para que o restante do cérebro interaja com uma imagem “correta”
- Essa estranheza fica localizada no sistema do olho e do nervo óptico, e outros sistemas conseguem evitá-la com facilidade, então é uma solução “boa o suficiente”
-
Caso de League: Cataclysm do Jarvan
- O Cataclysm do Jarvan, até hoje, é feito com minion
- Designers podem usar uma ferramenta que cria “invisible minion” ao anexar efeitos de gameplay a uma posição específica ou a um conjunto de posições
- RiotXypherous explica o que significa “minion” nesse contexto em um comentário no Reddit
- Esses objetos de jogo são uma forma estável e bem compreendida de rastrear e executar lógica de script
- A parede do Jarvan precisa exatamente de 24 minions para impedir que jogadores escapem
- Antigamente eram 12, mas como os jogadores às vezes conseguiam passar entre as paredes, Riot Exgeniar aumentou o número para 24
- A alternativa é uma estrutura de ring-terrain, uma peça única de lógica que controla a pathability do Cataclysm, o que pode organizar a lógica e reduzir um pouco o custo computacional
-
Avaliação do Cataclysm
- impact: 1/5
- O fato de a parede ser feita com minions quase não afeta outros desenvolvedores ao criar conteúdo novo
- O “Jarvan Ult Hitch” foi resultado da combinação entre essa dívida e um bug de carregamento que tentava ler uma definição ausente de auto-attack
- fix cost: 2/5
- Hoje, não é possível criar geometria personalizada com formas compostas sem escrever código novo
- Para criar um “area trigger” em forma de anel, seria necessário código matemático dedicado para calcular colisões em anel
- A Riot está explorando Constructive Solid Geometry para outros fins, e isso pode reduzir bastante o custo da correção
- contagion: 1/5
- Como não é preciso considerar a implementação da parede do Jarvan ao desenvolver recursos, ela está bem isolada
- O risco de contágio surge quando outros designers copiam e colam essa implementação em novos campeões, algo que de fato aconteceu ocasionalmente
- Como problema de implementação, a possível propagação do Cataclysm é baixa e bem compreendida
- impact: 1/5
-
Como lidar com Local Debt
- A característica típica da Local Debt é ter uma pontuação baixa de contagion
- Quando o impacto é maior que o fix cost, desenvolvedores que agem como bons cidadãos tendem a corrigi-la antes que muito tempo passe
- Se realmente não houver contágio, é seguro deixá-la por quanto tempo for necessário
- Um dos grandes erros é atacar imediatamente uma Local Debt que provoca o perfeccionismo do engenheiro, mas não tem impacto amplo o suficiente
- Como o escopo da mudança é localizado, normalmente é fácil validar a correção e fazer testes de regressão
- Exemplos corrigidos recentemente incluem bugs relacionados a inhibitor, Janna’s Monsoon e Tear of the Goddess
- Um bug em que, em certas situações, o inhibitor fazia campeões fazerem pathing para as coordenadas 0,0,0
- Um problema em que Janna’s Monsoon ignorava spell shield
- Um problema em que Tear of the Goddess acumulava stacks em conjurações sem mana
Dívida MacGyver: dois sistemas presos com fita adesiva
- Dívida MacGyver é um tipo cujo nome vem do programa de TV MacGyver, de meados dos anos 1980
- No contexto de dívida técnica, significa um estado em que dois sistemas conflitantes estão “presos com fita adesiva” nos pontos de interface espalhados pela base de código
-
Analogia do mundo real: Seattle
- Seattle teve, no passado, dois assentamentos rivais, cada um com sua própria malha urbana
- À medida que os dois assentamentos cresceram e viraram a Emerald City moderna, as malhas ligeiramente diferentes foram unidas, gerando quarteirões e prédios de formato estranho e uso ineficiente do espaço
-
Caso da League: std::string e AString
- Na base de código da League, coexistem a std::string do C++ e a classe customizada AString da Riot
- Ambas são formas de armazenar, modificar e passar strings
- A Riot considera que
std::stringcausa muitas alocações de memória “ocultas” e custos de performance, além de facilitar a escrita de código ruim - AString foi projetada com foco em gerenciamento cuidadoso de memória
- A estratégia de substituição foi manter os dois sistemas juntos e permitir conversão entre eles por meio de
.c_str()e.Get() - Foram adicionadas melhorias à AString para aumentar a usabilidade, e os engenheiros foram incentivados a substituir
std::stringpor conta própria ao modificar o código - Dessa forma,
std::stringdesaparece aos poucos e de maneira gradual, e as interfaces de “fita adesiva” entre os dois sistemas também diminuem junto com a limpeza do código
-
Avaliação de std::string vs AString
- impacto: 2/5
- A maior parte das alocações de alto impacto causadas por
std::stringjá foi eliminada por meio de profiling - Hoje, o principal custo é o pequeno custo mental de trocar de contexto ao converter de um sistema para o outro
- A maior parte das alocações de alto impacto causadas por
- custo de correção: 3/5
- A migração para AString não é um simples localizar e substituir
- AString tem variações com finalidades específicas, como AStackString, que faz a alocação inicial na memória stack, ARefString, para referência a strings estáticas, e AString, baseada em alocação na heap
- Para a substituição correta, uma pessoa precisa olhar e julgar cada ponto manualmente, e o processo de remover gradualmente o sistema existente será longo e lento
- contágio: -2/5
- Tornar AString mais fácil de usar do que
std::stringinverte o contágio na direção favorável - Sempre que engenheiros fazem check-in de mudanças no código do jogo, cresce a chance de a AString se espalhar mais
- Tornar AString mais fácil de usar do que
- impacto: 2/5
-
Como corrigir a Dívida MacGyver
- O grande custo da Dívida MacGyver muitas vezes é o custo intelectual de precisar trocar de modo ao cruzar a fronteira
- Se um bug ou recurso estiver preso no sistema “errado”, mover o ponto-alvo para o sistema “certo” costuma ser um trabalho direto
- O contágio relativo entre o sistema novo e o sistema existente é a métrica central
- Se você inverter o equilíbrio para que o sistema novo seja mais contagioso, o sistema melhor acaba vencendo
- É preciso fazer com que o sistema globalmente melhor também pareça mais atraente no nível local
- Se engenheiros sob pressão de tempo, ao fazer otimizações gananciosas no trabalho do dia a dia, ainda escolherem o estado final desejado, então a direção está correta
- Outra abordagem é um grande refactor brute-force e, dependendo de quão de perto os sistemas se mapeiam, talvez seja possível corrigir parte ou tudo com regex inteligente
Dívida Fundacional: quando uma suposição profunda fica embutida em toda a estrutura
- Dívida Fundacional é o estado em que alguma suposição nas camadas mais profundas do sistema fica incorporada à forma como tudo funciona
- Usuários experientes do sistema podem ter dificuldade para perceber isso, porque veem como “simplesmente é assim”
-
Analogia do mundo real: United States Customary Units
- Quem cresceu nos Estados Unidos acaba decorando conversões como 1 milha = 5.280 pés, 1 quart = 2 pints e 1 galão = 4 quarts
- O governo dos EUA já considerou várias vezes a transição para o sistema métrico, mas o país ainda é um dos 7 que não adotaram oficialmente o Système International como sistema oficial de medição
- Essa dívida está embutida em placas de estrada, receitas, escolas primárias e na cabeça das pessoas
-
Caso do League: BlockBuilder e Lua
- Entre os grandes casos de Dívida Fundacional com que a Riot lidou estão Determinism in League of Legends e Game Data Server
- O uso da linguagem de script Lua no League também é um exemplo de Dívida Fundacional
- Os designers do League usam uma ferramenta chamada BlockBuilder para conectar blocos funcionais e criar comportamentos complexos
- Os blocos funcionais incluem calcular a distância entre pontos, gerar minions, processar dano e vários tipos de script flow control
- O conjunto de operações que o designer pode escolher é variado, mas limitado, e os parâmetros de cada operação também têm restrições
- No início de League of Legends, decidiu-se não armazenar os blocos e parâmetros em um formato simples e restrito adequado aos dados, mas sim em arrays e tables da linguagem Lua, que é poderosa, porém complexa demais para esse objetivo
- Depois disso, cerca de 10 anos de desenvolvimento do jogo foram construídos sobre essa base, e a manipulação de objetos Lua se tornou uma das operações mais comuns no engine
-
Avaliação do Lua no BlockBuilder
- impacto: 4/5
- O desalinhamento entre Lua e o espaço do problema gera muitos custos
- Em cada frame da lógica do BlockBuilder, a callstack fica poluída com cerca de 6 stack frames de marshalling
- O trabalho de marshalling não é barato em termos de uso de CPU do servidor
- Ler diffs de alterações em scripts é desnecessariamente difícil
- Para fazer parsing/searching em arquivos de script a fim de entender a funcionalidade, é preciso uma compreensão bem profunda da linguagem Lua
- custo de correção: 4/5
- Como Lua está profundamente embutida no engine, é difícil removê-la
- Uma das propostas atuais é criar uma wrapper class que se comporte como um objeto Lua, mas que internamente seja uma struct muito mais simples, para aos poucos mudar o interior do scripting para uma forma mais adequada
- Seja qual for a abordagem, ela precisa ser conduzida com cuidado e reflexão
- contágio: 4/5
- Sempre que um sistema encosta no scripting, ele passa a ser moldado pelas operações e exigências do backend em Lua
- O scripting é a unidade central de lógica do LoL
- A Riot adiciona um novo Building Block em média a cada 3~4 dias, e cada Building Block manipula diretamente objetos Lua
- Quanto mais tempo Lua demorar para ser substituída, mais difícil será substituí-la
- impacto: 4/5
-
Formas de reduzir a Dívida Fundacional
- A Dívida Fundacional tende a ter pontuações altas nos três eixos: impacto, custo de correção e contágio
- Um alto custo de correção leva à continuidade de sistemas imperfeitos, e às vezes essa é mesmo a escolha certa
- Mas, por causa do alto impacto e do alto contágio, corrigir uma Dívida Fundacional grave pode trazer uma grande recompensa
- A estratégia de correção mais comum observada na Riot é construir um sistema novo ao lado do sistema antigo
- Quando possível, a ideia é transformar a dívida fundacional existente em Dívida MacGyver e fazer a portabilidade aos poucos entre o sistema novo e o antigo por meio de operações de conversão
- Essa abordagem limita a exposição ao risco, ao mesmo tempo em que permite começar a obter benefícios em áreas específicas
- Quando essa transição não é possível, pode-se criar uma compile time switch e, se possível, uma loading time switch para aumentar a confiança no novo sistema
- A abordagem com compile time switch está sendo usada na transição do GDS
- A abordagem com loading time switch funcionou bem em Determinism
Data Debt: estado em que uma grande quantidade de conteúdo se acumula sobre defeitos
- Data Debt ocorre quando muito conteúdo se acumula sobre outras categorias de dívida técnica
- O ponto de partida pode ser um bug no sistema de scripting, um formato de arquivo inadequado para um item, ou dois sistemas que não se encaixam bem entre si
- Quando uma grande quantidade de conteúdo, como art, scripts e sounds, é criada sobre esse defeito de código, corrigir a dívida técnica inicial se torna muito arriscado
- Com o tempo, fica dolorosamente difícil descobrir o que vai quebrar
-
Analogia do mundo real: DNA
- O genome de um organismo se acumula lentamente ao longo de milhões de anos por meio de mutation, transcription error e evolutionary pressure
- Alguns erros de cópia são inúteis, mas não prejudiciais; alguns são prejudiciais; e alguns oferecem vantagens poderosas
- É muito difícil descobrir o que um trecho de DNA realmente faz
- Entendemos completamente o que significam os base pairs e como conjuntos de base pairs são traduzidos em amino acids para protein construction
- Também começamos a entender melhor alguns papéis non-encoding do DNA
- Mas, nos mais de 3 bilhões de base pairs do genome humano, ainda há muitas partes que quase não entendemos
- O episódio sobre CRISPR da Radiolab aborda um desses quebra-cabeças resolvidos recentemente
-
Caso do League: bug de nomeação de parâmetro de block
- No League of Legends, o maior impacto do Data Debt acontece quando ele transforma algo que originalmente seria uma correção pequena em um trabalho árduo
- Os game engineers acumulam conhecimento profundo sobre como os sistemas do jogo são implementados e se tornam habilidosos em prever que dados serão quebrados por determinada mudança de código
- Data Debt é uma das considerações mais importantes ao alterar a engine de LoL
- Um caso de Data Debt corrigido alguns anos atrás foi um bug relacionado a parâmetros de block na linguagem de scripting BlockBuilder
- Em um toy example, ao tentar aumentar a armor do Owner com uma variável e uma constante, o valor esperado seria 25 de bonus armor, somando a variável Delta 20 e a constante 5
- Se o nome da variável fosse igual ao nome do parâmetro, antigamente o resultado era 40
- O autor diz que também não sabe por que não era 45
-
Processo real da correção
- Quando o engenheiro NoopMoney, da equipe de Champions, tentou corrigir esse comportamento, a alteração real no código foi de apenas 4 linhas removidas
- Porém, uma dívida altamente contagiosa exigia um planejamento minucioso mesmo para uma mudança pequena
- Em qualquer ponto das 400.000 linhas de script de LoL, qualquer parâmetro numérico podia estar sendo duplicado por esse bug
- O problema maior era que o jogo já estava balanceado e ajustado em torno desses valores potencialmente duplicados, então esses scripts estavam funcionando “correctly”
- NoopMoney precisou tornar a correção ativável por toggle em Live para se preparar para bugs inesperados
- Ele realizou uma ampla busca com regex e uma varredura de QA para identificar quais scripts dependiam desse bug
- No fim, os problemas causados pela correção foram relativamente pequenos, e apenas alguns scripts de champion precisaram de mudanças
- Por causa do Data Debt, era difícil prever o resultado
-
Avaliação do Parameter Naming Bug
- impact: 2/5
- O impacto quando ocorria era pequeno
- Podia dobrar o valor passado e descartar a constante
- Isso se tornava mais um pedaço inútil de tribal knowledge que designers e engineers precisavam lembrar
- O developer mindshare é um recurso valioso demais para ser desperdiçado assim
- fix cost: 2/5
- No geral, a correção em si foi direta
- Foi possível criar um live feature toggle para aumentar a confiança na segurança da correção
- A parte mais cara foi a triagem inicial para avaliar o escopo do problema e definir os alvos de teste
- contagion: 4/5
- Infelizmente, esse bug incentivava um comportamento muito lógico
- Se você quer causar dano a uma unit, é completamente lógico armazenar o valor em uma variável chamada “Damage”
- O bug era acionado quando o block ApplyDamage recebia a quantidade em um parâmetro com o mesmo nome
- Se outra pessoa fizesse copy/paste desse block para criar uma spell parecida, o bug se espalharia ainda mais
- impact: 2/5
-
Por que o Data Debt é tão contagioso
- O Data Debt normalmente recebe uma avaliação alta de custo de correção porque dificulta estimar o impacto de mudanças
- O mais preocupante é que, pela própria natureza dos data, ele quase sempre é altamente contagioso
- Geralmente é permitido criar novos data por meio de copy/paste de data existentes
- Ao criar uma nova spell de skillshot, começar pelo Mystic Shot do Ezreal pode economizar muito tempo, e os problemas dos data existentes também se propagam para seus data descendentes
- Como os data quase não passam por revisão técnica semelhante a code review, mesmo quando práticas ruins são amplamente conhecidas, é difícil perceber e impedir sua disseminação
- Para corrigir problemas em data, normalmente é preciso que uma pessoa com olhos e cérebro faça a verificação direta; compiler e formal logic sozinhos não bastam
-
Duas abordagens para corrigir o Data Debt
- A primeira abordagem é a do it right checkbox
- Consiste em criar, para o data creator, um toggle entre o comportamento “broken” existente e o novo comportamento “fixed”
- Idealmente, a versão fixed vira o padrão, e o conteúdo antigo usa a versão broken
- Depois disso, como no MacGyver Debt, é possível migrar para a nova versão com uma substituição lenta e constante
- A desvantagem é o custo permanente de adicionar cada vez mais elementos desnecessários à UI de edição
- A segunda abordagem é just fix the damn thing
- Foi a abordagem usada por NoopMoney no parameter naming bug
- Corrige-se o bug e depois tenta-se reparar todos os data significativamente afetados
- Técnicas para tornar isso menos assustador incluem muito grep e regex searching para entender o impacto teórico, targeted testing e preparar um toggle para reverter ao comportamento antigo caso omissões piores sejam descobertas após o ship
- Determinism ajuda bastante a testar esse tipo de mudança, pois permite verificar se o servidor gera o mesmo resultado antes e depois da alteração
- A primeira abordagem é a do it right checkbox
Resumo: a contagion deve entrar na avaliação de custo
- As métricas para medir dívida técnica são impact, fix cost e contagion
- impact é o efeito sobre clientes e desenvolvedores
- fix cost é tempo e risco
- contagion é o grau de propagação do problema
- A maioria dos desenvolvedores considera impact e fix cost regularmente, mas discussões sobre contagion são relativamente raras
- contagion pode se tornar o pior inimigo do desenvolvedor quando o problema se aprofunda e fica cada vez mais difícil de remover
- Em contrapartida, se você fizer a correção ser mais contagiosa que o problema, pode transformar contagion em uma arma
- A maior parte da dívida técnica observada no League se encaixa em uma das quatro categorias
- Local Debt: dívida tipo black box, com interior bagunçado
- MacGyver Debt: dívida em que 2 ou mais sistemas estão presos com fita adesiva por meio de uma conversion function
- Foundational Debt: dívida em que toda a estrutura foi construída sobre suposições infelizes
- Data Debt: dívida em que uma enorme quantidade de data se acumulou sobre outros tipos de dívida, tornando a correção arriscada e demorada
1 comentários
Comentários do Hacker News
A contagiosidade é exatamente o motivo pelo qual interfaces são um dos elementos mais importantes do design e precisam ser pensadas com cuidado.
Se uma interface bonita tiver uma implementação abaixo do ideal por trás, é fácil arrumar quando houver tempo; o contrário quase nunca é verdade.
Às vezes faltam as duas.
Nesses casos, impor de forma combinatória módulos pequenos e responsabilidade única pode evitar que a contaminação fique grave demais. Não exige muito conhecimento do futuro nem muito tempo; basta evitar interfaces de superfície ampla em estilo boneca russa, que controlam várias variações de comportamento por parâmetros. É melhor mover configuração, parsing e decisões de comportamento para as bordas da lógica, em vez de deixá-las infiltrar por todo o submodelo.
Mesmo começando conscientemente com a intenção de evitar dívida técnica a qualquer custo, é difícil acertar, e isso exige mais do que simples confiança técnica ou visão arquitetural. Na prática, entra no campo da previsão do futuro.
Se você embute implicitamente uma implementação burra na interface, muitas vezes não dá para corrigir apenas trocando a implementação. O exemplo que me vem à mente é o comportamento de ordenação e paginação. Desenvolvedores júnior, e hoje também muitos sêniores que já deveriam saber melhor, frequentemente começam por requisições com parâmetros do tipo
limit/offset, o que leva a problemas terríveis de performance e comportamentos estranhos. A forma como a paginação funciona de maneira eficiente e as opções de ordenação que podem ser suportadas com boa performance estão intrinsecamente acopladas ao formato dos dados e à escolha do armazenamento de dados. Quem não passou por esse processo nas camadas inferiores dificilmente conseguirá criar bem a interface de alto nível, a menos que primeiro se aprofunde bastante na implementação.Na maioria dos casos, não quero ver a implementação; quero ver apenas uma interface bem documentada. Se o comportamento da interface não pode ser explicado em palavras simples, há algo errado.
O QWERTY é famoso por não ser a interface física ideal, e o volante pode ser um caso parecido. No lado dos computadores, x86 é o exemplo clássico de uma interface superficialmente abaixo do ideal.
É bem surpreendente que este texto tenha sido escrito por um gerente de engenharia.
Nenhum gerente com quem trabalhei conseguiria falar sobre a nossa base de código nesse nível de detalhe técnico. Nem mesmo os que já tinham sido engenheiros.
Mas, para ser justo, nunca tivemos gerentes promovidos internamente; temos o mau hábito de contratar gerentes de fora porque as pessoas internas, inclusive eu, não querem deixar a engenharia.
Acho que falta o tipo de dívida mais comum que já vi: dívida de fundador.
É a dívida criada por fundadores para lançar tecnologia rápida e valiosa. Aquilo que parecia fruta fácil de colher acaba virando a base de todo o sistema.
Os documentos de fundação de muitos países também entram nessa categoria lol (mas não USA! USA! USA!).
Dívida MacGyver e dívida de fundação chegam mais perto, mas nenhuma das duas captura exatamente esse fenômeno.
Do ponto de vista técnico, é um excelente texto.
Mas eu diria que isso é mais uma nomenclatura do que uma “taxonomia”. Ela não é intencionalmente abrangente nem mutuamente exclusiva, embora eu possa estar errado. Os exemplos físicos de cada item foram especialmente bons e dão no que pensar.
Como sempre, há uma pequena questão filosófica que me incomoda. Os “três eixos” do começo parecem ser os retornos e investimentos do RoI tradicional, acrescidos de uma subcategoria específica de retorno futuro e condicional. Imagino que essa decisão tenha funcionado bem na prática, e práticas de desenvolvimento de videogames não precisam ser absolutamente científicas, mas um pouco mais de segurança filosófica não faria mal.
Também foi discutido na época:
A Taxonomy of Technical Debt - https://news.ycombinator.com/item?id=16810092 - abril de 2018 (113 comentários)
E também há este:
A Taxonomy of Tech Debt (2018) - https://news.ycombinator.com/item?id=39782923 - março de 2024 (1 comentário)
A descrição “defino dívida técnica como código ou dados cujo custo será pago por desenvolvedores futuros” é uma das melhores que já vi.
Como em qualquer dívida, ao assumi-la é preciso aplicar um limiar que equilibre a necessidade imediata e o custo futuro. Tenho a impressão de que a maioria das pessoas, não só desenvolvedores, superestima a necessidade imediata e subestima o custo futuro.
Pessoalmente, tenho uma aversão quase patológica a qualquer tipo de dívida. Às vezes passo mais um dia separando algo que pode ser útil no futuro. Acerto em mais ou menos 50% dos casos, mas cada vez que faço esse trabalho o hábito se reforça e meu fluxo de trabalho padrão também fica mais rápido.
Até agora trabalhei em 3 “startups”, e em todas entrei depois que já tinham receita suficiente para pagar salários próximos do normal.
O que vi com mais frequência foi vários fundadores tendo uma compreensão nebulosa que mistura a ideia que tiveram, o que de fato foi construído e quais partes da implementação realmente funcionam.
Desde que li este texto pela primeira vez, venho usando a palavra contagiosidade para explicar dívida técnica, e ela se encaixa muito bem.
Não sei se “dívida local” pode ser chamada de dívida técnica em situações normais.
Na prática, sempre há alguma parte bagunçada em algum lugar, e encapsulá-la para escondê-la de modo que ninguém se machuque é normal. Se quase nunca precisa ser alterada enquanto os requisitos não mudarem, e se, quando mudarem, qualquer implementação teria de ser modificada, então está tudo bem.
Se as 24 instâncias de minion do exemplo forem um problema real, e não apenas algo pouco elegante, então parece mais uma dívida de fundação, no sentido de que “minion” virou a unidade básica mais simples e poderia ter existido algo mais leve.
Incentivar desenvolvedores a fazer mudanças que incluam até módulos antigos, maduros e que não precisariam ser tocados acaba sendo uma boa forma de impedir que isso se acumule a ponto de virar problema.
Um aspecto importante é quando se assume dívida técnica conscientemente para obter um ganho de curto prazo.
Então esse ganho vira mais um eixo a ser ponderado.
Quer erguer um prédio novo agora e concluir o trabalho, em vez de esperar 15 anos até ter capital? Pegue um empréstimo.
Dívida é uma ferramenta, mas uma ferramenta poderosa e perigosa. Se você não reconhece e respeita que a está usando, vai se machucar. Ou alguém que recebeu a granada de você vai se machucar. Como dívida real.