Como posso evitar causar bugs no software quando corrijo bugs não relacionados? [duplicado]

48

Sou estagiário de software e recebo bugs para corrigir, bem como recursos para adicionar ao software. Quando eu adiciono recursos, tudo funciona bem. Meu problema é mais com correção de bugs. Eu estou trabalhando em uma base de código extremamente grande (milhões de linhas) com documentação pobre (não há comentários, toneladas de números mágicos, acoplamento entre classes diferentes, etc). A única documentação fornecida é um documento do Word de 8 ou mais páginas. Mas ainda sinto que posso fazer um trabalho melhor com a correção de bugs.

Vou dar um exemplo de uma situação que encontrei, que sinto que poderia ter feito melhor em:

  • Recebi um bug para corrigir cálculos de extensões para um tipo específico de objeto (computação gráfica)
  • Encontrei o problema com o bug e por que ele foi causado. Porque o programa preencheu uma estrutura de dados (array contíguo) com memória representando um ponto cartesiano 3D que aparentemente não deveria ter (Portanto, este ponto seria usado em cálculos de extensão).
  • O bug foi de fato causado por isso. NO ENTANTO, outro trecho de código (em uma classe diferente) usava aritmética de ponteiros para obter este ponto e usá-lo para outro recurso, para fazer o mouse se aproximar desse ponto quando um determinado recurso do software fosse habilitado. No entanto, desde que removi o ponto, consertei o bug que me foi atribuído e causei outro bug no software.

O que posso fazer para que coisas assim não ocorram? Como posso melhorar? Existe algum processo que está faltando?

    
por Jason 14.09.2017 / 05:23
fonte

7 respostas

71

Você não pode ser o único responsável por esses tipos de defeitos. Você é humano e é impossível pensar em grandes sistemas como um todo.

Para ajudar a evitar "erros de regressão" - bugs que são criados acidentalmente ao modificar o sistema, você pode fazer o seguinte:

  • Desenvolva um conjunto abrangente de testes de regressão automatizados. Espero que o seu sistema tenha um grande conjunto de testes. Toda vez que um bug é descoberto em seu sistema, você deve escrever um teste automatizado que reproduz o bug. Em seguida, corrija o bug. Se o sistema regredir, seu "teste de regressão" será interrompido e notificará o desenvolvedor.
  • Inspecione o código. Sempre que você corrigir um bug, é uma boa ideia inspecionar os usos da função que você alterou para tentar identificar as alterações que você introduziu
  • Escreva mais testes. Se ao inspecionar o código, você identificar o código que não é coberto por nenhum teste automatizado, esta é uma boa oportunidade para adicionar alguns.
  • Familiarize-se com o sistema e trabalhe com o software por um longo período. A prática leva à perfeição. Ninguém espera que um desenvolvedor não introduza erros em um novo sistema ou quando é um estagiário. Todos nós fizemos isso.
por 14.09.2017 / 05:42
fonte
10

Você não pode eliminar isso em qualquer sentido completo. Aqui estão alguns passos para diminuir o impacto e diminuir a probabilidade:

  1. Adicione um teste de unidade e um teste de regressão para a funcionalidade ou bug que você está alterando ou corrigindo. Se você não tiver nenhuma estrutura de teste ou configuração de cabos para o seu projeto, configure-a para que seja parte de seu processo. Isso irá melhorar as coisas ao longo do tempo.
  2. Aprimore seu design de código para que o código seja mais fracamente acoplado. Isso significa seguir o SRP, mesmo que isso signifique sacrificar um pouco de DRY. Particularmente, em sistemas OOP, seu código e dados devem estar na mesma classe e os dados devem ser sempre privados. Se isso não fizer sentido, haverá uma incompatibilidade de impedância entre o domínio do problema e o design. Não deve ser possível que uma alteração na estrutura de dados de uma classe afete o comportamento de outra classe.
  3. Converse com seus colegas de trabalho sobre sua correção proposta. Isso pode ter a forma de revisões de código, mas às vezes isso não captura as regressões. Revisões de código geralmente não são para pegar bugs. Como corolário, certifique-se de que o restante da equipe saiba sobre as alterações que você está fazendo e dê tempo para que você receba comentários sobre quais outras partes do sistema podem confiar no código. Faça isso mesmo se você for a pessoa sênior da equipe.

Espero que isso ajude.

    
por 14.09.2017 / 05:55
fonte
8

Existem muitas boas sugestões em outras respostas. Eu adicionaria a eles: sua organização provavelmente tem um problema no nível gerencial.

  • O gerenciamento pode estar priorizando o trabalho de novos recursos sobre o endereçamento de dívidas. O bug que você mencionou é uma evidência de que práticas ruins do passado estão tornando mais caro o desenvolvimento de software livre de bugs hoje. Coletar pontos de dados como esses para informar à administração que existe um problema contínuo causado pela dívida não endereçada, e que seria prudente programar um "marco de qualidade" onde não há novos recursos adicionados, mas a dívida existente é diminuída.

  • O gerenciamento provavelmente ignora o escopo do problema. As ferramentas de análise estática que encontram bugs reais são caras, mas são muito mais baratas do que falhar no mercado porque você enviou um software que perde dados do usuário. Um bom analisador estático lhe dirá que você tem dezenas de milhares de erros não endereçados, onde eles estão e como consertá-los. Isso permite que você identifique o tamanho do problema e a rapidez com que o atenua.

por 14.09.2017 / 13:45
fonte
5

In the matter of reforming things, as distinct from deforming them, there is one plain and simple principle; a principle which will probably be called a paradox. There exists in such a case a certain institution or law; let us say, for the sake of simplicity, a fence or gate erected across a road. The more modern type of reformer goes gaily up to it and says, “I don’t see the use of this; let us clear it away." To which the more intelligent type of reformer will do well to answer: "If you don’t see the use of it, I certainly won’t let you clear it away. Go away and think. Then, when you can come back and tell me that you do see the use of it, I may allow you to destroy it."

