¿Cómo superar la programación por coincidencia? [cerrado]

24

En el libro El programador pragmático , los escritores mencionan la programación por concepto de coincidencia . Explica qué es, por qué se causa, cuáles son los peligros que puede encontrar y se compara con un campo de minas terrestres en una guerra.

¿Alguna vez has visto viejas películas de guerra en blanco y negro? El soldado cansado avanza con cautela fuera de la maleza. Hay un claro por delante: ¿hay minas terrestres o es seguro cruzar? No hay indicios de que sea un campo minado, sin signos, alambre de púas o cráteres. El soldado empuja el suelo delante de él con su bayoneta y sus muecas, esperando una explosión. No hay uno Así que avanza minuciosamente por el campo por un tiempo, empujando y hurgando mientras avanza. Finalmente, convencido de que el campo está a salvo, se endereza y marcha con orgullo hacia adelante, solo para volar en pedazos.

Las sondas iniciales del soldado para las minas no revelaron nada, pero esto fue simplemente afortunado. Fue llevado a una falsa conclusión, con resultados desastrosos.

Como desarrolladores, también trabajamos en campos minados. Hay cientos de trampas esperando para atraparnos cada día. Recordando la historia del soldado, debemos tener cuidado de sacar conclusiones falsas. Deberíamos evitar la programación por coincidencia, confiando en la suerte y los éxitos accidentales, a favor de la programación deliberada ...

Pero no estoy realmente satisfecho con la forma en que describen el problema de "cómo superarlo". Sí, tienes que pensar antes de escribir el código, pero ¿cómo practicar eso? Lo único que puedo pensar es al agregar características a los proyectos de código abierto existentes, donde debe tener conocimiento tanto de "lo que estoy haciendo ahora" como de "cómo funcionan los otros códigos", y no es tan aplicable cuando escribes tus propios proyectos.

EDITAR:

un resumen de tus publicaciones:

  • No adivines tu próximo movimiento, prueba que es correcto
  • Prueba unitaria y refactorización tanto como sea posible, cuando sea necesario
  • Agregue funciones, compile y pruebe a menudo
  • Si no puede explicar el código a un novato, probablemente esté programando por coincidencia.

Por cierto, es difícil aceptar una respuesta, es realmente difícil. Todas las respuestas son realmente geniales :)

py_script
fuente
66
Ayudaría a las personas que no hayan leído el libro durante mucho tiempo tener un enlace como pragprog.com/the-pragmatic-programmer/extracts/coincidence .
btilly
A menos que tenga una carrera de programación muy corta (o sea una tienda de un solo hombre), es probable que encuentre un código que le resulte extrañamente familiar y un poco de búsqueda de códigos más tarde, el centavo cae: es suyo. No es solo una consideración de código abierto ...
Robbie Dee
@Robbie Dee. ¿Puedes aclararlo un poco más? No te entiendo. De hecho, tengo una corta carrera en programación y esa es la razón de la etiqueta de programador junior.
py_script
2
@py_script Estaba señalando que puedes encontrar tu propio código años después (y quedarte desconcertado) tan fácilmente como el de otra persona. Entonces, si comienza con buenos hábitos, esto pagará dividendos más adelante.
Robbie Dee

Respuestas:

26

No tiene que pensar con anticipación, simplemente sea muy claro en lo que se hizo y sea muy claro en lo que está haciendo en este momento.

Las subrutinas deben decir lo que hacen, hacer lo que dicen y no tener dependencias ocultas. Entonces, alguien que los llame puede razonar más fácilmente sobre lo que harán.

Evitar el estado global. (Variables, singletons, etc.) Cuanto más tenga que tener en mente para comprender qué hacen las cosas, más difícil será comprender qué se supone que sucederá y encontrar los casos extremos.

Escribir pruebas unitarias. Las pruebas unitarias son excelentes para capturar el comportamiento real del código que acaba de escribir, en lugar del comportamiento ideal que espera encontrar.

Acorte su ciclo de edición / compilación / prueba. Cuando agrega una gran porción de código y prueba mal, entonces las probabilidades son de que se comportará de manera diferente de lo que piensa. Luego lo "arreglas" con algún cambio aleatorio y obtienes la respuesta correcta por el momento, pero no tienes idea de cómo sucedió realmente. Ahora estás programando por coincidencia. Pero cuando agrega 5 líneas y luego prueba, las probabilidades de que obtenga la respuesta correcta porque funciona como cree que funciona son mucho mejores. Puedo decir por experiencia que 5 trozos de 10 líneas cada uno, probados individualmente, son una bestia muy diferente a las 50 líneas de código probadas a la vez.

