¿Cuándo deben permanecer las afirmaciones en el código de producción? [cerrado]

166

Existe una discusión en comp.lang.c ++. Moderada sobre si las afirmaciones, que en C ++ solo existen en las compilaciones de depuración de forma predeterminada, deben mantenerse en el código de producción o no.

Obviamente, cada proyecto es único, por lo que mi pregunta aquí no es tanto si se deben mantener las afirmaciones, sino en qué casos es recomendable / no es una buena idea.

Por afirmación, quiero decir:

  • Una verificación en tiempo de ejecución que prueba una condición que, cuando es falsa, revela un error en el software.
  • Un mecanismo por el cual se detiene el programa (tal vez después de un trabajo de limpieza realmente mínimo).

No estoy necesariamente hablando de C o C ++.

Mi propia opinión es que si usted es el programador, pero no posee los datos (que es el caso con la mayoría de las aplicaciones comerciales de escritorio), debe mantenerlos encendidos, porque una aserción fallida muestra un error, y no debe ir con un error, con el riesgo de corromper los datos del usuario. Esto lo obliga a realizar una prueba exhaustiva antes de enviar, y hace que los errores sean más visibles, por lo que es más fácil de detectar y corregir.

¿Cuál es tu opinión / experiencia?

Salud,

Carl

Ver pregunta relacionada aquí


Respuestas y actualizaciones

Hola Graham

Una afirmación es error, pura y simple y, por lo tanto, debe manejarse como tal. Dado que un error debe manejarse en el modo de lanzamiento, realmente no necesita afirmaciones.

Es por eso que prefiero la palabra "error" cuando hablo de afirmaciones. Hace las cosas mucho más claras. Para mí, la palabra "error" es demasiado vaga. Un archivo que falta es un error, no un error, y el programa debería solucionarlo. Intentar desreferenciar un puntero nulo es un error, y el programa debe reconocer que algo huele a queso malo.

Por lo tanto, debe probar el puntero con una aserción, pero la presencia del archivo con el código normal de manejo de errores.


Ligeramente fuera de tema, pero un punto importante en la discusión.

Como aviso, si sus afirmaciones entran en el depurador cuando fallan, ¿por qué no? Pero hay muchas razones por las que un archivo podría no existir que están completamente fuera del control de su código: derechos de lectura / escritura, disco lleno, dispositivo USB desconectado, etc. Como no tiene control sobre él, creo que las afirmaciones son no es la forma correcta de lidiar con eso.

Carl


Thomas

Sí, tengo Code Complete, y debo decir que estoy totalmente en desacuerdo con ese consejo en particular.

Supongamos que su asignador de memoria personalizado se arruina y pone a cero un trozo de memoria que todavía utiliza algún otro objeto. Sucede que pongo a cero un puntero que este objeto desreferencia regularmente, y uno de los invariantes es que este puntero nunca es nulo, y tiene un par de afirmaciones para asegurarse de que se mantenga así. ¿Qué haces si el puntero de repente es nulo? ¿Solo si () a su alrededor, esperando que funcione?

Recuerde, aquí estamos hablando del código del producto, por lo que no hay que irrumpir en el depurador e inspeccionar el estado local. Este es un error real en la máquina del usuario.

Carl

desconocido
fuente
3
Hay una publicación relacionada interesante en el Software Engineering SE (aunque la discusión está centrada en c ++): ¿Debería haber afirmaciones en las compilaciones de lanzamiento
Sonny

Respuestas:

84

Las afirmaciones son comentarios que no pasan de moda. Documentan qué estados teóricos están destinados y qué estados no deberían ocurrir. Si se cambia el código de modo que los estados permitieron el cambio, el desarrollador pronto es informado y necesita actualizar la afirmación.

