3 pontos por GN⁺ 2024-10-01 | 1 comentários | Compartilhar no WhatsApp
  • 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::string e AString, 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
  • 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::string causa 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::string por conta própria ao modificar o código
    • Dessa forma, std::string desaparece 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::string já 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
    • 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::string inverte 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
  • 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
  • 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
  • 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

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

 
GN⁺ 2024-10-01
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.

    • Concordo, mas para projetar direito geralmente falta uma de duas coisas: tempo para projetar e conhecimento sobre exatamente o que será preciso fazer hoje e daqui a 1 ano.
      À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.
    • Concordo, mas projetar interfaces robustas e preparadas para o futuro sempre foi um dos problemas mais difíceis no desenvolvimento de software.
      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.
    • Para expor uma boa interface, é preciso entender como seria uma boa implementação interna.
      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.
    • Por isso gosto de linguagens como OCaml ou Ada, que tornam as interfaces muito explícitas.
      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.
    • A história parece ter muitos contraexemplos.
      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.

    • O texto também aborda um pouco isso, mas o custo é o custo cognitivo de entender aquilo quando for realmente necessário trabalhar nele, e eu acrescentaria também o custo de manter as ferramentas iguais.
      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.

    • É exatamente igual a uma dívida real.
      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.
    • Já ouvi isso ser chamado de dívida tática em vez de “dívida técnica”.
    • Normalmente é por causa da velocidade.