elprogramadormediocre/chapter03.md
2022-09-23 17:43:47 +02:00

18 KiB

Los errores a lo largo del camino

¡Vaya!

Tiene que pasar: algo que pensabas que era una buena idea no funcionó de la manera que lo habías planeado, y ahora te das cuenta de que has cometido un terrible error. A veces es algo que se podría haber evitado fácilmente (por ejemplo: realizar un commit de un código destinado a la depuración). A veces es una cascada de errores, cada uno de los cuales se basa en las consecuencias del error anterior. Está el error de ignorar los efectos secundarios de un módulo cuando se usa de una manera que no estaba prevista, o darse cuenta de que has diseñado un módulo pequeño y estrechamente acoplado y después descubrir que tu módulo será parte de una pieza de software más grande y tu código no está diseñado para hacer una transición sin problemas. ¡Vaya!

Sin embargo, los errores que realmente me asustan son los que no esperaba, aquellos en los que las consecuencias no deseadas corren desenfrenadas por todo el sistema como una reacción en cadena. Esos errores son los que me quitan el sueño por la noche.

Los programadores cometen errores. La naturaleza de nuestros trabajos requiere que seamos conscientes de lo que sucede en múltiples secciones de código. Perdemos la noción del estado de nuestro programa y del código que ya hemos realizado. Intentamos salpicar nuestro código con comentarios y recordatorios de lo que sucede en una sección del código, pero los comentarios se vuelven obsoletos y aumentan nuestra distracción. Nos apresuramos y confiamos que el músculo de nuestra memoria tome el relevo. Nos negamos áreas en las que podemos probar el código adecuadamente porque sentimos que debemos apresurarnos para hacer las cosas rápidamente.

Entramos en pánico, y cuando entramos en pánico cometemos errores.

Evitar los errores

Vamos a hablar con claridad: no hay manera de evitar o eliminar los errores. El software es demasiado complejo para estar completamente libre de errores. Sin embargo, lo que podemos hacer es crear lugares en los que podamos detectar tantos errores del código como sea posible antes de mostrarlo a los demás. Podemos comprender mejor nuestro código y lo que está haciendo cuando tenemos la capacidad de depurar y probar nuestro código en un entorno seguro. Podemos ver cómo se comportará bajo ciertas circunstancias. La creación de un modelo similar al del sistema de destino nos permite probar nuestro código con versiones más pequeñas de la realidad del sistema del destino y ver cómo se comporta en esas condiciones.

Ponemos mucho énfasis en evitar errores, tanto en la programación como en la cultura de la programación. Hay historias de terror de cómo pequeños errores en un programa causaron grandes dolores de cabeza a las personas involucradas. La moraleja de estas historias es que los errores simples pueden ser costosos y debemos ser doblemente cuidadosos para evitar errores. Estas anécdotas se cuentan con la esperanza de asustar a los desarrolladores para que sean más cautelosos, pero pueden tener el efecto contrario. Pueden hacer que los programadores se vuelvan paranoicos acerca de poder cometer cualquier error, y cuando operamos en un modo basado en el miedo, comenzamos a entrar en pánico. Decirle a los programadores que no cometan errores es similar a decirle a alguien que no tenga miedo: tienen más miedo de tener miedo.

La mejor (y quizás la única) forma en que aprendemos, es cometiendo errores. Aprender cometiendo errores es una forma efectiva de permitirnos ser curiosos y ver qué causó que el programa fallara. Cuando nos privamos de la libertad de cometer errores, nos privamos de las oportunidades de aprendizaje al cometer esos errores. Eso no significa que tengamos que cometer todos los errores que otros desarrolladores han cometido antes que nosotros (eso serían montones de errores). Tampoco significa que debamos introducir el caos en nuestro proceso de desarrollo para aprender mejor. Lo que significa es que debemos cometer nuestros propios errores a nuestra manera, para seguir aprendiendo y descubrir dónde existen lagunas en nuestra comprensión.

Hacer un modelo

Necesitamos entornos donde los programadores puedan aprender de una manera segura de sus propios errores. Necesitamos espacios donde los programadores puedan sentirse bien y seguros a la hora de probar cosas nuevas. Necesitamos lugares donde los desarrolladores puedan probar sus ideas y que esos cambios no se extiendan a otros sistemas no relacionados. Esta es la mejor forma, con la que los desarrolladores pueden aprender y ser valientes en su proceso de aprendizaje.