MSalters
fuente
16
@jkschneider: las pruebas unitarias son para probar cosas dentro del alcance del código del programa. Las afirmaciones son para garantizar que las suposiciones en las que se basa el código sean realmente verdaderas, antes de que el código continúe procesándose bajo esas suposiciones. Son "documentación" en el sentido de que, si resultan no ser el caso, el programa abortará, declarará el supuesto e indicará que el supuesto no se mantuvo. También puede leer la afirmación como documentación de eso en el código, por supuesto.
2
Esta es la mejor respuesta porque relaciona afirmaciones con comentarios, lo cual es una forma útil de pensar sobre ellos. Están un paso por delante de los comentarios porque se prueban constantemente en máquina durante el desarrollo, pero siempre deben ser significativos para los lectores humanos primero. Al igual que los comentarios, no deberían ser parte de la lógica o la ejecución final. Al igual que los comentarios, puede dejarlos o sacarlos dependiendo de si el idioma está compilado o interpretado, sus planes de implementación, estrategia de ofuscación, etc. He visto un caso en el que un comentario realmente causó un error, pero eso fue uno raro
DaveWalley
1
Más específicamente, las afirmaciones son informativas y no funcionales . Una afirmación por sí sola no tiene ningún efecto en el flujo o los resultados del programa. Una excepción, por otro lado, altera el flujo del programa y, por lo tanto, los resultados.
yoyo
59

Permítame citar el Código completo de Steve McConnell. La sección sobre Afirmaciones es 8.2.

Normalmente, no desea que los usuarios vean mensajes de afirmación en el código de producción; Las afirmaciones son principalmente para uso durante el desarrollo y mantenimiento. Las afirmaciones normalmente se compilan en el código en el momento del desarrollo y se compilan a partir del código para la producción.

Sin embargo, más adelante en la misma sección, se dan estos consejos:

Para un código altamente robusto, afirme y luego maneje el error de todos modos.

Creo que siempre que el rendimiento no sea un problema, deje la afirmación, pero en lugar de mostrar un mensaje, haga que escriba en un archivo de registro. Creo que ese consejo también está en Code Complete, pero no lo estoy encontrando en este momento.

Thomas Owens
fuente
77
Creo que lo que la segunda cita de medios de código completo es que usted debe tener la aserción - que se compilan en el código de producción - y usted debe también tener "si AttemptGracefulRecovery {();} (condición!)", Es decir, Don No permita que la violación de los invariantes del programa bloquee el programa.
yoyo
34

Deje las afirmaciones activadas en el código de producción, a menos que haya medido que el programa se ejecuta significativamente más rápido con ellos desactivados.

si no vale la pena medir para demostrar que es más eficiente, entonces no vale la pena sacrificar la claridad para una apuesta de rendimiento ". - Steve McConnell 1993

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

David Cary
fuente
11
En realidad, lo peor que sucede es cuando el código falla debido a algo que NO es una afirmación. Si el código definitivamente se bloqueará más adelante con una probabilidad del 100%, entonces la afirmación debe estar absolutamente allí. Si desmarca un puntero, debe afirmar implícitamente que antes no es nulo. Si divide por un número, afirma que no es cero. Elimine las afirmaciones y todas las ubicaciones de los accidentes no están INDOCUMENTADAS. El verdadero problema no es estructurar el programa para permitir que los subsistemas se bloqueen y sean reiniciados por un perro guardián.
Rob
55
assert ref != null;es diferente a if (ref == null) throw new IllegalArgumentException();No debería usar el primero para condiciones previas que podrían ser falsas. Debe usar assertpara cosas que no pueden ser falsas. Ejemplo, int i = -1 * someNumber; i = i * i;luego para recordarle a la gente que ies positivo,assert i > 0;
Capitán Man
1
"En realidad, lo peor que sucede es cuando el código se bloquea debido a algo que NO es una afirmación. Si el código definitivamente se bloqueará más adelante con una probabilidad del 100%, entonces la afirmación debe estar allí". - Esta es una falsa dicotomía.
Rob Grant
2
@RobertGrant: Para muchos programas, el bloqueo está lejos de ser lo peor que puede suceder. Para un programa que se supone que verifica la solidez del diseño de un edificio o puente, informar erróneamente que un diseño es sólido puede ser lo peor que podría hacer. Para un programa que está expuesto al mundo exterior pero tiene acceso de solo lectura a datos confidenciales, filtrarlos puede ser peor que cualquier otra cosa que el programa pueda hacer. La noción de que un choque sin una indicación significativa de la causa es "lo peor que podría pasar" ignora muchos peligros que son mucho peores.
supercat
@supercat No estaba de acuerdo con el comentario que estaba citando.
Rob Grant
21

