¿Cómo reducir la cantidad de errores al codificar?

30

Nadie es perfecto, y no importa lo que hagamos, vamos a producir código que tiene errores de vez en cuando. ¿Cuáles son algunos métodos / técnicas para reducir la cantidad de errores que produce, tanto al escribir un nuevo software como al cambiar / mantener el código existente?

GSto
fuente
Un buen método es hacer un diseño más avanzado (no demasiado, pero lo suficiente como para hacer que su código sea más estructurado y más fácil de entender).
Giorgio

Respuestas:

58

Evita la codificación elegante. Cuanto más complicado sea el código, más probable es que haya errores. Por lo general, en los sistemas modernos, el código claramente escrito será lo suficientemente rápido y pequeño.

Use las bibliotecas disponibles. La forma más fácil de no tener errores al escribir una rutina de utilidad es no escribirla.

Aprenda algunas técnicas formales para las cosas más complicadas. Si hay condiciones complicadas, asegúrelas con lápiz y papel. Idealmente, conozca algunas técnicas de prueba. Si puedo probar que el código es correcto, casi siempre es bueno, excepto por errores grandes, tontos y obvios que son fáciles de solucionar. Obviamente, esto solo llega hasta cierto punto, pero a veces puedes razonar formalmente sobre cosas pequeñas pero complicadas.

Para el código existente, aprenda cómo refactorizar: cómo hacer pequeños cambios en el código, a menudo utilizando una herramienta automatizada, que hacen que el código sea más legible sin cambiar el comportamiento.

No hagas nada demasiado rápido. Tomarse un poco de tiempo por adelantado para hacer las cosas bien, verificar lo que ha hecho y pensar en lo que está haciendo puede dar buenos resultados más adelante.

Una vez que haya escrito el código, use lo que tiene para hacerlo bueno. Las pruebas unitarias son geniales. A menudo puede escribir pruebas con anticipación, lo que puede ser una excelente respuesta (si se realiza de manera consistente, este es un desarrollo basado en pruebas). Compile con opciones de advertencia y preste atención a las advertencias.

Haz que alguien más mire el código. Las revisiones formales de códigos son buenas, pero pueden no estar en un momento conveniente. Las solicitudes de extracción o similares si su scm no las admite permiten revisiones asincrónicas. La verificación de amigos puede ser una revisión menos formal. La programación de pares asegura que dos pares de ojos miran todo.

David Thornley
fuente
x2: lo que dijo Ryan.
JBRWilkinson
2
Además, la mayoría de los idiomas pueden ser más o menos exigentes. Desea que sea lo más exigente posible.
1
"Aprende algunas técnicas formales para las cosas más complicadas" ... ¿por ejemplo?
Dan Rosenstark
1
@Yar: Espero algo parecido a los sistemas explicados en este libro: amazon.com/Verification-Sequential-Concurrent-Programs-Computer/… ; aunque tengo que decir que un libro específico es extremadamente seco y aburrido, por lo que probablemente haya muchos mejores por ahí (pero es el único que he leído).
Joeri Sebrechts
30

Las pruebas unitarias le permiten reducir la cantidad de errores que aparecen por segunda vez. Si encuentra un error en su código, al escribir una prueba unitaria se asegurará de que no vuelva a aparecer más tarde. (Además, pensar en todos los casos y escribir miles de pruebas unitarias por adelantado es difícil de hacer a veces)

Ryan Hayes
fuente
3
"Pensar en todos los casos por adelantado" conduce a interfaces limpias y completamente especificadas, lo que solo puede ser algo bueno. Las pruebas unitarias solo son difíciles de escribir si las está adaptando a un código que no fue diseñado teniendo en cuenta las pruebas.
Mike Seymour
1
Si puedes, deberías. Desafortunadamente, la mayoría de los lugares donde he visto las pruebas unitarias son algo que cuesta más que simplemente "solucionar errores realmente rápido". Por lo tanto, si puede, debe escribir pruebas por adelantado, pero si no se considera "rentable", escribirlas junto con las correcciones de errores lo ayudará a aumentarlo con el tiempo sin tener que gastar demasiado dinero en escribir pruebas unitarias. .
Ryan Hayes
44
"Las pruebas muestran la presencia, no la ausencia de errores". - E. Dijkstra. Dicho esto, las pruebas automatizadas son definitivamente una forma muy útil de mantener y aumentar la calidad del software.
2010
9

+1 en los dos comentarios de prueba de unidad.

Más allá de eso, establezca el nivel de advertencia más alto que ofrece su compilador y asegúrese de que las advertencias se traten como errores. Los errores a menudo se esconden en esos errores "erróneos".

Del mismo modo, invierta en herramientas de análisis estático que se ejecutan en tiempo de compilación (las veo como un nivel adicional de advertencias del compilador).

Alan
fuente
+1 para el comentario de análisis estático. Es invaluable obtener toda esa información gratis :)
Morten Jensen
9