Estos entornos deben replicar los sistemas de destino y deben estar lo más cerca posible de esos sistemas de destino. Eso no significa que haya que hacer copias exactas de entornos de producción costosos, pero sí se necesita crear modelos de entornos de producción que prueben la mayoría de las piezas con las que tu código entrará en contacto. Tener modelos o réplicas que reflejen los sistemas de producción significa que cuando muevas tu código a producción, introducirá menos cambios con consecuencias no deseadas. Tus cambios ya habrán existido en un entorno de producción. Puedes estar tranquilo sabiendo que los cambios que provoquen en estos modelos serán los mismos cambios que aparecerán en el sistema de destino.

En la situación más ideal, necesitarás tener un entorno como este, en una máquina que controles. Esto significa que no estás compitiendo con otros programadores de tu organización que también están siendo valientes con sus cambios. También querrás asegurarte de que tu entorno se mantenga actualizado con sus cambios (y cualquier cambio de producción) para que tu modelo de desarrollo coincida con lo que está en el sistema de destino y lo que estará en el sistema de destino. Un buen modelo es aquel que se mantiene actualizado con lo que se está modelando. Es lo mismo que un mapa de una ciudad: es mejor cuando coincide con el área de su modelo y se mantiene actualizado con los cambios que ocurren en esa ciudad. Un buen mapa de la ciudad podría informarte sobre las obras recientes que se están realizando en tu ruta. Un mapa inútil es aquel que ni siquiera muestra tu ruta porque no se había construido esa ruta cuando se creó el mapa. Si nuestro modelo de producción se está quedando atrás constantemente con respecto a lo que está en producción, dedicaremos más tiempo a rectificar los cambios que estamos haciendo con los cambios entre nuestro modelo y el que está en producción.

Esto también significa que deberíamos tener un entorno que se pueda reconstruir rápidamente y replicar según sea necesario. Tener un modelo que se convierte en su propia realidad separada se convierte en un sistema más para mantener. Este modelo debe ser algo que se pueda eliminar y reconstruir a voluntad para eliminar cualquier experimento anterior. Es mejor pensar en él como una copia efímera de tu entorno de destino que tiene un uso limitado y puede desecharse cuando ya no sea necesario. Este entorno de pruebas debe ser rápido a la hora de volverlo a replicar para que haya poca fricción a la hora de crear nuevos entornos para probar. Eso quizás puede significar crear secuencias de comandos para el proceso de construcción de estos entornos. La forma en que decidas hacer esto depende de ti, pero ten en cuenta que quieres algo que sea lo más simple posible y que requiera la menor cantidad posible de nuestra atención para replicarlo.

De nuevo, no tiene que ser perfecto, sólo es un modelo, pero necesita ser lo bastante similar al original para que tu código se comporte de una manera similar entre el modelo creado y el entorno real.

Máquinas del tiempo

Hay un buen número de personas que te hablarán sobre los beneficios de un sistema de control de revisiones (y muchas de esas personas te mostrarán los pasos exactos para configurar un sistema de control de revisiones). Los sistemas de control de revisiones como git, svn, o cvs y similares han ayudado a los programadores a coordinar lanzamientos de publicaciones y mantener un registro de qué trabajos se han añadido a sus proyectos. Tener un buen sistema de control de revisiones te permite crear áreas donde puedas probar código nuevo sin tener que añadir estas pruebas a código ya en producción. Un buen control de revisiones te permite crear un espacio (o también llamados ramas o branch por su nombre en inglés cuando estamos hablando de git) basado en un código ya existente que puedes utilizar para experimentar y desarrollar. También te permite realizar commits en ese espacio y divagar tanto como necesites para poder explorar a fondo los cambios que estás realizando. Sin embargo, lo que es más importante es que un buen sistema de control de revisiones también te permitirá abandonar ese espacio si lo necesitas, no estás forzado a añadir esos cambios a tu código en producción. Esto te permite ver si algo podría funcionar o abandonar esos cambios si no lo hace. Un buen control de revisiones brinda a los programadores la capacidad de ramificarse desde cualquier punto en el tiempo y explorar lo que sucedió en el código base. En cierto sentido, son máquinas del tiempo y universos infinitos, lo que te permite jugar con distintos escenarios "¿y si?" con tu código y avanzar y retroceder en el tiempo en el código. Esto es vital para tu aprendizaje porque puedes sentirte seguro probando y probando cosas y rebobinando esos cambios (o eliminándolos por completo) sin afectar el trabajo de otras personas.

