Como reduzir o número de bugs durante a codificação?

30

Ninguém é perfeito, e não importa o que façamos, produziremos código que possui bugs de tempos em tempos. Quais são alguns métodos / técnicas para reduzir o número de erros que você produz, tanto ao escrever um novo software quanto ao alterar / manter o código existente?

GSto
fonte
Um bom método é fazer um design mais inicial (não muito, mas o suficiente para tornar seu código mais estruturado e mais fácil de entender).
Giorgio

Respostas:

58

Evite codificação sofisticada. Quanto mais complicado o código, maior a probabilidade de haver erros. Geralmente em sistemas modernos, o código claramente escrito será rápido e pequeno o suficiente.

Use bibliotecas disponíveis. A maneira mais fácil de não ter erros ao escrever uma rotina de utilidade é não escrevê-la.

Aprenda algumas técnicas formais para as coisas mais complicadas. Se houver condições complicadas, pregue-as com caneta e papel. Idealmente, conheça algumas técnicas de prova. Se eu puder provar que o código está correto, é quase sempre bom, exceto por erros grandes, burros e óbvios, fáceis de corrigir. Obviamente, isso só vai tão longe, mas às vezes você pode argumentar formalmente sobre coisas pequenas, mas complicadas.

Para o código existente, aprenda a refatorar: como fazer pequenas alterações no código, geralmente usando uma ferramenta automatizada, que torna o código mais legível sem alterar o comportamento.

Não faça nada muito rapidamente. Levar um pouco de tempo para fazer as coisas direito, verificar o que você fez e pensar no que está fazendo pode render muito tempo depois.

Depois de escrever o código, use o que for necessário para torná-lo bom. Os testes de unidade são ótimos. Geralmente, você pode escrever testes com antecedência, o que pode ser um ótimo feedback (se feito de forma consistente, esse é um desenvolvimento orientado a testes). Compile com as opções de aviso e preste atenção aos avisos.

Peça a alguém para olhar o código. Revisões de código formais são boas, mas podem não ser em um momento conveniente. Solicitações pull, ou similares, se o seu scm não suportá-las, permitir revisões assíncronas. A verificação de amigos pode ser uma revisão menos formal. A programação em pares garante que dois pares de olhos olhem para tudo.

David Thornley
fonte
x2 - o que Ryan disse.
precisa saber é o seguinte
2
também a maioria dos idiomas pode ser mais ou menos exigente. Você quer que seja o mais exigente possível.
1
"Aprenda algumas técnicas formais para as coisas mais complicadas." ... por exemplo?
Dan Rosenstark
1
@Yar: Espero algo como os sistemas explicados neste livro: amazon.com/Verification-Sequential-Concurrent-Programs-Computer/… ; embora eu deva dizer que um livro específico é extremamente seco e sem graça, provavelmente há livros muito melhores por aí (mas é o único que li).
Joeri Sebrechts
30

Os testes de unidade permitem reduzir o número de erros que aparecem pela segunda vez. Se você encontrar um bug no seu código, escrever um teste de unidade garantirá que ele não volte mais tarde. (Além disso, às vezes é difícil pensar em todos os casos e escrever milhares de testes de unidade antecipadamente)

Ryan Hayes
fonte
3
"Pensar em todos os casos antecipadamente" leva a interfaces limpas e totalmente especificadas, o que só pode ser uma coisa boa. Os testes de unidade são difíceis de escrever se você os estiver adaptando ao código que não foi projetado com o teste em mente.
Mike Seymour
1
Se você pode, você deveria. Infelizmente, na maioria dos casos, vi os testes de unidade como algo que custa mais do que apenas "corrigir bugs muito rapidamente". Portanto, se puder, escreva os testes com antecedência, mas se não for considerado "econômico", escrevê-los juntamente com correções de bugs o ajudará a aumentar o tempo ao longo do tempo, sem gastar muito do orçamento em escrever apenas testes de unidade .
Ryan Hayes
4
"O teste mostra a presença, não a ausência de bugs." - E. Dijkstra. Dito isto, os testes automatizados são definitivamente uma maneira muito útil de manter e aumentar a qualidade do software.
limist
9

+1 nos dois comentários do teste de unidade.

Além disso, defina o nível de aviso mais alto que seu compilador oferece e verifique se os avisos são tratados como erros. Os erros geralmente se escondem nesses erros "errôneos".

Da mesma forma, invista em ferramentas de análise estática que são executadas em tempo de compilação (eu as vejo como um nível extra de avisos do compilador).

Alan
fonte
+1 para o comentário da análise estática. É inestimável para obter todas as informações que de graça :)
Morten Jensen
9

Além do que foi mencionado:

  • Não ignore os códigos de erro - por exemplo, não assuma que você obteve um resultado válido, que um arquivo foi criado com sucesso, etc ... Porque algum dia algo acontecerá.
  • Não assuma que seu código nunca entrará em alguma condição e, portanto, "é seguro ignorar essa condição".
  • Teste seu código e teste-o por outra pessoa. Acho que sou a pior pessoa para testar meu próprio código.
  • Faça uma pausa, depois releia seu código e veja se você "perdeu o óbvio". Muitas vezes acontece comigo.