Además de lo mencionado:

  • No ignore los códigos de error, por ejemplo, no asuma que obtuvo un resultado válido, que un archivo se ha creado con éxito, etc. Porque algún día sucederá algo.
  • No asuma que su código nunca entrará en alguna condición y que, por lo tanto, "es seguro ignorar esa condición".
  • Pruebe su código, luego haga que otra persona lo pruebe. Me parece que soy la peor persona para probar mi propio código.
  • Tómese un descanso, luego vuelva a leer su código y vea si "perdió lo obvio". A menudo me pasa a mí.

Muchas otras cosas que estoy olvidando en este momento, pero las demás seguramente pensarán en ellas. :)

MetalMikester
fuente
77
Y si está tan seguro de que la condición X nunca sucederá ... use una afirmación para asegurarse de que cuando ocurra la condición X, lo sepa (a través de una excepción, un registro o lo que sea).
Frank Shearar
@MetalMikester: Las pruebas unitarias son buenas. Pero con lenguajes de alto nivel y buenas bibliotecas, la mayoría de los errores difíciles requieren pruebas de integración y regresión.
Vector
9

He desarrollado un estilo de programación bastante funcional, a pesar de que mis lenguajes principales son C ++ y Python. Descubrí que si paso todo el contexto a una función (o método) que esa función necesita para hacer su trabajo, y devuelvo los datos significativos que estoy buscando, mi código se ha vuelto mucho más robusto.

El estado implícito es el enemigo y, en mi experiencia, es la fuente número 1 de errores. Este estado puede ser variables globales o variables miembro, pero si los resultados dependen de algo que no se pasa a la función, está solicitando problemas. Claramente, no es factible eliminar el estado, pero minimizarlo tiene enormes efectos positivos en la confiabilidad del programa.

También me gusta decirles a mis compañeros de trabajo que cada rama (si, por, mientras,? :) es un error probable. No puedo decir cuál será la manifestación del error, pero cuanto menos comportamiento condicional tenga su código, más probable es que esté libre de errores simplemente debido al hecho de que la cobertura del código durante la ejecución será más consistente.

Vaya, todas estas cosas también tienen efectos positivos en el rendimiento también. ¡Ganar!

dash-tom-bang
fuente
En mi experiencia, puede volverse tedioso pasar rápidamente todo el estado para cada llamada, si los métodos son tan pequeños como deberían ser. Este problema se puede resolver mediante el uso de muchas clases pequeñas inmutables con tiempos de vida de objeto cortos. De esa manera, puede almacenar el estado temporal como campos y descartar el objeto cuando ya no necesite el estado. :-)
Jørgen Fogh
Otra consideración para el caso de que se vuelva tedioso es que tal vez estás tratando de pasar demasiado estado. :)
dash-tom-bang
En muchos casos eso es cierto, pero a menudo no lo es. Dentro de algunos dominios necesita acceso a mucho estado, incluso si no tiene mucho estado mutable . Actualmente estoy trabajando en un generador de código donde necesito acceso a varias tablas de símbolos. No quiero pasarlos a todos y cada uno de los métodos.
Jørgen Fogh
8
  • Escribe menos código que haga más.
  • Piense en las implicaciones de bajo nivel y las ramificaciones de alto nivel.
  • Contempla la abstracción que estás creando en tu código.
  • Escriba solo la complejidad esencial si es posible.
Paul Nathan
fuente
Iba a escribir una gran publicación que se resume como "escribe menos que hace más" (IOW conoce y utiliza las herramientas disponibles para ti). Solo haré +1 en su lugar.
Kaz Dragon
1
Pero tenga cuidado de no obtener un código elegante cuando intente lograr menos código.
gablin
1
@gablin: la fantasía está en el ojo del espectador en muchos aspectos. Hoy puedo escribir y leer código que me habría quedado estupefacto hace 4 años. Haskell hoy es elegante para mí. :)
Paul Nathan
8

Una respuesta un poco menos técnica: no programe cuando esté cansado (9h / día es suficiente), borracho o 'horneado'. Cuando estoy cansado no tengo la paciencia necesaria para escribir código limpio.

Alexandru
fuente
2
Por lo general, a la mayoría de los programadores les lleva varios años darse cuenta de esto. Es un punto importante.
Jeff Davis
7

Escribir pruebas unitarias y pruebas de integración .

ysolik
fuente
5

Algunas excelentes respuestas aquí con respecto a las pruebas unitarias y herramientas. Lo único que puedo agregarles es esto:

Involucre a sus evaluadores lo antes posible

Si tiene un equipo de prueba, no caiga en la trampa de tratarlos como los guardianes de la calidad de su código y de detectar sus defectos por usted. En cambio, trabaje con ellos e involúcrelos lo antes posible (en proyectos ágiles, esto será desde el comienzo del proyecto, pero siempre podemos encontrar formas de involucrarlos antes si realmente lo intentamos).

  • Descubra cuál es su plan de prueba. Revise sus casos de prueba con ellos: ¿los está cubriendo todos con su código?
  • Pídales que comprendan los requisitos. ¿Es lo mismo que el tuyo?
  • Dales versiones de trabajo tempranas para realizar pruebas exploratorias: te sorprenderán las mejoras que sugieren.