Aprender cómo funciona tu sistema de control de revisiones te dará libertad para cometer errores. Muchos de estos sistemas pueden parecer complejos al principio, pero con la práctica continua y paciencia, comprenderás lo que hace el sistema de control de revisiones y cuáles son sus posibilidades. Podrás juzgar cuántos riesgos puedes tomar con tu código y tener más confianza con los riesgos que tomas.

El control de revisiones también puede desempeñar un papel importante al poder ver el desarrollo del código de otras personas. Puedes disponer de una ventana a su proceso de desarrollo y ver cómo se desarrollan ciertas características a medida que se añaden. Esto puede ayudarte a aprender sobre una base de código desconocida y mostrarte la dirección que tomaron para hacer el código de la manera que es. Puede brindarte una ventana a la historia de un proyecto y lo que se hizo para que sucediera. El control de revisiones puede ser una máquina del tiempo en la historia de un proyecto y puede ayudarte a comprender que la programación es un proceso. No todos los proyectos vienen completamente ya formados de la mente de los programadores.

Aprender de los errores

A veces fallamos. A veces, el código que escribimos no está a la altura de las realidades del sistema en el que se implementa. Enviamos código que hace algo inesperado y como resultado, los sistemas se rompen. Podemos perder la noción de dónde estamos en nuestro código y hacer cambios que entren en conflicto con otros cambios, lo que luego hace que dediquemos tiempo a deshacer esos cambios. Todos estos casos causan malestar, ya sea para nosotros, las personas a las que ayudamos o las personas con las que trabajamos.

No voy a mentir: el fracaso apesta. Nos hace sentir que somos menos personas porque fallamos. Nos sentimos incómodos y nos preguntamos qué pensarán los demás de nosotros. ¿Tendrán los demás una mala opinión sobre nosotros? ¿Hemos dañado nuestra relación con aquellas personas que usan lo que sea que hayamos programado? ¿Hemos defraudado a nuestro equipo? Todas estas preguntas surgen de dos deseos: el deseo de hacer lo mejor posible y el deseo de no hacer daño a los demás. Queremos que los demás piensen bien de nosotros y de nuestras habilidades. El fracaso va en contra de esos deseos y amplifica cualquier sentimiento de insuficiencia que podamos tener. Esos sentimientos pueden incluir preguntarse si deberíamos programar o si nuestros talentos deberían usarse en otras áreas. Nos preguntamos si deberíamos rendirnos.

No solemos pensar en el fracaso como parte del proceso de aprendizaje. El fracaso a menudo se ve como el punto final del viaje. En la escuela, una nota baja se considera una castigo. No lo vemos como: "necesito practicar esto un poco más", en cambio, sentimos que nos hemos causado vergüenza e incomodidad a nosotros mismos y a nuestros seres queridos. Nos perjudicamos gravemente a nosotros mismos, si no nos damos cuenta de que el fracaso es una parte natural del proceso de aprendizaje y que está bien fracasar. No todo lo que hagamos será perfecto. Los errores se infiltrarán en el mejor código que escribamos. Nos equivocaremos y desplegaremos el código en el sistema equivocado. Nuestros errores causarán malestar a los demás. Aceptar esto nos da la libertad de darnos cuenta que, a pesar de nuestros mejores esfuerzos, no seremos perfectos. En lugar de ver el fracaso como una limitación, podemos usarlo como parte de nuestro proceso de crecimiento.

Cuando nos damos cuenta de que vamos a cometer errores, podemos cambiar nuestro enfoque sobre cómo y dónde los cometemos. Antes mencioné sobre la creación de modelos de nuestros entornos. ¿Qué mejor manera de permitirnos cometer errores que en un entorno donde esos errores se pueden contener o revertir? La creación de modelos nos permite practicar y probar nuestras suposiciones en entornos que nadie más tiene que ver. Es similar a un lugar de ensayo para músicos, donde pueden repasar su material sin necesidad de tocarlo bien a la primera. Pueden resolver las partes problemáticas y cometer errores hasta que tengan confianza en su ejecución.