Si incluso está pensando en dejar afirmaciones en producción, probablemente esté pensando en que están equivocadas. El objetivo de las afirmaciones es que puede desactivarlas en la producción, ya que no son parte de su solución. Son una herramienta de desarrollo, utilizada para verificar que sus suposiciones sean correctas. Pero cuando entre en producción, ya debería tener confianza en sus suposiciones.

Dicho esto, hay un caso en el que activaré las aserciones en producción: si encontramos un error reproducible en producción que estamos teniendo dificultades para reproducir en un entorno de prueba, puede ser útil reproducir el error con las aserciones activadas en producción, para ver si proporcionan información útil.

Una pregunta más interesante es esta: en su fase de prueba, ¿cuándo desactiva las afirmaciones?

Miguel Muñoz
fuente
44
Creo que las afirmaciones nunca deberían incluirse en el código de producción. Las afirmaciones NO son errores, están diseñadas para desarrolladores. Las afirmaciones solo deben vivir en el código de prueba. Tener un bloqueo de la aplicación se debe a que la afirmación falla y el desarrollo es inaceptable y descuidado. Los desarrolladores deben hacer un esfuerzo adicional para manejar los errores con gracia.
iksnae
9
Si es inevitable que se bloquee dado un puntero nulo pasado a fn; no hay elección sobre tratarlo explícitamente. O tiene alguna forma de manejar con gracia la condición (porque puede provenir de la entrada del mundo exterior), o se bloquea en una ubicación DOCUMENTADA con una afirmación, en lugar de en un lugar aleatorio que puede haber corrompido las cosas en el camino. Sin embargo, cómo se maneja la aserción debe ser una decisión por módulo. Tal vez su perro guardián reinicie el proceso, o limpie una porción de memoria para ese módulo para restablecerlo al estado de inicio (objeto suave "reiniciar").
Rob
1
Creo que esta es una visión limitada del uso de afirmaciones. Siempre registro las afirmaciones tanto en la consola como en el almacenamiento en la nube, y las dejo en producción. Dejar afirmaciones confirma que mis suposiciones siguen siendo correctas incluso en el código de producción y en el uso de la producción. El hecho de que el código se haya ejecutado varias veces con éxito en la depuración con afirmaciones no significa que los usuarios no encontrarán una manera de pasar diferentes valores a través de la misma ruta de código.
SafeFastExpressive
El objetivo de la declaración de aserción es que puede activar o desactivar las comprobaciones. Si los deja en producción, ¿por qué usar la declaración de aserción?
MiguelMunoz
1
Las afirmaciones a menudo ralentizan el sistema. Como no están diseñados para la producción, pueden ser lentos e ineficientes, lo que puede ser necesario para realizar ciertas pruebas. Por ejemplo, Microsoft una vez agregó una función de recálculo rápido a Excel. Cuando una celda cambió, esta característica limitó el recálculo a solo las celdas que lo necesitaban. Probaron esto con una afirmación que recalculó toda la hoja de cálculo y comparó los resultados. Esto hizo que la versión de desarrollo se ejecutara muy lentamente, pero también eliminó muchos errores. Cuando lanzaron esta función, resultó ser muy confiable.
MiguelMunoz
16

Las afirmaciones nunca deben permanecer en el código de producción. Si una afirmación particular parece que podría ser útil en el código de producción, entonces no debería ser una afirmación; debe ser una comprobación de errores en tiempo de ejecución, es decir, codificado como esto algo: if( condition != expected ) throw exception.

El término 'aserción' ha llegado a significar "una verificación solo en tiempo de desarrollo que no se realizará en el campo".

Si comienzas a pensar que las afirmaciones pueden llegar al campo, inevitablemente también comenzarás a hacer otros pensamientos peligrosos, como preguntarte si realmente vale la pena hacer alguna afirmación. No hay afirmación que no valga la pena hacer. Nunca debería preguntarse "¿debería afirmar esto o no?" Solo debes preguntarte "¿Hay algo que olvidé afirmar?"

Mike Nakis
fuente
6

A menos que el perfil muestre que las afirmaciones están causando problemas de rendimiento, digo que también deberían permanecer en la versión de producción.