Tener una buena relación de trabajo con sus evaluadores significa que puede detectar suposiciones y defectos deficientes desde el principio, antes de que puedan causar algún daño. También significa que los evaluadores se sienten capacitados para ayudar con el diseño del producto y detectar problemas de usabilidad cuando hay tiempo para solucionarlos.

Paddyslacker
fuente
4

Herramientas de análisis estático

Los complementos y aplicaciones como FindBugs rastrean su código y encuentran lugares donde hay errores potenciales . Los lugares donde las variables no se inicializan y usan, o simplemente son locuras que 9 de cada 10 veces, hacen que sea más fácil que surjan errores. Herramientas como esta me ayudan a evitar que mi cabeza de hueso se mueva por el camino, incluso si aún no es un error.

PD: Recuerde siempre investigar por qué una herramienta le dice que algo es malo. Nunca está de más aprender (y no todo está bien en todas las situaciones).

Ryan Hayes
fuente
3

Inspección de código u otras formas de revisión por pares, como la programación de pares.

Las revisiones de código estructurado, como la inspección de Fagan, pueden ser al menos tan efectivas y eficientes como las pruebas unitarias e incluso han demostrado ser mejores que las pruebas unitarias en algunos casos. Las inspecciones también se pueden usar antes en el ciclo de vida del software y con artefactos distintos al código.

Peer Reviews in Software de Karl Wiegers es un gran libro sobre este tema.

Miguel
fuente
2

Además de todas las otras sugerencias aquí, active todas las advertencias posibles al más alto nivel de sensibilidad y trátelas como errores. Utilice también cualquier herramienta de linting que tenga el idioma.

Se sorprendería de cuántos errores simples pueden ser detectados por advertencias y cuántas de esas cosas simples se traducen en errores reales en su código.

Greyfade
fuente
2

Muchas buenas respuestas aquí, pero algunas cosas que quería agregar. Asegúrese de comprender realmente el requisito. He visto muchos errores cuando el usuario pensó que el requisito significaba X y el programador pensó que significaba Y. Presione para aclarar los requisitos pobres o ambiguos. Sé que a todos nos gusta saltar y codificar, pero cuanto más tiempo pase por adelantado para garantizar la comprensión, menos reparaciones y corrección de errores habrá.

Conozca el negocio que está apoyando, a menudo verá cosas en los requisitos que faltan o que necesitan más explicaciones. Sepa que si realiza la tarea Y como se indicó, se romperá la función Z existente.

Comprenda la estructura de su base de datos. Muchos errores son el resultado de una consulta que es sintácticamente correcta, pero devuelve los resultados incorrectos. Aprende a reconocer cuándo tus resultados se ven divertidos. Si escribo una consulta de informes compleja, siempre obtengo un especialista técnico para revisar mis resultados antes de marcarlo como listo, inevitablemente verán algo en los datos que me perdí. Luego tome nota de lo que atraparon y no lo recuerde la próxima vez que haga algo similar.

HLGEM
fuente
1

Creo que la técnica más importante es tomarse su tiempo . Si siente que necesita dos días para codificar un nuevo módulo, pero su jefe lo obliga a codificar solo en un día ... es probable que su código tenga más errores.

Uno de los libros que leí hace un tiempo decía que no deberías vivir con ventanas rotas , porque a la gente no le importará si alguien se rompe ... La codificación es la misma, a todos les importará ser los primeros en hacer algo malo pero rápido , pero a ninguno le importará un código infernal , con muchos errores y un diseño y estilo muy pobres.

greuze
fuente
1

Sigo la práctica de Test-Code-Test en lugar de Code-test-code-test. Esto me ayuda a pensar en casos de uso y enmarcar adecuadamente la lógica.

viv
fuente
1

Utilice herramientas de inspección de código como ReSharper o IDEs como IntelliJ IDEA que advierten sobre muchos errores de copiar y pegar y otros, por ejemplo, señalando variables que "se escriben, pero nunca se leen". Me ha ahorrado mucho tiempo.

Don Joe
fuente
1

Sorprendentemente, los siguientes tres puntos muy importantes aún no se han mencionado:

  • Use afirmaciones generosamente. La pregunta que siempre debe hacerse no es "¿debería afirmar esto?" pero "¿hay algo que olvidé afirmar?"

  • Opta por la inmutabilidad. (Use final / readonly liberalmente). Cuanto menos mutable tenga, menos cosas pueden salir mal.

  • No optimice prematuramente. Muchos programadores son ignorados por problemas de rendimiento, lo que les hace complicar innecesariamente su código y bastardar sus diseños sin siquiera saber de antemano si el rendimiento será un problema. Primero, cree su producto de software de forma académica, sin tener en cuenta el rendimiento; entonces, vea si funciona mal; (Probablemente no lo hará). Si hay algún problema de rendimiento, busque uno o dos lugares donde pueda proporcionar optimizaciones algorítmicas agradables y formales que harán que su producto cumpla con sus requisitos de rendimiento en lugar de modificar y piratear toda la base de código para apretar ciclos de reloj aquí y allá.

Mike Nakis
fuente