2 pontos por GN⁺ 2025-01-14 | 1 comentários | Compartilhar no WhatsApp
  • Debugging, de David J. Agans, aborda os fundamentos da depuração para encontrar a causa e corrigir um bug depois que ele é descoberto, oferecendo princípios aos quais vale a pena recorrer repetidamente tanto para desenvolvedores iniciantes e intermediários quanto para os mais experientes
  • O livro é composto por 9 regras que conectam entendimento do sistema, reprodução da falha, observação, divisão e conquista, controle de mudanças, trilha de auditoria, revisão de premissas, perspectiva externa e validação da correção a casos práticos
  • Embora apareçam tecnologias antigas e exemplos fora da computação, o ponto central não é uma ferramenta específica, mas sim uma forma de pensar para restringir o problema, aplicável à depuração de hardware e software em geral
  • Para bugs difíceis, como problemas intermitentes, o conselho de Make it Fail é especialmente útil, embora seja uma pena que o termo Heisenbug não seja tratado diretamente
  • Ao contrário de livros que explicam como usar o GDB ou escrever testes, o foco está no panorama geral da depuração, então o uso de ferramentas e testes de regressão precisa ser complementado com outros materiais

O problema que o livro mira

  • Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems, de David J. Agans, trata do processo de encontrar a causa e realmente corrigir um bug depois que ele é detectado
  • Em vez de se concentrar em uma tecnologia ou ferramenta específica, organiza os princípios de depuração necessários para desenvolvedores de software e hardware de computador
  • É especialmente adequado para desenvolvedores iniciantes e intermediários, e também ajuda os mais experientes a recuperar fundamentos que podem ser esquecidos em situações urgentes
  • Um de seus pontos fortes é condensar, em princípios e casos, conhecimentos de depuração que normalmente são aprendidos pela experiência

As 9 regras de depuração

  • Entenda o sistema

    • Leia o manual, compreenda a estrutura geral e entenda os princípios básicos e o funcionamento detalhado
    • Verifique também o que as ferramentas usadas mostram e o que elas escondem
  • Faça falhar

    • Execute o problema novamente e volte ao início para estimular diretamente as condições de falha
    • Em vez de imitar a falha, provoque a falha real e encontre as condições não controladas que produzem bugs intermitentes
    • Registre tudo, não confie demais em estatísticas e aceite que eventos raros podem de fato acontecer
    • Não descarte as ferramentas de depuração; use-as para expor o problema
  • Pare de pensar e olhe

    • Antes de começar reparos complexos com base em suposições, obtenha primeiro dados observáveis
    • Examine a falha e os detalhes, crie instrumentação interna ou adicione instrumentação externa
    • Não evite investigar a fundo, mas tenha cuidado com o efeito Heisenberg, em que a própria observação pode alterar o comportamento
    • Use hipóteses apenas como ferramenta para restringir a área de busca, não como conclusão
  • Divida e conquiste

    • Reduza a área de investigação por aproximação sucessiva (successive approximation) e determine de que lado o bug está
    • Use padrões de teste visíveis com clareza e parta de um estado ruim para restringir a causa
    • Remova primeiro bugs já conhecidos e ruído para simplificar o que está sendo investigado
  • Mude apenas uma coisa por vez

    • Isole os fatores centrais e entenda o que está errado antes de corrigir
    • Altere os testes também um de cada vez e compare com os casos normais
    • Por fim, verifique o que mudou desde a última vez em que tudo funcionou
  • Mantenha uma trilha de auditoria

    • Registre em uma trilha de auditoria o que foi feito, em que ordem e quais foram os resultados
    • Como até detalhes que parecem triviais podem ser a causa, registre os eventos conectando-os entre si
    • A trilha de auditoria do processo de projeto também ajuda nos testes e deve ser documentada
  • Verifique o plugue

    • Desconfie das premissas tidas como óbvias e confira tudo novamente desde o começo
    • Inclua também nas verificações as próprias ferramentas usadas para encontrar o problema
  • Obtenha uma nova perspectiva

    • Quando travar sozinho, busque novos insights por meio de outra pessoa ou de uma forma diferente de explicar o problema
    • Só de explicar o problema para um manequim, seus pensamentos podem se organizar
    • Aproveite a especialização, ouça quem tem experiência e priorize compartilhar sintomas e pedir ajuda em vez do orgulho
  • Se você não corrigiu, então não está corrigido

    • Depois da correção, confirme se o problema realmente foi resolvido e verifique se a sua mudança eliminou a causa real
    • Problemas não desaparecem sozinhos; é preciso corrigir tanto a causa quanto o processo