Sin embargo, creo que esto también requiere que manejes las fallas de aserción de manera elegante. Por ejemplo, deberían dar como resultado un tipo general de diálogo con la opción de informar (automáticamente) el problema a los desarrolladores, y no simplemente cerrar o bloquear el programa. Además, debe tener cuidado de no usar aserciones para condiciones que realmente permite, pero que posiblemente no le gustan o consideran no deseadas. Esas condiciones deben ser manejadas por otras partes del código.

Anders Sandvig
fuente
Desde mi punto de vista, el propósito principal de una afirmación de producción es como un respaldo de emergencia: es muy probable que permitir que el programa continúe causando daños lo suficientemente graves como para evitar que sea más importante que cualquier otra cosa que el programa pueda estar haciendo. Tener un buen mensaje de error sería bueno, si es posible, pero eso es solo de importancia secundaria.
supercat
5

En mi C ++ defino REQUIRE (x) que es como afirmar (x), excepto que arroja una excepción si la afirmación falla en una versión de lanzamiento.

Dado que una afirmación fallida indica un error, debe tratarse seriamente incluso en una versión de lanzamiento. Cuando el rendimiento de mi código es importante, a menudo usaré REQUIRE () para código de nivel superior y afirmar () para código de nivel inferior que debe ejecutarse rápidamente. También uso REQUIRE en lugar de afirmar si la condición de falla puede ser causada por datos pasados ​​del código escrito por un tercero o por corrupción de archivos (de manera óptima, diseñaría el código específicamente para que se comportara bien en caso de corrupción de archivos, pero nosotros no siempre tengo tiempo para hacer eso)

Dicen que no debe mostrar esos mensajes de afirmación a los usuarios finales porque no los entenderán. ¿Entonces? Los usuarios finales pueden enviarle un correo electrónico con una captura de pantalla o algún texto del mensaje de error, que le ayuda a depurar. Si el usuario simplemente dice "se bloqueó", tiene menos capacidad para solucionarlo. Sería mejor enviarte automáticamente los mensajes de fallo de aserción a través de Internet, pero eso solo funciona si el usuario tiene acceso a Internet y puedes obtener su permiso.

Qwertie
fuente
También dicen que no debe mostrar esos mensajes de afirmación a los piratas informáticos porque son pistas valiosas para entrar.
DaveWalley
4

Si desea conservarlos, reemplácelos por manejo de errores. Nada peor que un programa simplemente desapareciendo. No veo nada malo en tratar ciertos errores como errores graves, pero deben dirigirse a una sección de su programa que esté equipada para tratarlos mediante la recopilación de datos, el registro y la información al usuario de que su aplicación ha tenido alguna condición no deseada y está saliendo

bruceatk
fuente
2

Siempre que se manejen como cualquier otro error, no veo ningún problema. Sin embargo, tenga en cuenta que las afirmaciones fallidas en C, como con otros lenguajes, simplemente saldrán del programa, y ​​esto generalmente no es suficiente para los sistemas de producción.

Hay algunas excepciones: PHP, por ejemplo, le permite crear un controlador personalizado para las fallas de aserción para que pueda mostrar errores personalizados, hacer un registro detallado, etc. en lugar de simplemente salir.

Steve M
fuente
2

Nuestro software de servidor de base de datos contiene afirmaciones de producción y depuración. Las afirmaciones de depuración son solo eso: se eliminan en el código de producción. Las afirmaciones de producción solo suceden si (a) existe alguna condición que nunca debería existir y (b) no es posible recuperarse confiablemente de esta condición. Una afirmación de producción indica que se ha encontrado un error en el software o se ha producido algún tipo de corrupción de datos.

Dado que este es un sistema de base de datos y estamos almacenando datos potencialmente críticos para la empresa, hacemos todo lo posible para evitar datos corruptos. Si existe una condición que puede hacer que almacenemos datos incorrectos, afirmamos de inmediato, revertimos todas las transacciones y detenemos el servidor.

Dicho esto, también tratamos de evitar aserciones de producción en rutinas críticas de rendimiento.

