Quando as asserções devem permanecer no código de produção? [fechadas]

166

Há uma discussão em comp.lang.c ++. Moderada sobre se as asserções, que em C ++ existem apenas em construções de depuração por padrão, devem ser mantidas no código de produção ou não.

Obviamente, cada projeto é único, portanto, minha pergunta aqui não é tanto se as afirmações devem ser mantidas, mas , nesses casos, isso é recomendável / não é uma boa idéia.

Por afirmação, quero dizer:

  • Uma verificação em tempo de execução que testa uma condição que, quando falsa, revela um bug no software.
  • Um mecanismo pelo qual o programa é interrompido (talvez após um trabalho de limpeza realmente mínimo).

Eu não estou necessariamente falando sobre C ou C ++.

Minha opinião é que, se você é programador, mas não possui os dados (que é o caso da maioria dos aplicativos comerciais para desktop), você deve mantê-los, porque uma asserção falha mostra um erro e você não deve ir com um bug, com o risco de corromper os dados do usuário. Isso força você a testar fortemente antes de enviar, e torna os bugs mais visíveis, facilitando assim a localização e a correção.

Qual a sua opinião / experiência?

Felicidades,

Carl

Veja a pergunta relacionada aqui


Respostas e atualizações

Hey Graham,

Uma afirmação é erro, pura e simples e, portanto, deve ser tratada como uma. Como um erro deve ser tratado no modo de liberação, você realmente não precisa de asserções.

É por isso que prefiro a palavra "bug" ao falar sobre afirmações. Isso torna as coisas muito mais claras. Para mim, a palavra "erro" é muito vaga. Um arquivo ausente é um erro, não um bug, e o programa deve lidar com isso. Tentar desreferenciar um ponteiro nulo é um bug, e o programa deve reconhecer que algo cheira a queijo ruim.

Portanto, você deve testar o ponteiro com uma asserção, mas a presença do arquivo com o código de tratamento de erros normal.


Ligeiro fora do tópico, mas um ponto importante na discussão.

Como aviso, se suas afirmações entrarem no depurador quando falharem, por que não? Mas existem muitas razões pelas quais um arquivo não pode estar completamente fora do controle do seu código: direitos de leitura / gravação, disco cheio, dispositivo USB desconectado etc. etc. Como você não tem controle sobre isso, sinto que as afirmações são não é o caminho certo para lidar com isso.

Carl


Thomas,

Sim, tenho o Código Completo e devo dizer que discordo totalmente desse conselho em particular.

Digamos que seu alocador de memória personalizado estrague tudo e zere um pedaço de memória que ainda é usado por outro objeto. Por acaso zero um ponteiro que esse objeto desreferencia regularmente, e um dos invariantes é que esse ponteiro nunca é nulo, e você tem algumas afirmações para garantir que ele continue assim. O que você faz se o ponteiro de repente for nulo. Você apenas se () está em volta dele, esperando que funcione?

Lembre-se, estamos falando de código de produto aqui, portanto não há como entrar no depurador e inspecionar o estado local. Este é um bug real na máquina do usuário.

Carl

desconhecidos
fonte
3
Há um post relacionado interessante na Engenharia de Software SE (embora a discussão é c ++ focada): deve haver afirmações em compilações
sonny

Respostas:

84

Asserções são comentários que não ficam desatualizados. Eles documentam quais estados teóricos são pretendidos e quais não devem ocorrer. Se o código for alterado para que os estados permitam a alteração, o desenvolvedor será informado em breve e precisará atualizar a asserção.