Como os casos dão vida aos princípios

  • A lista de regras pode parecer seca à primeira vista, mas as explicações detalhadas e os relatos de caso trazem os princípios para situações reais
  • Muitos casos entram em detalhes técnicos, o que pode ser pesado para alguns leitores
  • Há exemplos com tecnologias antigas, mas isso não é um grande problema porque o essencial são os princípios, não a tecnologia específica
  • Nem todos os casos tratam de computação; há também um exemplo divertido relacionado à fiação de uma casa
  • Quem não tiver nenhuma familiaridade com hardware e software de computador pode ter dificuldade para acompanhar muitos dos exemplos
  • Depois da explicação das regras, vêm histórias em que várias regras são aplicadas em conjunto, exercícios simples para o leitor, dicas de help desk e comentários de encerramento

Pontos de destaque e limitações

  • O princípio “Pare de pensar e olhe” é especialmente importante
    • Porque muita gente tenta corrigir problemas na base do palpite antes de reunir dados que confirmem ou refutem a hipótese
  • “Se você não corrigiu, então não está corrigido” também é uma regra que marca bastante
    • É preciso verificar não apenas se foi corrigido, mas também qual era a causa e por que a correção funcionou
  • A discussão sobre “estimular a falha, não imitá-la” não é tão clara quanto outras partes do livro, mas ainda assim vale a pena entendê-la
  • Problemas intermitentes costumam ser os mais difíceis de lidar, e o livro oferece conselhos diretos sobre isso em Make it Fail
  • Heisenberg é mencionado, mas o termo comum no desenvolvimento de software, Heisenbug, não é tratado
    • Heisenbug significa um bug que desaparece ou muda de comportamento quando tentamos observá-lo ou isolá-lo

Diferença em relação a outros materiais

  • O livro se diferencia de manuais de ferramentas ou livros de testes por colocar no centro os princípios básicos da depuração
  • Debugging with GDB: The GNU Source-Level Debugger, de Richard Stallman e outros, explica principalmente técnicas ou comandos de ferramentas específicas
  • Há também materiais com conselhos gerais, como Guide to Faster, Less Frustrating Debugging, de Norman Matloff, mas eles não têm um escopo tão amplo quanto o livro de Agans
  • Livros de teste como Software Testing Techniques, de Boris Beizer, focam em escrever testes para encontrar bugs, dedicando relativamente menos espaço a como corrigir os bugs já descobertos
  • Depois de encontrar um bug, é preciso adicionar um teste correspondente à suíte de testes de regressão, mas testes e regressão estão fora do escopo deste livro

Materiais complementares e pontos fracos

  • O site complementar do livro, debuggingrules.com, traz links para informações relacionadas e um pôster com as 9 regras para baixar e imprimir
  • É uma pena que a lista completa de sub-regras, importante para entender as regras, não esteja reunida em uma única página no livro ou no site
  • Teria sido ainda mais útil se houvesse conselhos e exemplos mais específicos sobre ferramentas comuns e tipos de problema, como depuradores simbólicos, sondas de lógica digital e ddd sobre gdb
  • Também parece haver espaço para um livro separado que expanda as mesmas regras para resolução de problemas gerais fora da computação, embora os exemplos deste livro sejam técnicos demais para leitores não ligados à área
  • Os princípios básicos podem parecer óbvios à primeira vista, mas iniciantes precisam aprendê-los e profissionais experientes precisam relembrá-los repetidamente, e este livro é adequado tanto para esse aprendizado quanto para essa revisão