Graeme Perrow
fuente
55
Llamaría a su "aserción de producción" una "excepción" y la codificaría como tal.
DaveWalley
1
Probablemente tenga razón, pero el producto se escribió originalmente en C. Incluso cuando lo cambiamos para que fuera C ++, los compiladores que utilizamos en algunas de nuestras plataformas no admitían correctamente las excepciones. La mayor parte del código antiguo no se reescribió en C ++, por lo que estas afirmaciones aún se utilizan.
Graeme Perrow
1

Veo afirmaciones como pruebas unitarias en línea. Útil para una prueba rápida durante el desarrollo, pero en última instancia, esas afirmaciones deben ser refactorizadas para ser probadas externamente en pruebas unitarias.

Weston
fuente
Afirmar: Declarar un hecho o creencia. No son para probar (solo), sino para afirmar lo que crees que es verdad (es decir, tus suposiciones), de modo que tu programa no continuará si tus suposiciones son incorrectas por alguna razón. Por ejemplo, este es un uso válido de aserciones: afirmar (pow (1,0) <1). Realmente no es un lugar apropiado para la verificación de errores, porque si eso no es cierto, entonces casi todas las matemáticas modernas están equivocadas, y ... bueno, ¿cómo comenzarías a manejar eso? Manejar esa suposición incorrecta está fuera del alcance del programa; lo tomas con fe. Pero lo verificas, de todos modos.
1

Creo que es mejor manejar todos los errores que están dentro del alcance, y usar aserciones para los supuestos que estamos afirmando SON verdaderos.

es decir, si su programa está abriendo / leyendo / cerrando un archivo, entonces no se puede abrir el archivo está dentro del alcance; es una posibilidad real, que sería negligente ignorar, en otras palabras. Entonces, eso debería tener un código de verificación de errores asociado.

Sin embargo, supongamos que su fopen () está documentado como siempre devolviendo un identificador de archivo abierto válido. Abre el archivo y lo pasa a su función readfile ().

Esa función de readfile, en este contexto, y probablemente de acuerdo con su especificación de diseño, puede suponer que obtendrá un ptr de archivo válido. Por lo tanto, sería un desperdicio agregar un código de manejo de errores para el caso negativo, en un programa tan simple. Sin embargo, al menos debe documentar la suposición, de alguna manera, asegurarse de alguna manera, de que este es realmente el caso, antes de continuar su ejecución. En realidad, no debe suponer que siempre será válido, en caso de que se llame incorrectamente, o se copie / pegue en otro programa, por ejemplo.

Entonces, readfile () {afirmar (fptr! = NULL); ..} es apropiado en este caso, mientras que el manejo completo de errores no lo es (ignorando el hecho de que leer el archivo requeriría algún sistema de manejo de errores de todos modos).

Y sí, esas afirmaciones deben permanecer en el código de producción, a menos que sea absolutamente necesario desactivarlas. Incluso entonces, probablemente debería deshabilitarlos solo dentro de las secciones críticas de rendimiento.

Lee
fuente
1

Supongamos que una pieza de código está en producción y llega a una afirmación que normalmente se activaría. La afirmación ha encontrado un error! Excepto que no, porque la afirmación está desactivada.

¿Qué pasa ahora? O bien el programa (1) se bloqueará de manera poco informativa en un punto más alejado de la fuente del problema, o (2) se ejecutará alegremente hasta su finalización, probablemente dando un resultado incorrecto.

Ninguno de los escenarios es atractivo. Deje las afirmaciones activas incluso en la producción.

Marmota asesina
fuente
0

Raramente uso aserciones para otra cosa que no sea la verificación de tipo de tiempo de compilación. Usaría una excepción en lugar de una aserción solo porque la mayoría de los idiomas están diseñados para manejarlos.

Ofrezco un ejemplo

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

en contra

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

¿Cómo manejaría la aplicación la afirmación? Prefiero el viejo try catchmétodo de tratar con errores fatales.

roo
fuente
2
Las excepciones son para situaciones inusuales que espera encontrar en una aplicación de trabajo, como en su ejemplo aquí. Las afirmaciones son para situaciones con las que espera no encontrarse nunca. Entonces, si los encuentra, debe haber un error en su código. En su ejemplo, y la excepción es claramente el enfoque correcto. Pero las afirmaciones siguen siendo muy útiles para atrapar errores.
MiguelMunoz
0