MSalters
fonte
16
@jkschneider: testes de unidade são para testar coisas dentro do escopo do código do programa. As asserções são para garantir que as suposições nas quais o código se baseia sejam realmente verdadeiras, antes que o código continue processando sob essas suposições. Eles são "documentação" no sentido de que, se não for o caso, o programa interromperá, declarará a suposição e indicará que a suposição não se manteve. Você também pode ler a asserção como documentação disso no código, é claro.
2
Essa é a melhor resposta, pois relaciona afirmações a comentários, o que é uma maneira útil de pensar sobre eles. Eles são um passo à frente dos comentários, porque são constantemente testados à máquina durante o desenvolvimento, mas sempre devem ser significativos para os leitores humanos primeiro. Assim como os comentários, eles não devem fazer parte da lógica ou da execução final. Assim como os comentários, você pode deixá-los ou retirá-los, dependendo se o idioma é compilado ou interpretado, seus planos de implementação, estratégia de ofuscação etc. Vi um caso em que um comentário realmente causou um bug, mas isso foi um estranho.
25414 DaveMalley
1
Mais especificamente, as declarações são informativas e não funcionais . Uma afirmação por si só não afeta o fluxo ou os resultados do programa. Uma exceção, por outro lado, altera o fluxo do programa e, portanto, resulta.
yoyo
59

Permitam-me citar o código completo de Steve McConnell. A seção Asserções é 8.2.

Normalmente, você não deseja que os usuários vejam mensagens de asserção no código de produção; asserções são principalmente para uso durante o desenvolvimento e manutenção. As asserções são normalmente compiladas no código no momento do desenvolvimento e compiladas do código para produção.

No entanto, posteriormente na mesma seção, este conselho é dado:

Para código altamente robusto, afirme e lide com o erro de qualquer maneira.

Penso que, desde que o desempenho não seja um problema, deixe a afirmação inserida, mas, em vez de exibir uma mensagem, faça-a gravar em um arquivo de log. Acho que esse conselho também está no Código Completo, mas não o estou encontrando agora.

Thomas Owens
fonte
7
Acho que o que a segunda citação do Code Complete significa é que você deve ter a afirmação - que será compilada no código de produção - e também deve ter "if (! Condition) {AttemptGracefulRecovery ();}", ou seja, don deixe a violação dos invariantes do programa travar o programa.
yoyo
34

Deixe as afirmações ativadas no código de produção, a menos que você tenha medido que o programa é executado significativamente mais rápido com elas desativadas.

se não vale a pena medir para provar que é mais eficiente, não vale a pena sacrificar a clareza por uma aposta no desempenho. "- Steve McConnell 1993

http://c2.com/cgi/wiki?ShipWithAssertionsOn

David Cary
fonte
11
Na verdade, a pior coisa que acontece é quando o código falha devido a algo que NÃO é uma afirmação. Se o código definitivamente falhar mais tarde com 100% de probabilidade, a afirmação deve estar lá. Se você recusar um ponteiro, precisará implicitamente afirmar que ele não é nulo antes. Se você dividir por um número, afirma que não é zero. Retire as afirmações e todos os locais do acidente não serão detectados. O problema real não é estruturar o programa para permitir que os subsistemas falhem e sejam reiniciados por um cão de guarda.
Rob
5
assert ref != null;é diferente de if (ref == null) throw new IllegalArgumentException();Você não deve usar o primeiro para pré-condições que podem ser falsas. Você precisa usar assertpara coisas que não podem ser falsas. Exemplo, int i = -1 * someNumber; i = i * i;mais tarde, para lembrar às pessoas que ié positivo,assert i > 0;
Capitão Man
1
"Na verdade, a pior coisa que acontece é quando o código falha devido a algo que NÃO é uma afirmação. Se o código definitivamente falhar mais tarde com 100% de probabilidade, então a afirmação deve estar lá". - Esta é uma falsa dicotomia.
22430 Robbie Grant
2
@ RobertGrant: Para muitos programas, travar está longe de ser a pior coisa que pode acontecer. Para um programa que deve verificar a integridade de um projeto de construção ou ponte, relatar erroneamente que um projeto é sólido pode ser a pior coisa que ele poderia fazer. Para um programa exposto ao mundo externo, mas com acesso somente leitura a dados confidenciais, vazar esses dados pode ser pior do que qualquer outra coisa que o programa possa fazer. A noção de que um acidente sem indicação significativa da causa é "a pior coisa que poderia acontecer" ignora muitos perigos que são muito piores.
supercat 14/01
@ supercat Eu não estava de acordo com o comentário que estava citando.
Rob Grant
21