1 comentários

 
GN⁺ 2025-01-14
Opiniões do Hacker News
  • Acho que a tentação de acrescentar uma “correção” ao código quebrado que já existe, tentando fazê-lo funcionar, é a mais nociva
    Código quebrado é difícil de consertar porque há pontos demais que podem ser alterados, e é muito mais fácil quebrar um código que funciona
    Quando uma sequência inteira de luzes de Natal não acende, trocar as lâmpadas uma por uma falha se houver várias lâmpadas queimadas
    Em vez disso, é preciso começar por um exemplo mínimo funcional e ir acrescentando aos poucos até encontrar o ponto em que o erro aparece; na prática, muitas vezes recomeçar do zero economiza tempo

    • Se aprender com o problema não for a prioridade, talvez o mais rápido seja trocar a sequência inteira de luzes, em vez de testar as lâmpadas uma a uma
      Em um problema difícil de capturar em produção, alguns membros da equipe reescreveram a rotina problemática, e a versão reescrita foi implantada antes que o restante terminasse a depuração
      Pelo menos uma vez, nunca chegamos a encontrar o bug original porque não dava para gastar tempo indefinidamente em um problema “corrigido”
  • A regra número 0 é não entrar em pânico
    Prazos e clientes irritados atrapalham o pensamento claro; por isso, um bom gestor confiável precisa proteger os engenheiros dessa pressão para que eles possam se concentrar em resolver o problema

    • Concordo muito. Em plantões de SEV-2, quando vários gerentes das equipes afetadas entram na chamada e cada um começa a se intrometer, fica extremamente frustrante
      Um bom gerente nos moveu para outra chamada e disse: “ignore o que eles estão dizendo e concentre-se; eu cuido disso”, e depois disso meu respeito por esse gerente aumentou muito
    • Há uma história em um livro segundo a qual, em submarinos nucleares, há uma barra de latão diante do painel de instrumentos e das alavancas, e os engenheiros são treinados para, quando surge um problema, não tocar imediatamente nas alavancas, mas “segurar a barra”
    • Devagar é suave, suave é rápido
      Se não há tempo para fazer direito, por que você acha que haverá tempo para fazer duas vezes?
    • Um ex-chefe dizia que seu papel era ser um “guarda-chuva de merda”
      Ou seja, bloquear as coisas que caem de cima para que os engenheiros possam se concentrar no trabalho principal
    • Um princípio que acompanha isso é sempre ter um bom plano de rollback
      É muito melhor poder voltar para uma versão funcional e depurar sem a pressão de uma crise
  • Para a regra 4, “dividir para conquistar”, git bisect ajuda muito
    Se há um commit bom e, depois dele, entre dezenas ou centenas de commits, um commit ruim, em poucos passos dá para estreitar até o commit ou o código problemático
    Um exemplo de uso está em https://nickjanetakis.com/blog/using-git-bisect-to-help-find...
    Usei esse método durante uma consultoria ao vivo em uma base de código grande e desconhecida para reduzir rapidamente o escopo; sem isso, a área potencialmente quebrada seria ampla demais

    • Por causa do git bisect, mantenho a disciplina de que todo commit que entra no branch “real” deve compilar individualmente, passar nos testes conhecidos na época e, até onde se sabe, ser implantável
      Considero isso mais importante do que preservar cada tecla digitada ou manter até o último commit “Fixes.”. Esses princípios tornam a busca binária inútil
      Não uso com frequência, mas, quando acerta em cheio mesmo uma única vez nos bugs maiores e mais misteriosos, entrega de uma vez pistas que valeriam dias, então compensa bastante
    • Ao depurar um problema de configuração de rede nos anos 1990, um colega mais experiente me ensinou o princípio geral por trás do git bisect
      É a abordagem de comparar um sistema quebrado com um sistema funcional e eliminar sistematicamente as diferenças para encontrar o defeito
      Ela se aplica além de software ou hardware, e foi ótimo, no passado, quando havia dois jet skis iguais: dava para consertar um comparando com o outro
    • O ponto central aqui é o princípio mais geral de busca binária, mais do que o git bisect em si
      Ele pode ser usado não só em intervalos de commits, mas também para dividir o espaço do sistema
      Por exemplo, se um workflow de 10 etapas quebrou, você pode verificar se a etapa 5 ainda está normal, ou estreitar se é ou não um problema de hardware
      Isso é especialmente importante quando a causa do problema talvez não seja um commit de código no repositório que você está bisectando no momento
    • bisect é excelente, mas é preciso distinguir entre as “regras”, que são a filosofia e a forma de pensar do livro, e as “ferramentas”, que são conselhos práticos
      Quem começa por “qual ferramenta devo usar?” está em desvantagem em relação a quem começa por “isso não funcionava antes?”
      O mundo está cheio de ferramentas, e você enlouquece se tentar guardar todas na cabeça; é melhor adotar a filosofia primeiro
    • Acrescento um texto que escrevi sobre git bisect run. É uma pequena ferramenta realmente incrível
      https://andrewrepp.com/git_bisect_run
  • É preciso verificar se você está editando o arquivo certo na máquina certa

    • É uma variação de “verifique se está na tomada”
      Hoje em dia, sempre adiciono temporariamente uma linha que causa um erro fatal, para confirmar que estou no arquivo certo e, dependendo da situação, também na linha certa
    • Só Deus sabe quanto tempo foi desperdiçado editando arquivos gerados, arquivos de outra versão ou esquecendo de salvar
    • O maior princípio que sempre repeti para mim mesmo e para as pessoas a quem ensinei é: confirme que o código em execução é aquele que você acha que é
    • Por isso, primeiro é preciso fazê-lo quebrar de outro jeito
      É para confirmar que a minha alteração realmente tem efeito
  • Há regras adicionais
    “A culpa é toda minha”: pode até ser um bug do compilador ou uma falha de hardware, mas isso é muito raro; antes de qualquer coisa, deve-se suspeitar das minhas alterações no código
    “Quando encontrar um bug, procure também a família e os amigos dele”: é preciso pensar e verificar onde mais o mesmo tipo de coisa pode ter acontecido
    “Otimize primeiro para o usuário, em segundo lugar para o programador de manutenção, e por último para o computador”

    • A primeira regra é conhecida em The Pragmatic Programmer como “select não está quebrado
      Há um resumo em https://blog.codinghorror.com/the-first-rule-of-programming-...
    • A abordagem de “como pode ser um bug, vamos criar um caso de teste que eu possa relatar à mailing list” também é útil às vezes
      Em geral, no processo de reduzir o código que gera o erro para um caso mais simples, você acaba encontrando o bug na sua própria lógica
      Uma ou duas vezes, de fato sobrou algo que valia relatar aos desenvolvedores, e geralmente eram bibliotecas com poucas baterias de teste por terem no máximo algumas centenas de usuários
    • Eu sempre tive a mentalidade de que “a culpa é minha”, mas foi sinceramente humilhante ver minha workstation Linux cair o tempo todo por causa do i9-13900K
      No fim, foi um grande alívio descobrir que não era um erro de código aparentemente impossível, e sim um problema de CPU
    • É mais saudável partir do princípio de que o meu código está errado
      Ainda assim, o melhor é fazer mais algumas rodadas de busca binária na cadeia de causa e efeito para ter certeza
    • Sobre “família e amigos”, algumas vezes o bug que eu estava procurando apareceu enquanto eu corrigia problemas periféricos pequenos e aparentemente não relacionados
  • O conselho “entenda o sistema: leia o manual, leia tudo em profundidade, conheça o básico, conheça o roadmap, entenda as ferramentas e procure os detalhes” soa um tanto estranho
    Parece dizer que, se houver um bug no código, primeiro você deve ler o manual inteiro de 700 páginas da biblioteca que está usando, ler 7 livros relacionados e só olhar para o bug um ou dois meses depois
    Fico curioso se existe sequer um programador que siga esse conselho na prática

    • Este texto foi escrito em 2004, o ano do IPO do Google
      Atwood e Spolsky criaram o Stack Overflow em 2008, e era uma época em que as pessoas conheciam livros por nomes como “Camel book” e simplesmente tinham conhecimento
      0. https://stackoverflow.blog/2021/12/14/podcast-400-an-oral-hi...
      1. https://www.perl.com/article/extracting-the-list-of-o-reilly...
    • Aqui, a interpretação parece ser um pouco diferente
      “Leia tudo em profundidade” não quer necessariamente dizer “leia primeiro o manual inteiro de 700 páginas da biblioteca que você está usando”
      Se houver um problema com git bisect, em vez de juntar alguns trechos do Stack Overflow, você pode entender o assunto um pouco mais a fundo em https://git-scm.com/docs/git-bisect
    • No essencial, está certo
      O erro é pensar que o resultado de meses de trabalho seria apenas corrigir de qualquer jeito um único bug
      O objetivo é corrigir de verdade o máximo possível dessa classe de bugs e, antes de tudo, deixar de escrevê-los
      A alternativa é cair de paraquedas em um sistema desconhecido, mexer em coisas sem entendê-las até os testes ficarem verdes, abrir um PR e torcer para não ter piorado mais nada; fazer isso rotineiramente é quase um pesadelo
      Além disso, se o manual da biblioteca que você usa tem 700 páginas, há uma boa chance de você estar usando a biblioteca errada
  • Como 10ª etapa, não deveríamos adicionar o bug aos testes de CI para evitar regressões?
    É preciso verificar que, antes da correção, o CI falha, e que depois da correção ele passa

    • O maior repositório JavaScript puro em que já trabalhei, um projeto de cerca de 150 mil linhas, tinha essa regra, e ela foi realmente uma salvação
      Especialmente porque havia commits com mais de 5 anos e, por ser um componente/biblioteca, existiam vários hacks estranhos para IE
    • Nem sempre acho que valha a pena
      Alguns testes demoram ou são complexos de escrever e também exigem manutenção, e é preciso aceitar que uma suíte de testes não verifica todos os casos de borda
      Isso pode significar que um bug que chegou à produção pode voltar, mas, se foi um erro simples, talvez ele não tenha mais probabilidade de reaparecer do que centenas de outros possíveis erros
      No fim, depende da situação, e escrever testes não é de graça
    • De forma mais geral, é preciso documentar
      Já vi inúmeras vezes a causa profunda ser reativada e o mesmo problema voltar, ou ninguém saber que eu havia corrigido algo e todos continuarem usando workarounds como se o bug ainda existisse
      Mesmo depois de chegar ao “corrigido!”, deixar um registro simples e uma análise de causa raiz ajuda outras pessoas
    • O que fazer com testes de correção de bugs de anos atrás?
      Depois que os testes se acumulam por muito tempo, quão rápido o CI consegue rodar, e ainda faz sentido mantê-los indefinidamente?
  • Se você quiser incutir esse modo de pensar nas crianças, em si mesmo ou em outras pessoas, recomendo pelo menos o seguinte
    The Martian by Andy Weir https://en.wikipedia.org/wiki/The_Martian_(Weir_novel)
    https://en.wikipedia.org/wiki/Zen_and_the_Art_of_Motorcycle_...
    https://en.wikipedia.org/wiki/The_Three-Body_Problem_(novel)
    To Engineer Is Human - The Role of Failure in Successful Design By Henry Petroski
    https://pressbooks.bccampus.ca/engineeringinsociety/front-ma...
    https://en.wikipedia.org/wiki/Surely_You%27re_Joking,_Mr._Fe...!

    • Concordo. Acho que Zen and the Art of Motorcycle Maintenance é o livro que melhor captura a arte de resolver problemas
      Gosto especialmente do conceito de “gumption traps”
      Se você caiu na armadilha da rigidez de valores, inevitavelmente vai ficar mais lento; então é preciso reduzir o ritmo de propósito, revisitar por onde já passou e verificar se as coisas que você considerava importantes eram realmente importantes
      A passagem que diz que não há nada de errado em simplesmente olhar para a máquina por um tempo e que, se você observar como quem observa uma linha de pesca, chegará um momento em que um pequeno fato perguntará cautelosamente se você está interessado, é quase uma orientação para a vida
    • Não sei que parte de Three Body Problem tem a ver com depuração, resolução de problemas ou planejamento
      Acho que os acontecimentos simplesmente ocorrem por grandes saltos lógicos, e na prática é mais uma fantasia coberta por um trocadilho científico bem raso
  • Escrevi algo parecido alguns anos atrás. Na época, eu não tinha lido o livro original mencionado aqui
    https://explog.in/notes/debugging.html
    O zine de depuração da Julia Evans também é muito bom: https://wizardzines.com/zines/debugging-guide/

  • Mesmo depois de depurar com sucesso, o trabalho não acabou
    O ponto central de “Three Questions About Each Bug You Find” <http://www.multicians.org/thvv/threeq.html> são três perguntas
    Este erro existe em outros lugares? Qual é o próximo bug escondido por trás deste? O que deve ser feito para evitar bugs desse tipo?