La mayoría de las veces, cuando uso la aserción en Java (la palabra clave de aserción) automáticamente agrego algunos códigos de producción después. Según el caso, puede ser un mensaje de registro, una excepción ... o nada.

Según yo, todas sus afirmaciones son críticas en el lanzamiento del desarrollador, no en la producción. Algunos de ellos deben mantenerse, otros deben descartarse.

Nicolas
fuente
0

Las ASERCIONES no son errores y no deben tratarse como errores. Cuando se lanza una afirmación, esto significa que hay un error en su código o, alternativamente, en el código que llama a su código.

Hay algunos puntos para evitar habilitar las aserciones en el código de producción: 1. No desea que su usuario final vea un mensaje como "ERROR EN LA ASERCIÓN MyPrivateClass.cpp línea 147. El usuario final NO es su ingeniero de control de calidad. 2. La ASISTENCIA podría influir en el rendimiento

Sin embargo, hay una razón importante para dejar afirmaciones: ASSERTION puede influir en el rendimiento y el tiempo, y lamentablemente esto a veces es importante (especialmente en los sistemas integrados).

Tiendo a votar por dejar la afirmación en el código de producción pero asegurándome de que estas impresiones de las afirmaciones no estén expuestas al usuario final.

~ Yitzik

Yitshak Yarom
fuente
No, las afirmaciones son para hechos supuestos. Si nuestro código devuelve 27 cuando se supone que SIEMPRE devolverá 25, el problema también podría ser un error en nuestras suposiciones físicas sobre el universo: tal vez esos dos bits cambiaron al valor 5 posible, por primera vez en la historia de la informática. La afirmación está ahí para confirmar que su código todavía está operando bajo los supuestos para los que fue escrito. Si la física se sale por la ventana, su código debería notarlo, deje el disco solo y salga mientras está adelante;) Pero sí, no es un error en el código, y ¿qué tipo de manejo de errores podría hacer?
Permítame refinar mi opinión: 1. La afirmación verifica nuestros supuestos. Si nuestras suposiciones son erróneas, esto significa que hay un error en NUESTRO código. 2. Nuestro código no debe afirmar el uso de nuestro código. es decir, una función no debe fallar en la aserción si algo está mal en la entrada del usuario. Devolveremos el error y el usuario debe manejarlo (puede afirmar el éxito) 3. Prefiero dejar la afirmación en producción, pero el comportamiento probablemente se modificará. Estoy de acuerdo en que no hay un manejo apropiado de errores. Aserción fallida == error ... pero el sistema puede reiniciarse en lugar de detenerse y esperar el reinicio.
Yitshak Yarom
1
NECESARIAMENTE significa que hay un error. Por ejemplo, muchos de los proyectos en los que estoy involucrado incluyen una lista de hechos supuestos en la documentación. Esas suposiciones están ahí para proteger el código de los desarrolladores de ser llamado buggy, cuando la gente de negocios podría haberles dicho algo incorrecto, o cuando no hay información disponible de terceros sobre variables particulares, por ejemplo. Las afirmaciones se pueden usar para verificar que el programa debe / no debe ejecutarse, que los sistemas de terceros son correctos, no solo si es correcto.
-8

Una afirmación es error, pura y simple y, por lo tanto, debe manejarse como tal.

Dado que un error debe manejarse en el modo de lanzamiento, realmente no necesita afirmaciones.

El principal beneficio que veo para las afirmaciones es una ruptura condicional: son mucho más fáciles de configurar que explorar a través de las ventanas de VC para configurar algo que requiere 1 línea de código.

graham.reeds
fuente
2
Usar afirmaciones como puntos de interrupción condicionales es realmente molesto por varias razones. La más importante es que estas afirmaciones confunden a otros desarrolladores en el equipo: ¿cómo sabrían si es un error cuando esa afirmación se disparó o es que alguien dejó su punto de interrupción condicional en el código?
lego
No habría si las afirmaciones no estuvieran en el código en primer lugar. Y si los estuviera utilizando para monitorear el código, solo los vería (a menos que los esté registrando en el árbol de origen). He trabajado en lugares que afirma prácticamente todo. Tener que hacer clic unas 60 veces al inicio del programa porque el servidor de documentación de ayuda no está disponible se vuelve muy pesado.
graham.reeds