Estou esquecendo muitas outras coisas no momento, mas as outras certamente pensarão nelas. :)

MetalMikester
fonte
7
E se você tiver tanta certeza de que a condição X nunca acontecerá ... use uma declaração para garantir que, quando a condição X acontecer, você saberá sobre isso (por meio de uma exceção, registro ou qualquer outra coisa).
precisa saber é o seguinte
@MetalMikester: Os testes de unidade são bons. Porém, com linguagens de alto nível e boas bibliotecas, a maioria dos erros rígidos exige testes de integração e regressão.
Vector
9

Eu desenvolvi um estilo de programação bastante funcional, apesar de minhas principais linguagens serem C ++ e Python. Descobri que, se eu passar todo o contexto para uma função (ou método), essa função precisa fazer seu trabalho e retornar os dados significativos que estou procurando, meu código se tornará muito mais robusto.

O estado implícito é o inimigo e, na minha experiência, é a fonte número 1 de bugs. Esse estado pode ser variável global ou membro, mas se os resultados dependem de algo que não é passado para a função, você está solicitando problemas. Claramente, não é possível eliminar o estado, mas minimizá-lo tem enormes efeitos positivos na confiabilidade do programa.

Também gosto de dizer aos meus colegas de trabalho que todo ramo (se, por enquanto,? :) é um bug provável. Não sei dizer qual será a manifestação do bug, mas quanto menos comportamento condicional o seu código tiver, maior a probabilidade de ele estar livre de erros simplesmente devido ao fato de que a cobertura do código durante a execução será mais consistente.

Entenda, todas essas coisas também têm efeitos positivos no desempenho. Ganhar!

traço-tom-bang
fonte
Na minha experiência, pode rapidamente se tornar entediante repassar todo o estado para cada chamada, se os métodos forem tão pequenos quanto deveriam ser. Esse problema pode ser resolvido usando muitas pequenas classes imutáveis ​​com vida útil curta do objeto. Dessa forma, você pode armazenar o estado temporário como campos e descartar o objeto quando não precisar mais do estado. :-)
Jørgen Fogh
Outra consideração para esse caso de se tornar tedioso é que talvez você esteja tentando passar muito estado. :)
dash-tom-bang
Em muitos casos, isso é verdade, mas geralmente não é. Em alguns domínios, você precisa acessar muitos estados, mesmo que não tenha muitos estados mutáveis . Atualmente, estou trabalhando em um gerador de código em que preciso acessar várias tabelas de símbolos. Eu não quero passá-los para todo e qualquer método.
21414 Jornier Fogh
8
  • Escreva menos código que faça mais.
  • Pense nas implicações de baixo nível e nas ramificações de alto nível
  • Contemple a abstração que você está criando no seu código.
  • Escreva apenas a complexidade essencial, se possível.
Paul Nathan
fonte
Eu ia escrever um grande post que resume como "escreva menos que faça mais" (o IOW conhece e usa as ferramentas disponíveis para você). Vou apenas marcar isso com +1.
Kaz Dragon
1
Mas tome cuidado para não obter código sofisticado ao tentar obter menos código.
gablin
1
@ gablin: a fantasia está nos olhos de quem vê em muitos aspectos. Hoje, posso escrever e ler códigos nos quais eu teria ficado estupefato há 4 anos. Hoje Haskell é chique para mim. :)
Paul Nathan
8

Uma resposta um pouco menos técnica: não programe quando estiver cansado (9h / dia é suficiente), bêbado ou assado. Quando estou cansado, não tenho a paciência necessária para escrever um código limpo.

Alexandru
fonte
2
Geralmente, a maioria dos programadores leva vários anos para perceber isso. É um ponto importante.
1011 Jeff Davis
7

Escreva testes de unidade e testes de integração .

ysolik
fonte
5

Algumas ótimas respostas aqui sobre ferramentas e testes de unidade. A única coisa que posso adicionar a eles é esta:

Envolva seus testadores o mais cedo possível

Se você tem uma equipe de teste, não caia na armadilha de tratá-los como guardiões da qualidade do código e de detectar seus defeitos. Em vez disso, trabalhe com eles e envolva-os o mais cedo possível (em projetos ágeis, será desde o início do projeto, mas sempre podemos encontrar maneiras de envolvê-los mais cedo, se realmente tentarmos).

  • Descubra qual é o plano de teste deles. Revise seus casos de teste com eles - você está cobrindo todos eles com seu código?
  • Peça a eles que entendam os requisitos. É o mesmo que o seu?
  • Dê a eles versões de trabalho iniciais para realizar testes exploratórios - você ficará surpreso com as melhorias que eles sugerirão.

Ter um bom relacionamento de trabalho com seus testadores significa que você pode detectar preconceitos e defeitos ruins muito cedo, antes que eles possam causar algum dano. Isso também significa que os testadores se sentem capacitados para ajudar no design do produto e detectar problemas de usabilidade quando houver tempo para corrigi-los.