Se você está pensando em deixar afirmações na produção, provavelmente está pensando errado. O ponto principal das afirmações é que você pode desativá-las na produção, porque elas não fazem parte da sua solução. Eles são uma ferramenta de desenvolvimento, usada para verificar se suas suposições estão corretas. Mas no momento em que você entra em produção, você já deve ter confiança em suas suposições.

Dito isso, há um caso em que ativarei asserções na produção: se encontrarmos um bug reproduzível na produção que estamos tendo dificuldades em reproduzir em um ambiente de teste, pode ser útil reproduzir o bug com as asserções ativadas em produção, para ver se eles fornecem informações úteis.

Uma pergunta mais interessante é a seguinte: na sua fase de teste, quando você desativa as afirmações?

MiguelMunoz
fonte
4
Penso que as afirmações nunca devem ser incluídas no código de produção. asserções NÃO são erros, elas são projetadas para desenvolvedores. As asserções devem viver apenas no código de teste. Ter uma falha no aplicativo é devido a uma falha de asserção no desenvolvimento inaceitável e desleixado. Os desenvolvedores precisam ir além para lidar com os erros normalmente.
iksnae
9
Se for inevitável que você trate, devido a um ponteiro nulo passado para um fn; não há escolha em lidar com isso explicitamente. Ou você tem uma maneira de lidar com a condição normalmente (porque ela pode vir de informações do mundo exterior) ou trava em um local DOCUMENTADO com uma declaração, em vez de em um local aleatório que pode ter corrompido as coisas ao longo do caminho. como a afirmação é tratada deve ser uma decisão por módulo. Talvez o seu watchdog reinicie o processo ou limpe um pedaço de memória desse módulo para redefini-lo para o estado inicial (objeto virtual "reboot").
Rob
1
Eu acho que essa é uma visão limitada do uso de afirmações. Sempre registro as declarações no console e no armazenamento em nuvem e as deixo em produção. Deixar afirmações confirma que minhas suposições permanecem corretas, mesmo no código de produção e no uso da produção. Só porque o código foi executado algumas vezes com êxito na depuração com afirmações ativadas não significa que os usuários não encontrarão uma maneira de passar valores diferentes pelo mesmo caminho de código.
SafeFastExpressive
O ponto principal da declaração de afirmação é que você pode ativar ou desativar as verificações. Se você deixá-los em produção, por que usar a declaração assert?
18718 MiguelMunoz
1
As asserções geralmente tornam o sistema lento. Como não foram projetados para produção, são livres para serem lentos e ineficientes, o que pode ser necessário para a execução de determinados testes. Por exemplo, uma vez a Microsoft adicionou um recurso de recálculo rápido ao Excel. Quando uma célula foi alterada, esse recurso limitou o recálculo apenas às células que precisavam. Eles testaram isso com uma afirmação que recalculou toda a planilha e comparou os resultados. Isso fez a versão de desenvolvimento rodar muito lentamente, mas também eliminou muitos bugs. Quando eles lançaram esse recurso, ele se mostrou muito confiável.
MiguelMunoz 13/02/19
16

As asserções nunca devem permanecer no código de produção. Se uma afirmação em particular parece útil no código de produção, não deve ser uma afirmação; deve ser uma verificação de erro tempo de execução, ou seja, algo codificado como este: if( condition != expected ) throw exception.

O termo 'asserção' passou a significar "uma verificação apenas no tempo de desenvolvimento que não será executada em campo".

Se você começar a pensar que afirmações podem chegar ao campo, inevitavelmente também começará a fazer outros pensamentos perigosos, como se perguntar se vale a pena fazer alguma afirmação. Não há afirmação que não valha a pena fazer. Você nunca deve estar se perguntando "devo afirmar isso ou não?" Você só deve estar se perguntando "Existe algo que eu esqueci de afirmar?"

Mike Nakis
fonte
6

A menos que o perfil mostre que as asserções estão causando problemas de desempenho, eu digo que elas também devem permanecer no lançamento da produção.