Refactorizar sin piedad. Muchas veces he visto un refactorizador que hará que mi código sea algo más simple, pero requerirá mucho trabajo que no quería hacer. Después de que comencé a abordar deliberadamente esos refactores como una prioridad, descubrí que generalmente se amortiza en un mes. Pero tenga en cuenta la clave, los refactores en los que me concentro son los que simplifican la vida cotidiana, y no los que cumplen con una estética arbitraria de mejor o más general. Esos refactores con los que he aprendido a ser mucho más cauteloso.

Ninguna de estas cosas requiere una planificación previa. Pero todos hacen que sea más fácil entender su código existente y, por lo tanto, facilitan la implementación de su próximo fragmento de una manera deliberada.

btilly
fuente
Gracias, muy buena respuesta. Creo que la parte del ciclo es realmente valiosa. ¿Puede darme ejemplos de refactorización que debería hacer, pero que llevará mucho tiempo implementar y eso puede desanimar a alguien?
py_script
1
Tengo muchos ejemplos En un proyecto de C ++ creé clases sin métodos de stringificación. Una vez que los creé, la depuración se hizo más fácil y el desarrollo se aceleró. En un proyecto de Perl teníamos un hash de configuración donde cada desarrollador tenía su propia copia ajustada de cada nueva configuración. Agregar parámetros de configuración fue una molestia porque tenía que editar la configuración de cada desarrollador. Escribí un sistema de plantillas para hashes, y eso se volvió más fácil. En un sistema de informes, agregué una función para mostrar resultados intermedios. Mi desarrollo se aceleró e incluso recibí un informe de error del usuario ...
hasta el
1
donde el departamento de finanzas había rastreado mi lógica y encontrado la consulta exacta en la que me equivoqué y cuál era mi error. (Accidentalmente estaba aplastando filas duplicadas con lo UNIONque necesitaba UNION ALL.) Y así sucesivamente.
btilly
1
Dentro de un mes? Por lo general, encuentro que cada vez que toco el código, creo que debería ser refactorizado, pero no refactorizado, lleva casi tanto tiempo como refactorizarlo.
Amy Blankenship
1
@AmyBlankenship Sí. Dentro de un mes. A veces ridículamente adentro, a veces no. El archivo de configuración que mencioné anteriormente es un ejemplo de "a veces no". Me llevó unos días escribir, documentar y probar un nuevo motor de plantillas. Luego, vuelva a escribir la configuración existente para usarla, y sea mucho más corta, pero produzca la misma estructura de datos exacta. (Esa fue la parte más difícil del proyecto.) Así que ... días desperdiciados, sin ningún resultado visible. Sin embargo, el esfuerzo valió la pena en poco menos de un mes, pero ha estado generando intereses desde entonces.
btilly
41

Se reduce a no adivinar . La mayoría de los programadores nuevos lo hacen, pero también he visto veteranos, porque creen que ahorra tiempo de investigación. Algo no funciona, por lo que agrega a +1o a -1, cambia a truea falseo viceversa, reordena algunas declaraciones, agrega o cambia demoras, cambia las prioridades de hilo y otras pequeñas transformaciones, básicamente probando permutaciones aleatorias hasta que funcione.

Eso se aplica principalmente a cambiar el código existente, pero también es un factor en el código nuevo, porque nadie realmente comienza desde cero. Siempre está construyendo sobre bibliotecas estándar, sistemas operativos o al menos arquitecturas de procesador.

En otras palabras, no habrá terminado hasta que sepa por qué funciona su solución. El camino que tomas para llegar allí no importa tanto. Incluso las permutaciones aleatorias a veces pueden ser útiles para reducir un error que es difícil de diagnosticar, siempre y cuando se tome el tiempo para preguntarse: "Bien, cambiar de verdadero a falso lo solucionó, pero ¿por qué?"

Karl Bielefeldt
fuente
1
Excelente punto +1. En ninguna parte esto aplica más que las correcciones de código ...
Robbie Dee
Cierto para mí también, desafortunadamente. Para ser sincero, a veces hay dificultades objetivas, como la falta de documentación. Hoy estaba arreglando un poco mi código, y me di cuenta de que no sabía para qué era útil un parámetro, porque faltaba documentación. Solo sabemos que es un número.
py_script
Lo admito. Cuando se enfrenta al síndrome del palillo de dientes inclinado, puede ser más fácil apilar los \ s hasta que funcione que averiguar cuántas capas de escape está enfrentando ...
hasta el
16

El comentario más aterrador que encontré en un programa fue

No toques esto. Funciona. No sabemos cómo ni por qué, pero funciona. 1

y da miedo solo porque reconoce que el fragmento de código fue el resultado de la programación por coincidencia .