Los errores sirven para aprender qué funciona y qué no funciona. Son una parte integral de nuestro proceso de aprendizaje. Tenemos tendencia a recordar las lecciones de aquello que no funcionó bien, mejor que aquellas que sí funcionaron. Los errores nos ayudan a reforzar aquellas áreas donde nos faltan conocimientos y nos ayudan a comprender las brechas que aún tenemos que cerrar.

Los errores también actúan como un recordatorio para hacer una pausa por un momento y no dejarse llevar por la urgencia de las cosas. Mis propios errores tienden a surgir cuando me apresuro a cumplir con una fecha límite (ya sea real o auto impuesta). Mis peores errores suceden cuando estoy cansado y apurado, cuando prácticamente estoy golpeando el teclado tratando de hacer que algo (¡cualquier cosa!) funcione. Cuando me permito hacer una pausa por un momento, reflexionar sobre lo que estoy tratando de hacer y sentir la incertidumbre en el momento, puedo tomar medidas para recalibrar y reenfocarme en el momento. Me doy la libertad de corregir el rumbo y comprender que no estoy dando lo mejor de mí y necesito hacer algo diferente. Puede ser algo pequeño como darle un poco de descanso a mi cerebro o algo grande como revisar las suposiciones que hice sobre lo que estoy haciendo. Hacer la pausa me permite determinar si quiero continuar haciendo lo que estoy haciendo y entender si ese es el mejor camino.

Llevar un registro de nuestros errores

Es interesante no cometer los mismos errores dos veces, pero incluso si repetimos el mismo error aún puede ser útil. Saber que hemos repetido el mismo fallo es útil porque nos da un patrón que podemos entender. Esos patrones nos muestran que hacer esto en particular conduce a un resultado erróneo que se vuelve a repetir. Luego podemos determinar qué causó el error y planificar cómo mitigarlo. Esto es parte del proceso de aprendizaje, siempre y cuando no caigamos en una espiral de auto-recriminación cuando nos demos cuenta de que hemos vuelto a cometer el mismo error.

Un truco que yo mismo debería usar con más frecuencia es escribir un registro en un diario. Llevar un diario de lo que ocurrió y cómo lo solucionamos es una forma de explicarle a otra persona (a menudo a nosotros mismos) lo que sucedió. Explicar lo sucedido nos permite convertirnos en maestros para nosotros mismos y para los demás. Refuerza nuestro proceso de aprendizaje. Escribir lo que sucedió de una manera que otros puedan entender, nos permite organizar los pensamientos en nuestra cabeza de una manera clara y comprensible. Cuando articulamos nuestros propios pensamientos sobre lo que sucedió y los transcribimos, comenzamos a comprender nuestros propios pensamientos y podemos liberarnos de otras ideas sobre cómo solucionar este y otros problemas. Nos damos la pausa que necesitamos para comprender completamente lo que sucedió y cuál es la mejor manera de avanzar. Nos convertimos en nuestra propia caja de resonancia de ideas sobre la mejor manera de proceder.

No se trata de mantener un registro para la posteridad para que podamos mirar hacia atrás en una lista de fallos y castigarnos por el pasado (si eres como yo, eso sucede automáticamente). Es una manera de enseñarnos a nosotros mismos y maximizar el proceso de aprendizaje. Se trata de darnos la libertad de ser el instructor de nuestro yo futuro para que podamos ser más conscientes cuando un error está a punto de ocurrir y comprender cómo corregirlo. Esto nos permite concentrarnos ahora mismo el tiempo suficiente para comprender qué sucedió, qué hicimos para corregirlo y cuál es la mejor manera de proceder a partir de aquí. También nos ayuda a ubicar dónde están nuestras lagunas de aprendizaje y las "próximas acciones" que debemos tomar para llenar esas brechas.

Hablaremos más sobre el proceso de llevar un registro en capítulos posteriores pero recomiendo encarecidamente el hábito de llevar un diario, aunque únicamente sea por la razón que le brinda un aprendiz dispuesto a enseñar, incluso si ese aprendiz eres tu mismo.