No entanto, acho que isso também exige que você lide com as falhas de asserção de maneira um tanto graciosa. Por exemplo, eles devem resultar em um tipo geral de diálogo com a opção de (automaticamente) relatar o problema aos desenvolvedores e não apenas encerrar ou travar o programa. Além disso, você deve tomar cuidado para não usar asserções para condições que realmente permite, mas possivelmente não gosta ou considera indesejadas. Essas condições devem ser tratadas por outras partes do código.

Anders Sandvig
fonte
A meu ver, o principal objetivo de uma asserção de produção é como um recuo de emergência: permitir que o programa continue muito provavelmente causará danos suficientemente graves para impedir que seja mais importante do que qualquer outra coisa que o programa possa estar fazendo. Ter uma boa mensagem de erro seria bom, se possível, mas isso é apenas de importância secundária.
supercat 14/01
5

No meu C ++, defino REQUIRE (x), que é como assert (x), exceto que lança uma exceção se a asserção falhar em uma versão.

Como uma afirmação com falha indica um bug, ela deve ser tratada com seriedade, mesmo em uma versão do Release. Quando o desempenho do meu código é importante, geralmente utilizarei REQUIRE () para código de nível superior e assert () para código de nível inferior que deve ser executado rapidamente. Também uso REQUIRE em vez de afirmar se a condição de falha pode ser causada por dados passados ​​de código gravado por terceiros ou por corrupção de arquivo (idealmente, eu projetaria o código especificamente para ser bem comportado em caso de corrupção de arquivo, mas nós nem sempre tem tempo para fazer isso.)

Eles dizem que você não deve mostrar essas mensagens de afirmação aos usuários finais, porque eles não as entenderão. Assim? Os usuários finais podem enviar um e-mail com uma captura de tela ou algum texto da mensagem de erro, o que ajuda na depuração. Se o usuário simplesmente disser "travou", você terá menos capacidade de corrigi-lo. Seria melhor enviar automaticamente as mensagens de falha de afirmação pela Internet, mas isso só funciona se o usuário tiver acesso à Internet e você puder obter sua permissão.

Qwertie
fonte
Eles também dizem que você não deve mostrar essas mensagens assert para hackers porque são pistas valiosas para quebrar em.
DaveWalley
4

Se você deseja mantê-los, substitua-os pelo tratamento de erros. Nada pior do que um programa simplesmente desaparecendo. Não vejo nada de errado em tratar certos erros como erros sérios, mas eles devem ser direcionados para uma seção do seu programa equipada para lidar com eles, coletando dados, registrando-os e informando ao usuário que seu aplicativo teve alguma condição indesejada e está saindo.

bruceatk
fonte
2

Desde que sejam tratados como qualquer outro erro, não vejo problema com ele. Lembre-se de que as afirmações com falha em C, como em outros idiomas, acabam de sair do programa e isso geralmente não é suficiente para os sistemas de produção.

Existem algumas exceções - o PHP, por exemplo, permite criar um manipulador personalizado para falhas de asserção, para que você possa exibir erros personalizados, fazer log detalhado, etc., em vez de apenas sair.

Steve M
fonte
2

Nosso software de servidor de banco de dados contém asserções de produção e depuração. As asserções de depuração são exatamente isso - elas são removidas no código de produção. As asserções de produção só acontecem se (a) existir alguma condição que nunca deve existir e (b) não for possível recuperar com segurança essa condição. Uma asserção de produção indica que um erro no software foi encontrado ou ocorreu algum tipo de corrupção de dados.

Como este é um sistema de banco de dados e estamos armazenando dados potencialmente críticos para a empresa, fazemos o possível para evitar dados corrompidos. Se existir uma condição que possa nos levar a armazenar dados incorretos, nós imediatamente declaramos, revertemos todas as transações e paramos o servidor.

Dito isso, também tentamos evitar afirmações de produção em rotinas críticas ao desempenho.

