Hemos estado ejecutando Pex sobre algún código, y ha estado mostrando algunas cosas buenas (cosas buenas, ¡pero mostrándolas antes de que llegue a producción!).
Sin embargo, una de las cosas buenas de Pex es que no necesariamente deja de tratar de encontrar problemas.
Un área que encontramos es que al pasar una cadena, no estábamos buscando cadenas vacías.
Entonces cambiamos:
if (inputString == null)
a
if (string.IsNullOrEmpty(inputString)) // ***
Eso solucionó los problemas iniciales. Pero luego, cuando volvimos a ejecutar Pex, decidió que:
inputString = "\0";
Estaba causando problemas. Y entonces
inputString = "\u0001";
Lo que hemos decidido es que se pueden usar los valores predeterminados si nos encontramos // ***
y que estamos contentos de ver la excepción causada por cualquier otra entrada extraña (y lidiar con ella).
¿Es suficiente?
validation
strings
defensive-programming
Peter K.
fuente
fuente
Respuestas:
Tres preguntas deberían ayudarlo a determinar qué tan defensivo debe estar en su codificación.
Primero: ¿cuáles son las consecuencias de una mala entrada? Si es un mensaje de error en una de las PC de su desarrollador, tal vez no sea tan crítico para estar a la defensiva. ¿Podría provocar interrupciones financieras en los clientes, interrupción de la información contable de IE? ¿Es un sistema en tiempo real donde las vidas están en riesgo? En el escenario de vida / muerte, probablemente debería haber más código de validación y manejo de errores que el código de característica real.
En segundo lugar, ¿cuántas personas reutilizarán esta función o código? ¿Solo tu? ¿Tu departamento? ¿Tu compañía? ¿Sus clientes? Cuanto más amplio es el uso del código, más defensivo.
Tercero: ¿cuál es la fuente de la entrada que estoy validando? Si es una entrada del usuario o de un sitio web público, estaría súper a la defensiva. Si la entrada siempre proviene de su código, sea algo defensivo, pero no pase tiempo indebido haciendo cheques.
Siempre será posible agregar más verificación de errores y validación en un sistema. El punto es que el costo de escribir y mantener este código superará el costo de los problemas causados por errores en el código.
fuente
Los usuarios son malvados, y todo lo que ingresen debe verificarse con el mayor rigor.
Cualquier cosa generada sin el beneficio de la entrada del usuario, o de datos previamente desinfectados, no debería tener que verificarse al mismo nivel. El problema aquí es cuando olvida y utiliza estos métodos en datos incorrectos, porque ha olvidado que el código no se ha endurecido.
Lo único que siempre debe verificar es cualquier cosa que pueda causar un desbordamiento o un bloqueo. No importa cuán profundamente esté enterrado ese método y cuán seguro esté de que esa condición nunca podría ocurrir. Necesitas programarlo de todos modos, solo para aplacar a Murphy.
fuente
Estaría tan a la defensiva como tú necesitas estar. Un poco ambiguo, supongo que sí, pero intentaré explicarlo.
Cuando corrige un método, si ese método tiene parámetros de entrada, debe tomar la decisión sobre lo que espera que sean esos parámetros. En situaciones y lugares dentro de una aplicación, esto será diferente. Por ejemplo, si un método o fragmento de código está aceptando datos de una entrada del usuario, entonces querrá cubrir toda la base del código y manejar cualquier entrada en consecuencia, ya sea a través de un mensaje de error o alguna forma agradable de mostrar datos inaceptables.
Si el método es un ídem API expuesto. No puede controlar lo que está entrando, por lo que debe esperar tratar de cubrir todos los aspectos y programar en consecuencia.
Para los métodos que se producen dentro del motor central de su proyecto, aquí tiene que tomar una decisión. ¿Supongo que los datos que están llegando han sido previamente seleccionados y validados antes de que lleguen o debo realizar las verificaciones necesarias? Supongo que esto depende del nivel conceptual del método y si este es un lugar aceptable para verificar. Entonces, lo que podría considerar es:
1) ¿Es este el único lugar donde tendré que hacer esta verificación? ¿Será necesario verificar esta variable en muchos lugares diferentes para esta condición? Si es así, ¿puedo hacer la verificación una vez más arriba en la cadena y luego asumir la validez después?
2) ¿Se espera que otros componentes del sistema funcionen con los métodos e interfaces que escribo? Si es así, puede controlar mediante declaraciones de afirmación de depuración, excepciones de depuración, comentarios de métodos y arquitectura general del sistema el resultado que necesita, o los datos necesitarán verificaciones establecidas.
3) ¿Cuáles son los resultados de la falla en este punto del código? ¿Causará que todo falle? ¿Se detectará algún error en otro lugar y ese error se detectará al menos?
4) ¿Tiene sentido poner un cheque aquí? A veces, verificar un dato posible de datos corruptos, aunque ayudar a resolver el problema en ese punto y no un error puede ayudar a ocultarlo. En ese momento, podría pasar horas persiguiendo un problema diferente solo para descubrir que el problema real se debió a una verificación válida de los datos que se encuentran en la cadena de eventos en cascada a la que se informó el usuario / desarrollador.
En general, soy un programador defensivo, sin embargo, también creo que con una prueba exhaustiva de TDD y de unidades apropiadas, puede poner los controles en el código en los niveles requeridos y tener la seguridad de que está funcionando como debería una vez que llegue a las secciones de nivel inferior .
fuente
He pasado semanas antes en errores que podrían haberse detectado con 5 minutos de trabajo adicional como este por adelantado. En mi opinión, este trabajo inicial siempre vale la pena. Estarás arreglando el error eventualmente. La única pregunta es cuánto tiempo te llevará.
Una cosa que este tipo de herramientas de análisis a menudo descubre son cosas que no son necesariamente errores, pero son malos hábitos de programación que hacen que los errores sean más probables. Uno de estos hábitos comunes es la inicialización variable. A veces, una cadena vacía es un valor perfectamente válido para una variable. En ese caso, desea configurar su herramienta para que no se considere un error en esa instancia en particular. Sin embargo, a menudo una cadena vacía no es un valor válido para una variable, pero la gente la está configurando de todos modos, porque el compilador se queja si no hay algo allí, o su idioma se inicializa automáticamente en la cadena vacía, ya sea válida o no.
Esto frustra a las personas porque parece una trampa 22 sin una solución válida, pero la solución es refactorizar su código para que no sea necesario inicializar la variable hasta que haya un valor válido para poner allí. Eso requiere un poco de reflexión adicional por adelantado, pero hace que su código sea mucho más robusto, y en realidad es más fácil de escribir y mantener a largo plazo.
fuente