He tenido la suerte de ser ponente en la conferencia de Miguel Ángel Durán (@midudev). Esta primera edición ha sido online en Twitch, con picos de audiencia en directo de 9k personas.
Por el momento, la grabación puede verse en Twitch, mi ponencia comienza a las 3 horas y 53 minutos. Si más adelante deja de estar ahí y pasa a YouTube, actualizaremos el enlace de este artículo.
Este artículo es el guión que usé para la ponencia, viene siendo lo que digo con palabras. La parte final del artículo sobre cohesión y acoplamiento no dio tiempo a explicarla, así que es un plus del artículo.
Quiero dar las gracias a midudev y a toda la audiencia, tanto en directo como en diferido, porque para mí es muy importante divulgar las maneras en que hacemos mejor software. Después de tantos años viendo los mismos problemas, una y otra vez, hay ganas de ver cambios en el sector.
Cuando llevaba ya un par de años ganando el pan como programador, sobre todo cuando ya había tenido que mantener código legado de otras personas, e incluso mi propio código legado, empecé a pensar que no sabía programar. Pensé que algo debía estar fundamentalmente mal, en la forma en que programamos, como para que resulte tan duro mantener un proyecto. Estaba cansado de hacer jornadas maratonianas antes de una entrega, a veces, hasta el amanecer. Estaba cansado de tener miedo a que las cosas fallasen al ponerlas en producción.
¿Cómo es posible que arregle una cosa y rompa otras tres?, ¿por qué tardamos tanto en hacer modificaciones?, ¿cómo puede ser que siempre vayamos con prisa?, y la pregunta del millón… ¿Cuándo vamos a tirar abajo todo el proyecto y reescribirlo desde cero?
Como consultor, a lo largo de la última década me he encontrado con muchos equipos que pedían lo mismo. La pregunta que hago a esos equipos es, ¿qué haríais diferente, esta vez, como para no volver a tener que reescribir el proyecto dentro de un año?
Algunas personas salen con su fe en la religión del framework;
“Esta vez usaremos la última versión de .Net core 38.43, con el nuevo EntityFramework, y todo saldrá bien”, o, “esta vez usaremos la última versión de react con hooks y redux y sagas y con mongo, y todo saldrá bien”. Esta creencia superoptimista tecnológica asume que con nuevas librerías y frameworks quedará todo solucionado. Total, si son tecnologías que ha desarrollado Microsoft, Facebook, Netflix (o ponga aquí el nombre de una gran tecnológica), ¿cómo va a salir mal? Obviamente, no es así, porque no han dejado de salir librerías y frameworks en los últimos 20 años; de hecho, salen cada vez más cantidad, más rápido, y seguimos escribiendo y trabajando con código que mata la moral de cualquiera.
Luego hay otra gente que responde con teoría de la arquitectura megalomaniaca, “montaremos un sistema de eventos y colas, usando todos los patrones de diseño del GoF, con microservicios y sobre todo con Kubernetes,… y todo saldrá bien”. Obviamente, tampoco es la solución mágica, no tenemos más que recordar la ingente cantidad de sobreingeniería que sufríamos en la época heavy de J2EE con los EJB, Soap, los servidores de aplicaciones rimbombantes, etc.
Algunos de los peores proyectos en los que he trabajado en mi vida estaban basados en diseños grandilocuentes de una sola persona que además luego no los sufría en sus carnes. Gracias a dios, a mí en esa época no me dejaron hacer ningún “diseño de arquitectura”, simplemente me trataban como un code monkey. Yo la hubiera liado muy parda si hubiera jugado a ser el arquitecto de la torre de marfil.
Entonces, si estas dos vías no son la solución, ni juntas ni separadas, ¿qué podemos hacer diferente la próxima vez?, ¿cómo tenemos que programar?
El primer paso consiste en comprender por qué se nos va de las manos el proyecto, en qué momento y por qué dejamos de tener el control del código. Cuando empezamos un proyecto nuevo, un greenfield, avanzamos muy rápido implementando las primeras funcionalidades; nos cabe todo el código en un mapa mental. Es fácil y divertido añadir nuevas líneas de código, da gusto trabajar en el proyecto, pero esta cadencia no dura mucho tiempo; de manera muy progresiva, casi sin darnos cuenta, el ritmo se va haciendo más lento.
Con el paso de los meses vamos invirtiendo cada vez menos tiempo en escribir código y más a leerlo, sobre todo depurando. Tras varios años de vida del proyecto pasamos la gran mayoría de las horas leyendo el código, buscando bugs o tratando de entenderlo. Algo se tuerce con el paso del tiempo, algo se echa a perder, ¿qué puede ser?
Principalmente hay tres motivos:
Un código sostenible minimiza estos tres elementos degradantes mediante una serie de principios y técnicas, aplicables desde lo más minucioso hasta lo más global.
Veréis, tenemos el grave problema de que le prestamos muy poca atención a los pequeños detalles, sin embargo, el código no se arruina en un solo día, sino que lo vamos empeorando un poquito cada día, porque ignoramos o infravaloramos que cada línea de código importa. Cuando nos empezamos a dar cuenta de que el código se ha vuelto inhóspito, ya cuesta mucho reparar el daño. Cuando hablo de pequeños detalles me refiero a cómo nombramos las variables o a dónde las definimos e inicializamos, por ejemplo.
Hay cuatro reglas esenciales para poder desarrollar código sostenible:
Me explico:
Todavía hay muchos equipos que no escriben test, o que no escriben los suficientes, o no son test de calidad, con lo que se terminan haciendo insostenibles. Es absolutamente imprescindible saber escribir buenos test y proveer de la cobertura adecuada. Si no tienes test, todos tus demás esfuerzos por implementar una arquitectura hexagonal, domain-driven design, patrones de diseño o cualquier otra cosa, no evitarán que pierdas el control del proyecto. Esto que acabo de decir es lo más importante de esta charla, si no tienes test abundantes y sólidos, el proyecto no mantendrá su cadencia inicial. ¿Qué opina el señor agorer de un proyecto sin test?
Sin test, terminarás con un código inmanejable, porque nadie escribe código idóneo a la primera. Por más cuidado que pongas cometerás errores de diseño que solamente se pueden subsanar mediante refactorización permanente, diaria, para lo cual es fundamental tener test. La refactorización no es algo que se hace una vez al año durante dos semanas seguidas, sino unos cuantos minutos cada día.
Historieta: En 2004 monté mi primera empresa junto a tres amigos. Uno de los mejores clientes que conseguimos fue una pequeña empresa de telefonía y canales de televisión que representaba más de la mitad de nuestra facturación. Recuerdo que una mañana, mientras estaba desarrollando la parte de su web de registro de clientes, me llamaron porque ciertos usuarios no podían darse de alta en el sistema. Resulta que eran extranjeros con apellidos que contenían símbolos como el apóstrofe o la diéresis y el código de validación fallaba y no les permitía registrarse. No teníamos ni un test, no sabíamos ni cómo se hacían, y tampoco teníamos un entorno de pruebas, sino que hacíamos los cambios a cualquier hora, directamente en el entorno de producción. Aquella mañana hice como diez cambios en el código, cada uno correspondiente a una llamada de teléfono de mi cliente, quejándose por un caso diferente. Mientras arreglaba un caso estropeaba otros, estaba cada vez más tenso y mi cliente más molesto. Con cada cambio en producción teníamos que reiniciar el servicio y los usuarios conectados perdían su sesión. Al final del día el cliente se había dado cuenta de que no éramos solventes, había agotado su paciencia. Paró el proyecto y nos lo quitó. Para mí fue una gran lección y la terminé de comprender cuando aprendí a hacer test. Ahí me dije…, qué bien me hubiera ido en los proyectos anteriores si hubiera tenido test.
Advertencia: Hoy en día hay tantas alternativas que los usuarios no se molestan en llamarte a quejarse, simplemente dejan tu aplicación o tu servicio y se van a la competencia sin que te des ni cuenta. El coste de un bug en producción es más alto que nunca, porque la tolerancia de la gente es menor y las alternativas disponibles son mayores.
Consejo: Hay que reducir al mínimo el número de bugs que llegan a producción y por supuesto, hay que impedir que reaparezcan bugs que ya estaban reportados y corregidos.
La tercera regla: abstracciones adecuadas. Cualquier nombre de variable, de constante, de método, de clase, etc, es una abstracción, representa un concepto. Este concepto debería pertenecer al dominio del problema o de la solución, debería hablar de negocio y no de mecanismos de implementación, ni hablar de enteros o de booleanos. Aquí pecamos habitualmente de dos cosas:
Cuarta regla: programar con una intención explícita, es la más difícil de explicar, tendrías que estudiar todo el libro para saber a lo que me refiero, pero resumiéndola mucho, te diría que no tomes decisiones de implementación arbitrarias, sino que razones cada línea de código de manera que puedas explicar por qué debe ir así. No te quedes solo con la frase de “es que aquí las cosas se hacen así”, eso son dogmas, intenta razonar por qué se hacen las cosas así, cuáles son las ventajas y los inconvenientes. No te conformes con que el programa funcione, busca que cada línea tenga un sentido lógico razonado.
La técnica que mejor me funciona a mí para razonar sobre el código que escribo es trabajar en pares, pair programming.
POLA: El principio de diseño que más me aporta para conseguir esto, es el principio de menor asombro o menor sorpresa. Para mí este principio es el más importante de todos los principios de diseño, el que más me guía en la toma de decisiones. En el libro se explica el principio y aparecen varios ejemplos al respecto.
Consejo: reeler el código a diario, los 10 primeros minutos del día y hacer pequeños cambios como rename, extract method o inline method. La lectura del día siguiente te permite detectar posibles sorpresas que dejaste, aplicar POLA. Al llegar al trabajo, tomándote el café o el mate, ponte a leer el código de ayer.
Cumplir con las reglas del código sostenible no implica que el diseño a más alto nivel sea simple de entender o de cambiar; además tenemos que encontrar un balance entre cohesión y acoplamiento. Debemos minimizar el acoplamiento en algunas partes y maximizar la cohesión en otras.
Por acoplamiento nos referimos a la interdependencia entre dos o más artefactos.
Por cohesión nos referimos a la capacidad de un artefacto, o de un conjunto, de funcionar como una unidad.
Por tanto, el acoplamiento es necesario para construir un artefacto cohesivo compuesto por varias piezas. Ahora bien, el acoplamiento es indeseable cuando los cambios en una parte del código obligan a cambios en otras partes del código que no deberían verse afectadas por dichos cambios. El exceso de acoplamiento provoca roturas en efecto dominó, hace que cueste mucho más comprender una funcionalidad completa, que sea muy difícil testar el código, y que sea muy arriesgado refactorizarlo. Todo esto es muy abstracto, ¿cómo puedes saber si tu código tiene exceso de acoplamiento y falta de cohesión?
Pistas para detectar exceso de acoplamiento y falta de cohesión:
Algunos ejemplos de código que tiene test, pero con diseños difíciles de comprender y cambiar:
¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.
Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?
Pero espera 🖐 que tenemos un conflicto interno. A nosotros las newsletter nos parecen 💩👎👹 Por eso hemos creado la LEAN LISTA, la primera lista zen, disfrutona y que suena a rock y reggaeton del sector de la programación. Todos hemos recibido newsletters por encima de nuestras posibilidades 😅 por eso este es el compromiso de la Lean Lista