Para evitar la programación por coincidencia, debe poder explicar (a un compañero de trabajo, usted mismo o un pato de goma ) exactamente qué hace el código y por qué funciona. Las viñetas para programar deliberadamente son en su mayoría ayuda para avanzar hacia ese objetivo de poder explicar el código.


1 Para el contexto, este comentario apareció en el código que maneja los cambios de contexto en un sistema operativo primitivo. El código ya había estado en producción durante varios años cuando lo encontré.

Bart van Ingen Schenau
fuente
2
esto también se llama codificación de pollo vudú c2.com/cgi/wiki?VoodooChickenCoding
menosSeven
1
Incluso si el codificador cree que es cierto, ese tipo de comentario es enormemente inútil. Es posible que el código haya sido complejo, pero si fuera a leerlo de otro modo, podría pensar que es sencillo. Todo lo que hace el comentario es aumentar la paranoia!
Robbie Dee
3
Esto también puede suceder cuando se mantiene una base de código antigua, en un lenguaje con el que la mayoría del equipo de desarrollo actual no se siente muy cómodo.
pcurry
Por lo tanto, el pato de goma no es solo para la depuración. Bien ... Creo que estamos trabajando en la misma compañía, tenemos muchos comentarios como este: P
py_script
Hay situaciones en las que una solución simplemente funciona debido a una falla en una API, y no hay una solución mejor y lógica. La depuración de algunas librerías compiladas de terceros puede ser tan profunda como la depuración del kernel. E incluso si encuentra el problema, después de horas de depuración hay poco que pueda hacer. Entonces enfocas el problema de manera diferente. Adoptas el modelo de "caja negra" que te obliga a programar por coincidencia. Juegas con el comportamiento extraño de la caja negra y si logras que funcione de la manera que deseas, GENIAL, agrega un comentario con "magia no toques" y sigue adelante.
Radu Simionescu
7

Para los nuevos programadores, la parte más importante de superar esto es comprender realmente lo que están haciendo.

En muchas áreas, cuando no sabes cómo hacer algo, simplemente vas por prueba y error. Intenta algo; si funciona, genial, si no, prueba con otra cosa.

En la programación, especialmente cuando se usa un lenguaje que tiene el concepto de comportamiento indefinido (como C o C ++), este enfoque simplemente no funciona, porque el éxito ya no es una decisión booleana. Puede tener cosas que "funcionan", que a veces funcionan, que funcionan para algunas entradas pero no para otras.

En ocasiones he enseñado a nuevos programadores, y la tendencia a intentar escribir cosas aleatorias para ver si funciona es común. Escribirían una línea y luego se volverían hacia mí y me preguntarían: "¿Funcionaría de esta manera?" aunque estaba claro que no tenían absolutamente ninguna idea de si podría hacerlo.

La conclusión es que, como nuevo programador, realmente tienes que poder explicar qué hace tu código. Tienes que aprender a leer el código, no solo escribirlo.

(Por supuesto, eso se aplica también a los programadores experimentados, pero mi experiencia aquí ha sido principalmente con nuevos principiantes completos).

Sebastian Redl
fuente
<< Tiene que aprender a leer el código, no solo escribirlo. >> Entonces, sobre mi pregunta inicial, ¿cree que me ayudará a agregar funciones en proyectos de código abierto?
py_script
2

Es demasiado fácil codificar, probar y corregir "en la línea de carreras". Tiene alguna funcionalidad que, dado X e Y, produce Z. ¿Pero qué pasa si X está dañado e Y no está disponible? ¿Qué pasa si no puede generar Z? Tenga constantemente presente lo que puede salir mal y anótelo para el ciclo de prueba.

Mantenga sus rutinas cortas y descriptivas: el mejor código requiere pocos comentarios (si los hay).

Los nombres significativos de métodos, clases y variables contribuyen en gran medida a la legibilidad.

Si encuentra un olor a código, entonces DETÉNGASE. Es poco probable que ese olor desaparezca y un pequeño esfuerzo ahora podría ahorrarle una gran cantidad de dolor más adelante.

Incluya pruebas dentro de su proceso de desarrollo. Yo recomendaría el uso de BDD en lugar de TDD, ya que te obliga a describir lo que pretendes lograr en lugar de depender ciegamente de una serie de pruebas que podrían darte una falsa sensación de seguridad.

Resista el impulso de agregar características geniales adicionales (a menos que sea su propio proyecto favorito). Cualquier código adicional debe ser diseñado, escrito, probado y mantenido. Si no es requerido por el cliente / negocio, corre el riesgo de que esto se convierta en una gran pérdida de recursos.

Robbie Dee
fuente