Graeme Perrow
fonte
5
Eu chamaria sua "afirmação de produção" de "exceção" e a codificaria como tal.
25814 DaveWalley
1
Você provavelmente está certo, mas o produto foi originalmente escrito em C. Mesmo quando o mudamos para C ++, os compiladores que usamos em algumas de nossas plataformas não suportam corretamente exceções. A maior parte do código antigo não foi reescrita no C ++, portanto, essas asserções ainda são usadas.
Graeme Perrow
1

Vejo afirmações como testes de unidade em linha. Útil para um teste rápido durante o desenvolvimento, mas, em última análise, essas afirmações devem ser refatoradas para serem testadas externamente em testes de unidade.

Weston
fonte
Afirmar: Declare um fato ou crença. Eles não são para teste (apenas), mas para afirmar o que você acha que é verdadeiro (ou seja, suas suposições), para que seu programa não continue se suas suposições estiverem erradas por algum motivo. Por exemplo, este é um uso válido de asserções: assert (pow (1,0) <1). Não é realmente um local apropriado para a verificação de erros, porque, se isso não for verdade, praticamente toda a matemática moderna está errada e ... bem, como você começaria a lidar com isso? Lidar com essa suposição incorreta está fora do escopo do programa; você aceita com fé. Mas você verifica, de qualquer maneira.
1

Acho melhor lidar com todos os erros que estão no escopo e usar asserções para suposições que afirmamos serem verdadeiras.

ou seja, se o seu programa estiver abrindo / lendo / fechando um arquivo, não será possível abrir o arquivo no escopo - é uma possibilidade real, que seria negligente ignorar, em outras palavras. Portanto, isso deve ter um código de verificação de erro associado a ele.

No entanto, digamos que seu fopen () esteja documentado como sempre retornando um identificador de arquivo aberto válido. Você abre o arquivo e o passa para a função readfile ().

Essa função readfile, neste contexto, e provavelmente de acordo com sua especificação de design, pode assumir que ele obterá um ptr de arquivo válido. Portanto, seria um desperdício adicionar código de tratamento de erros para o caso negativo, em um programa tão simples. No entanto, deve pelo menos documentar a suposição, de alguma forma - garantir de alguma forma - que esse é realmente o caso, antes de continuar sua execução. Na realidade, não deve assumir que sempre será válido, caso seja chamado incorretamente ou seja copiado / colado em outro programa, por exemplo.

Então, readfile () {assert (fptr! = NULL); ..} é apropriado nesse caso, embora o tratamento completo de erros não seja (ignorando o fato de que realmente ler o arquivo exigiria algum sistema de tratamento de erros).

E sim, essas afirmações devem permanecer no código de produção, a menos que seja absolutamente necessário desativá-las. Mesmo assim, você provavelmente deve desativá-los apenas em seções críticas de desempenho.

Lee
fonte
1

Suponha que um pedaço de código esteja em produção e atinja uma afirmação que normalmente seria acionada. A afirmação encontrou um bug! Exceto que não, porque a afirmação está desativada.

Então o que acontece agora? Ou o programa irá (1) travar de maneira não informativa em um ponto mais afastado da origem do problema ou (2) executar alegremente até a conclusão, provavelmente dando o resultado errado.

Nenhum dos cenários é convidativo. Deixe as afirmações ativas, mesmo na produção.

Marmota assassina
fonte
0

Eu raramente uso asserções para qualquer outra coisa que compila a verificação do tipo de tempo. Eu usaria uma exceção em vez de uma asserção apenas porque a maioria dos idiomas é criada para lidar com eles.

Eu ofereço um exemplo

file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");

contra

file = create-some-file();
ASSERT(file.exists());

Como o aplicativo lidaria com a afirmação? Prefiro o antigo try catchmétodo de lidar com erros fatais.

roo
fonte
2
Exceções são para situações incomuns que você espera encontrar em um aplicativo em funcionamento, como no seu exemplo aqui. As asserções são para situações que você espera nunca encontrar. Portanto, se você os encontrar, deve haver um erro no seu código. No seu exemplo, e exceção é claramente a abordagem correta. Mas afirmações ainda são muito úteis para detectar bugs.
MiguelMunoz
0