Paddyslacker
fonte
4

Ferramentas de análise estática

Plugins e aplicativos como o FindBugs rastreiam seu código e encontram lugares onde há possíveis erros. Locais onde as variáveis ​​não são inicializadas e usadas ou apenas coisas loucas que 9 vezes em 10, facilitam a ocorrência de erros. Ferramentas como essa me ajudam a impedir que minha cabeça de osso se mova pela estrada, mesmo que ainda não seja um bug.

PS: Lembre-se de sempre pesquisar por que uma ferramenta diz que algo está ruim. Nunca é demais aprender (e nem tudo está certo em todas as situações).

Ryan Hayes
fonte
3

Inspeção de código ou outras formas de revisão por pares, como programação em pares.

Revisões de código estruturado, como a inspeção de Fagan, podem ser pelo menos tão eficazes e eficientes quanto os testes de unidade e até provaram ser melhores do que os testes de unidade em alguns casos. As inspeções também podem ser usadas anteriormente no ciclo de vida do software e com artefatos diferentes do código.

Revisões por pares em software de Karl Wiegers é um ótimo livro sobre esse assunto.

Michael
fonte
2

Além de todas as outras sugestões aqui, ative todos os avisos possíveis para o mais alto nível de sensibilidade e trate-os como erros. Use também todas as ferramentas de fiapos que o idioma possui.

Você ficaria surpreso com quantos erros simples podem ser detectados por avisos e quantas dessas coisas simples se traduzem em erros reais no seu código.

greyfade
fonte
2

Muitas boas respostas aqui, mas gostaria de acrescentar algumas coisas. Certifique-se de realmente entender o requisito. Eu já vi muitos bugs quando o usuário pensou que o requisito significava X e o programador achou que significa Y. Retorne para esclarecer sobre requisitos ruins ou ambíguos. Eu sei que todos nós gostamos de entrar e codificar, mas quanto mais tempo gastamos para garantir a compreensão, menos retrabalho e correção de erros haverá.

Conheça o negócio que você está apoiando, você verá muitas coisas nos requisitos que estão faltando ou que precisam de mais explicações. Saiba que se você executar a tarefa Y conforme indicado, ela interromperá o recurso Z.

Entenda sua estrutura de banco de dados. Muitos bugs são resultado de uma consulta sintaticamente correta, mas retorna os resultados incorretos. Aprenda a reconhecer quando seus resultados parecem engraçados. Se estou escrevendo uma consulta de relatório complexa, sempre solicito a um especialista técnico para revisar meus resultados antes de marcá-la como pronta para o trabalho, pois eles inevitavelmente verão algo nos dados que perdi. Depois, anote o que eles captaram e não se esqueça de que na próxima vez que fizer algo semelhante.

HLGEM
fonte
1

Eu acho que a técnica mais importante é levar o seu tempo . Se você acha que precisa de dois dias para codificar um novo módulo, mas o seu chefe o força a codificar apenas em um dia ... seu código provavelmente será mais problemático.

Um dos livros que li há algum tempo atrás dizia que você não deveria viver com janelas quebradas , porque as pessoas não se importariam se alguém quebrasse ... A codificação é a mesma, todos se importam em ser os primeiros a fazer algo ruim mas rápido , mas ninguém se importa com um código infernal , com muitos bugs e design e estilo muito ruins.

gaze
fonte
1

Sigo a prática do teste-código-teste em vez do código-teste-código-teste. Isso me ajuda a pensar em casos de uso e a enquadrar adequadamente a lógica

viv
fonte
1

Use ferramentas de inspeção de código como ReSharper ou IDEs como IntelliJ IDEA que alertam sobre muitos bugs de copiar e colar e outros, por exemplo, apontando variáveis ​​que "são gravadas, mas nunca lidas". Me salvou muito tempo.

DonJoe
fonte
1

Surpreendentemente, os três pontos muito importantes a seguir ainda não foram mencionados:

  • Use afirmações liberalmente. A pergunta que você deve sempre se perguntar não é "devo afirmar isso?" mas "há algo que eu esqueci de afirmar?"

  • Opte pela imutabilidade. (Use final / somente leitura liberalmente.) Quanto menos estado mutável você tiver, menos coisas poderão dar errado.

  • Não otimize prematuramente. Muitos programadores são desviados das preocupações com o desempenho, fazendo com que eles involuntariamente desnecessariamente seu código e bastardize seus projetos, sem sequer saber de antemão se o desempenho será um problema. Primeiro, construa seu produto de software da maneira acadêmica, desconsiderando o desempenho; depois, veja se o desempenho é ruim; (Provavelmente não.) Se houver algum problema de desempenho, encontre um ou dois lugares onde você pode fornecer otimizações algorítmicas legais e formais que farão com que seu produto atenda aos requisitos de desempenho, em vez de ajustar e hackear toda a sua base de códigos para aperte ciclos de relógio aqui e ali.

Mike Nakis
fonte