
Desde principios de 2018, ocupo el cargo de líder/jefe/desarrollador principal en el equipo: llámalo como quieras, pero la esencia es que soy completamente responsable de uno de los módulos y de todos los desarrolladores que trabajan en él. Esta posición me brinda una nueva perspectiva sobre el proceso de desarrollo, ya que participo en más proyectos y me involucro más en la toma de decisiones. Recientemente, gracias a estas dos circunstancias, me di cuenta de cuán importante es la comprensión en la calidad del código y en la aplicación.
La idea que quiero expresar es que la calidad del código (y del producto final) está estrechamente relacionada con el grado de conciencia que tienen las personas que diseñan y escriben el código sobre lo que están haciendo.
Es posible que estés pensando: 'Gracias, capitán. Por supuesto, sería bueno entender lo que uno escribe. De lo contrario, uno podría contratar a un grupo de monos para que golpeen teclas al azar y así quedarnos tranquilos'. Y tienes toda la razón. Por lo tanto, doy por hecho que comprendes que tener una comprension general de lo que se hace es esencial. Esto podría considerarse como el nivel cero de comprensión, y no lo vamos a desglosar en profundidad. Vamos a detallar lo que realmente es necesario entender y cómo esto impacta las decisiones que tomas cada día. Si hubiera conocido estas cosas desde el principio, me habría ahorrado mucho tiempo desperdiciado y código dudoso.
Aunque a continuación no verás ni una línea de código, pienso que todo lo que se dice aquí tiene una gran relevancia para escribir código de calidad y expresivo.
Primer nivel de comprensión: ¿Por qué no funciona?
Los desarrolladores generalmente alcanzan este nivel en las primeras etapas de su carrera, a veces incluso sin ningún tipo de ayuda de los demás, al menos según mis observaciones. Imagina que recibes un informe de error: alguna función en la aplicación no funciona y necesita ser reparada. ¿Cómo procederás?
El esquema estándar es el siguiente:
- Encontrar el fragmento de código que causa el problema (como se hace esto es un tema aparte, lo trato en mi libro sobre código obsoleto)
- Hacer cambios en ese fragmento
- Asegurarse de que el error esté corregido y que no hayan surgido errores regresivos
Ahora nos centraremos en el segundo punto: realizar cambios en el código. Existen dos enfoques para este proceso. El primero: comprender qué está sucediendo en el código actual, identificar el error y corregirlo. El segundo: avanzar a ciegas; agregar, por ejemplo, +1 en una declaración condicional o un bucle, observar si esta función ahora funciona en el escenario necesario, luego intentar algo más y así hasta el infinito.
El enfoque correcto es el primero. Como explica Steve McConnell en su libro Code Complete (por cierto, lo recomiendo mucho), cada vez que hacemos un cambio en el código, debemos ser capaces de predecir con confianza cómo afectará a la aplicación. Cito de memoria, pero si la corrección de errores no funciona como esperabas, eso debería alarmarte seriamente y cuestionar todo tu plan de acción.
Resumiendo, para realizar una buena corrección de errores que no degrade la calidad del código, es necesario entender tanto la estructura del código como la fuente del problema específico.
Segundo nivel de comprensión: ¿Por qué funciona?
Este nivel se alcanza de manera menos intuitiva que el anterior. Yo, siendo todavía un desarrollador principiante, lo aprendí gracias a mi jefe, y posteriormente expliqué varias veces el tema a los novatos.
Esta vez, imagina que recibiste dos informes de errores a la vez: el primero se refiere al escenario A, el segundo al escenario B. En ambos escenarios algo está fallando. Por lo tanto, primero te ocupas del primer error. Siguiendo los principios que hemos extraído para el primer nivel de comprensión, profundizas en el código relacionado con el problema, descubres por qué hace que la aplicación se comporte de esa manera en el escenario A y haces correcciones razonables que dan exactamente el resultado que esperabas. Todo va excelente.
Luego pasas al escenario B. Repites el escenario en un intento de provocar el error, pero — ¡sorpresa! — ahora todo funciona como debería. Para confirmar tu hipótesis, deshaces los cambios realizados en el proceso de solucionar el error A, y el error B vuelve a aparecer. Tu corrección resolvió ambos problemas. ¡Qué suerte!
No contabas con esto en absoluto. Has ideado una forma de corregir el error en el escenario A y no tienes ni idea de por qué funcionó también para el escenario B. En esta etapa, la tentación de concluir que ambas tareas se han completado con éxito es muy grande. Tiene sentido, ¿no? La idea era eliminar errores, ¿verdad? Pero el trabajo aún no ha terminado: todavía necesitas averiguar por qué tus acciones corrigieron el error en el escenario B. ¿Por qué? Porque podría estar funcionando sobre principios incorrectos, y entonces tendrás que buscar otra solución. Aquí tienes un par de ejemplos de tales casos:
- dado que la solución no se adaptó específicamente al error B considerando todos los factores, es posible que, sin darse cuenta, hayas roto la función C.
- no se puede descartar que haya escondido un tercer bug relacionado con la misma función, y tu arreglo de bugs vincula el funcionamiento correcto del sistema en el escenario B a él. Ahora todo parece bien, pero un buen día se notará y se corregirá este tercer bug. Entonces, el error volverá a aparecer en el escenario B, y sería bueno si solo ahí.
Todo esto introduce un caos en el código y eventualmente caerá sobre ti —de alguna manera, probablemente en el momento menos apropiado. Tendrás que reunir fuerzas para obligarte a dedicar tiempo a comprender por qué todo parece funcionar, pero vale la pena.
Tercer nivel de comprensión: ¿Por qué funciona?
Mi reciente revelación está precisamente relacionada con este nivel, y probablemente este sería el que más ventajas me habría dado si hubiera llegado a esta idea antes.
Para que quede más claro, analicemos el ejemplo: tu módulo necesita ser compatible con la función X. No estás muy familiarizado con la función X, pero te han dicho que para su compatibilidad necesitas usar el framework F. Otros módulos que se integran con X funcionan precisamente con él.
Tu código no ha estado en contacto con el marco F desde el primer día de su existencia, por lo que implementar este marco no será tan sencillo. Esto tendrá serias consecuencias para algunos componentes del módulo. Sin embargo, te sumerges por completo en el desarrollo: pasas semanas escribiendo código, probando, lanzando versiones piloto, recibiendo retroalimentación, corrigiendo errores de regresión, descubriendo complicaciones imprevistas, no cumpliendo con los plazos acordados inicialmente, escribiendo aún más código, probando, obteniendo retroalimentación, corrigiendo errores de regresión, todo esto con el objetivo de implementar el marco F.
Y en algún momento, de repente te das cuenta, o quizás lo escuchas de alguien, que tal vez el marco F no te proporcionará compatibilidad con la función X. Tal vez todo este tiempo y esfuerzo se han destinado a algo completamente diferente.
Algo similar sucedió una vez durante el trabajo en un proyecto del que era responsable. ¿Por qué sucedió esto? Porque entendía muy poco sobre el propósito de la función X y cómo se relacionaba con el marco F. ¿Qué debería haber hecho? Pedirle a la persona que plantea la tarea de desarrollo que explique claramente cómo el plan previsto conduce al resultado deseado, en lugar de simplemente repetir lo que se hizo para otros módulos o creer que eso es lo que se necesita para que funcione la función X.
La experiencia de este proyecto me enseñó a negarme a iniciar el proceso de desarrollo a menos que tengamos una clara comprensión de por qué se nos piden ciertas acciones. Rechazar de manera directa. Cuando recibes una tarea, el primer impulso es abordarla de inmediato para no perder tiempo. Pero la política de "congelar el proyecto hasta que entendamos todos los detalles" puede reducir el tiempo desperdiciado en órdenes completas.
Incluso si intentan presionarte, forzarte a comenzar a trabajar, aunque no entiendas la justificación — resiste. Primero aclara el propósito de la tarea que te han asignado y decide si es el camino correcto hacia el objetivo. Tuve que aprender todo esto por la experiencia amarga — espero que mi ejemplo facilite la vida a quienes lo leen.
Cuarto nivel de comprensión: ???
En programación siempre hay algo nuevo que aprender, y creo que apenas he rozado la superficie de la comprensión del tema. ¿Qué otros niveles de entendimiento has descubierto a lo largo de los años trabajando con código? ¿Qué decisiones tomaste que mejoraron la calidad del código y la aplicación? ¿Qué elecciones resultaron ser erróneas y te enseñaron valiosas lecciones? Comparte tus experiencias en los comentarios.
Fuente: habr.com