Na maioria das vezes, quando uso asserção em java (a palavra-chave assert), adiciono automaticamente alguns códigos de produção depois. De acordo com o caso, pode ser uma mensagem de log, uma exceção ... ou nada.

De acordo com mim, todas as suas afirmações são críticas no lançamento do desenvolvedor, e não na produção. Alguns deles devem ser mantidos, outros devem ser descartados.

Nicolas
fonte
0

As afirmações não são erros e não devem ser tratadas como erros. Quando uma asserção é lançada, isso significa que há um erro no seu código ou, alternativamente, no código que está chamando o seu código.

Existem alguns pontos para evitar a ativação de asserções no código de produção: 1. Você não deseja que o usuário final veja uma mensagem como "ASSERTION falhou MyPrivateClass.cpp linha 147. O usuário final NÃO é você engenheiro de QA. 2. A ASSERÇÃO pode influenciar o desempenho

No entanto, há um forte motivo para deixar afirmações: a afirmação pode influenciar o desempenho e o tempo, e infelizmente isso às vezes é importante (especialmente em sistemas embarcados).

Costumo votar em deixar a afirmação ativada no código de produção, mas certificando-me de que essas impressões de asserções não sejam expostas ao usuário final.

~ Yitzik

Yitshak Yarom
fonte
Não, afirmações são para fatos assumidos. Se nosso código retornar 27 quando for assumido que SEMPRE retornará 25, o problema também pode ser um bug em nossas suposições físicas sobre o universo: talvez esses dois bits tenham mudado para o valor possível 5, pela primeira vez na história da computação. A asserção existe para confirmar que seu código ainda está operando sob as premissas para as quais foi escrito. Se a física sair pela janela, seu código deve notar, deixar a unidade em paz e sair enquanto está à frente;) Mas sim, não é um erro no código e que tipo de manipulação de erro você poderia fazer?
Permita que refine minha opinião: 1. Asserção verifique nossas suposições. Se nossas suposições estão erradas, isso significa que há um erro no código OUR. 2. Nosso código não deve afirmar o uso de nosso código. isto é, uma função não deve falhar na afirmação se algo estiver errado na entrada do usuário. Retornaremos o erro e o usuário deve lidar (ele pode garantir o sucesso) 3. Prefiro deixar a afirmação na produção - mas o comportamento provavelmente será modificado. Concordo que não há tratamento de erros apropriado. Asserção com falha == bug .. mas o sistema pode se reiniciar em vez de parar e aguardar a reinicialização.
Yitshak Yarom
1
NECESSARIAMENTE não significa que há um erro. Por exemplo, muitos dos projetos em que estou envolvido vêm com uma lista de fatos assumidos na documentação. Essas suposições existem para proteger o código dos desenvolvedores de serem chamados de buggy, quando pessoas de negócios podem ter dito a coisa errada ou quando nenhuma informação está disponível de terceiros sobre variáveis ​​específicas, por exemplo. As asserções podem ser usadas para verificar se o programa deve / não deve ser executado, se os sistemas de terceiros estão corretos, não apenas se a TI está correta.
-8

Uma afirmação é erro, pura e simples e, portanto, deve ser tratada como uma.

Como um erro deve ser tratado no modo de liberação, você realmente não precisa de asserções.

O principal benefício que vejo para as afirmações é uma quebra condicional - elas são muito mais fáceis de configurar do que vasculhar as janelas do VC para configurar algo que requer 1 linha de código.

graham.reeds
fonte
2
Usar afirmações como pontos de interrupção condicionais é realmente irritante por várias razões. O mais importante é que essas declarações confundem outros desenvolvedores da equipe - como eles saberiam se é um erro quando essa declaração foi disparada ou se alguém deixou seu ponto de interrupção condicional no código?
lego
Não haveria se afirmações não estavam no código em primeiro lugar. E se você os estivesse usando para monitorar apenas o código, você os veria (a menos que os estivesse verificando na árvore de origem). Já trabalhei em lugares que afirmam praticamente tudo. Ter que clicar cerca de 60 vezes no início do programa, porque o servidor de documentação de ajuda não está disponível, fica cansativo muito rapidamente.
graham.reeds