- G.K. Chesterson

Assim como todas as outras ideias levantadas aqui, outra coisa valiosa a fazer é descobrir como e por que um bug foi introduzido. Supondo que sua equipe use o Git ou um sistema de controle de origem similar, ferramentas como git blame ou o botão Culpa do GitHub podem ajudar nisso. Depois de identificar as linhas ou códigos inválidos que causaram o erro, use suas ferramentas de controle de origem para descobrir quando foi adicionado, por quem e como parte de uma mudança mais ampla.

Sempre que eu corrijo um bug, tento escrever no rastreador de bugs da organização uma narrativa de como eu acho que o bug veio a existir. Às vezes, é uma história boba e um pouco embaraçosa como "Parece que Bob comentou a linha 96 para fins de teste, acidentalmente a cometeu em commit 5ab314c, e ela foi mesclada porque estávamos correndo para colocar o Frobnicator implantado e não revisando coisas corretamente naquela semana. " Ainda assim, aprendendo que é bom , no ponto em que você está resolvendo o bug, porque ele garante que provavelmente não há uma boa razão para o código quebrado seja do jeito que é e deixa você consertar com mais confiança. Mas às vezes você mergulha no git blame , e descobre que a linha de código "obviamente quebrada" que você estava prestes a mudar foi introduzida em um commit que pretende corrigir algum outro bug que você não tinha pensou, e de repente você percebe que o seu "conserto" vai causar algum dano e que você precisa fazer algo mais esperto.

É claro que, como muitas outras respostas aqui, seria bom se o desenvolvedor anterior tivesse deixado um teste que falharia se você ingenuamente reintroduzisse um bug antigo - um tipo de aviso automático de que a cerca que você está prestes a derrubar tem um propósito que você não pode ver. Mas você não pode controlar quantos testes seus desenvolvedores anteriores escreveram, porque esse é o passado; investigar o histórico do código é uma das poucas técnicas que estão disponíveis para você no momento de corrigir um bug existente para reduzir o risco de quebrar coisas.

    
por 14.09.2017 / 15:26
fonte
1

"another piece of code (in a different class) used pointer arithmetic to get this point and use it for another feature, to make the mouse snap near this point when a certain feature in the software was enabled."

Sim, você recebeu uma base de código de alta manutenção. Parece que nesse caso, em vez de um acoplamento regular entre objetos com interfaces óbvias, havia alguma "mágica" acontecendo, o que, é claro, parou de funcionar assim que foi alterado.

Não há truque de mágica para isso. Muitas técnicas de desenvolvimento de software se concentram no gerenciamento da complexidade para possibilitar a identificação do impacto de uma mudança - mas estas não foram seguidas aqui . Isso é culpa dos desenvolvedores anteriores e dos seniors que supervisionam o projeto.

    
por 14.09.2017 / 13:21
fonte
1

Eu também trabalho em um aplicativo legado frágil. Meu objetivo número um para qualquer mudança é "não quebre nada que estava trabalhando anteriormente". (O objetivo número dois é que o usuário seja capaz de concluir seu trabalho, mesmo que fique um pouco desajeitado ou que você precise invocar alguma solução alternativa; eles não podem ficar absolutamente presos a qualquer momento.)

  1. Existe um livro, Working Effectively with Legacy Code, de Michael Feathers. É bastante útil, mas vou avisá-lo que é 100% sobre a adição de testes automatizados. É tecnicamente sempre possível (e ele explica como fazer isso para muitas situações complicadas), mas nem sempre é viável do ponto de vista comercial. O livro também é útil porque tem pequenas barras laterais divertidas sobre como as coisas podem ficar ruins.
  2. Inspeção manual tediosa de tudo que toca no ponto de mudança. Se você alterou duas funções, foo e bar, faça uma pesquisa de texto completo em toda a sua base de código (você tem TODAS as verificações, certo?) Para as palavras foo e bar. (Note que você vai querer se tornar um usuário avançado de alguma ferramenta de busca como o grep.) É importante não tentar simplificar essa busca ... não apenas procure em arquivos Java, porque sua função pode ser chamada usando reflexão de algo iniciado em algum front-end JavaScript ... apenas um exemplo. Mantenha o acompanhamento até que você tenha testado completamente tudo o que chama sua alteração.
  3. Inverta sua técnica de pesquisa e tente encontrar pontos em seu aplicativo fazendo a mesma coisa sem chamar seu ponto de mudança. Pesquise por código copiado e colado ou mesmo por duas formas e verifique se alterou todos os pontos que precisavam ser alterados. (Em seguida, repita o passo anterior).

Parece um pouco horrível, mas esses aplicativos legados difíceis de manter normalmente ainda estão por aí e sendo atualizados porque estão fazendo algo importante e alguém realmente precisa deles para trabalhar. Tente obter alguma satisfação com isso.

    
por 15.09.2017 / 00:30
fonte
0

Você pode refatorar o código para ocultar o acesso direto a esse membro específico e adicionar um acessador. Isso quebrará qualquer código que dependa do acesso direto ao membro, para que você conheça todos os lugares que o usam.

Como bônus, realoque o membro e preencha seu lugar anterior com lixo - isso quebrará o código que o acessa por meio da aritmética de ponteiros.

Como desenvolvedor, você quer que seu código quebre cedo, quebre com frequência. "Funciona 99,99% ou o tempo" é muito pior para correção de bugs do que "não funciona de jeito nenhum".    (claro, para código de produção, você não quer o "break early, break often")

    
por 14.09.2017 / 09:44
fonte