Software Crafters
Academy
Cinco Principios fundamentales sobre Ingeniería
y Artesanía del Software
Cada vez que nos hablan de Principios de Diseño de Software, nos marean con
SOLID. A ver, nada en contra, simplemente hay otros más importantes y que
la mayoría pasa por alto.
Por esta razón, he preparado esta guía con 5 principios fundamentales de
Ingeniería y Artesanía del Software (y algún otro extra) que todo developer
debería conocer.
Si no sabes qué es SOLID, no te preocupes, en los anti-principios FLACCID
los mencionamos.
Este es el contenido que encontrás en esta guía:
Anti-principios FLACCID
3
Hola,
5
Principio 1: POLA (Principle Of Least Astonishment)
7
Principio 2: No programes con null
10
Principio 3: CQS (Command Query Separation)
12
Principio 4: Funciones Puras
13
Principio 5: Cláusulas de guarda
14
Por dónde seguir
16
Anti-principios FLACCID
Probablemente en algún momento de tu carrera hayas escrito código FLACCID.
Tranquilo, no te lo tomes como algo personal.
A todos nos ha pasado.
A mi también.
Aunque probablemente no tengas ni idea de que estoy hablando.
Verás, FLACCID es un acrónimo que describe cómo NO debe ser el código que
desarrollamos.
Full responsibility
Layerless
Assume no errors
Crazy Case
Interfaceless
Dependency ejection
Este ingenioso apodo solo se lo he escuchado a mi amigo Ramón Esteban, al
parecer se lo inventaron él y unos colegas suyos cuando trabajaban en
Microsoft.
En plan sarcástico, solían decir: “En vez de SOLID, mejor escribe FLACCID”
No me quiero imaginar con qué clase de código tenían que lidiar en el día a
día para que se les ocurriera esta broma.
Ya ves, hasta en Microsoft escriben código que mata la moral de cualquiera.
En este caso más bien les agudizó el ingenio.
Bien. Solo me voy a centrar en cómo evitar la F de Full Responsibility,
gracias al principio de responsabilidad única.
Ya sabes, el primero de los 5 principios SOLID del tío Bob:
Single Responsibility
Open/Closed
Liskov substitution
Interface segregation
Dependency Inversion
Por cierto, Robert C. Martin solo inventó el acrónimo, no los principios.
El SRP es el más complejo y menos comprendido de todos los principios
SOLID. Esto quizás tenga que ver con que su nombre no es el más adecuado.
Y es que los developers leemos responsabilidad única y empezamos a
construir módulos que hacen una sola cosa.
Pasamos de Full Responsability, clases y módulos que tienen cientos de
líneas, a todo lo contrario, clases con un solo método.
Que las funciones hagan una sola cosa está bien, es lo que esperamos.
En cambio las clases y los módulos deben tener un nivel de abstracción más
elevado, esperamos que encapsulen más de comportamiento que una función.
Cuando nos pasamos de rosca aplicando el principio de responsabilidad
única, acabamos con responsabilidades compartidas entre diferentes
artefactos.
Esto nos lleva a perder cohesión y a generar acoplamientos indeseados.
Recuerda que cuando programamos debemos de maximizar la cohesión y
minimizar el acoplamiento.
Si quieres profundizar en los principios SOLID te recomiendo mi libro Clean
JavaScript.
De todas formas los principios que veremos en esta guía desde mi punto de
vista son mucho más interesantes que SOLID, pero antes deja que me
presente.
Hola,
Antes de seguir me presento. Mi nombre es Miguel A.
Gómez, soy emprendedor y developer especializado en la
calidad del software y en blockchain.
He trabajado en proyectos grandes, medianos y pequeños,
he co-fundado varias empresas y he ayudado a que una
startup americana se vendiera por muchos millones de
dólares gracias a escribir código sostenible.
Quizás esto no te diga nada y haces bien.
Por cierto, cuando hablo de código sostenible me refiero a código fácil de
mantener. Y con fácil de mantener quiero decir sencillo de cambiar.
Hay una verdad absoluta en el software: y es que si un proyecto tiene
cierto éxito cambiará.
Llevo desarrollando software desde 2006. En realidad empecé antes, pero ese
año fue cuando creé una pequeña aplicación web para mi primer cliente.
En aquel momento estaba terminando Ingeniería en Radioelectrónica aunque ya
sabía que programar era realmente lo que quería hacer. Me había dado cuenta
de que con el hardware no iba a llegar muy lejos desde Canarias.
Durante esa época empecé a sufrir titulitis aguda. Estaba obsesionado con
coleccionar papelitos de masters, certificaciones, carreras universitarias…
Supongo que de aquellas soñaba con tener una oficina con todos los títulos
colgados como el típico despacho de un abogado de película. Ya sabes, con
todas las paredes cubiertas de diplomas y certificados.
Y si, leíste bien, carreras universitarias en plural.
No era suficiente con estudiar una carrera que me dio por estudiar otra… y
no solo eso, sino que mientras hacía la segunda me matriculé en una
tercera.
Hice el camino largo no, el larguísimo. Ingeniería en Radioelectrónica,
Ingeniería Informática y Teleco. Las dos primeras las terminé. Con la
tercera me dije: ¿pero qué estás haciendo con tu vida?
Llegó un momento en que abrí los ojos, me di cuenta que la universidad
estaba obsoleta y que acumular papelitos no era el camino.
No hace falta ir a la universidad para ser un buen developer...
A finales de 2015 nos juntamos unos cuantos amigos y confundé mi segunda
una startup (en la que yo sería el CTO). El plan era construir una especie
de Booking .com orientado a personas con movilidad reducida.
La idea era buena, conseguimos levantar más de un millón y medio de euros
entre premios y rondas de inversión.
Pero aquello no terminó de salir bien... en la newsletter te cuento por
qué.
Hace unos años me dió por escribir un libro, Clean JavaScript, lo mismo te
suena. Lo usan miles de desarrolladores en España y en Latam como
referencia (quizás tú seas uno). También lo tienes en inglés, por si te
interesa.
El libro es un poco raro porque no va sobre JavaScript sino sobre cómo
escribir mejor software en general. Ni siquiera los ejemplos son en el
lenguaje de Brendan Eich sino en TypeScript. Por cierto, lo escribí antes
de ChatGPT, ahora todo el mundo tiene uno.
Principio 1: POLA (Principle Of Least
Astonishment)
¿Cuál crees que es el principio de diseño más importante?
Cuando hago esta pregunta a otros developers muchos, además de SOLID,
suelen decir: GRASP, DRY, YAGNI, KISS…
Pero muy pocos dicen POLA (Principle Of Least Astonishment). El principio
de menor sorpresa, en español.
Este quizás sea el rey de todos los principios en el desarrollo de
software. El que hay que priorizar, en el que se basan los demás.
La idea es sencilla:
El código debe actuar de la manera que dice, sin tener que entrar a mirar
el detalle cada función o método privados, ni cada una de las definiciones
de variables.
Cada abstracción debe comportarse como una caja negra, con una entrada y
una salida bien definida, con un comportamiento obvio.
const user: Maybe<User> = findUserBy(userId:Id);
¿Podría uno esperar que esa línea de código enviará un email, o que hiciera
alguna modificación en la base de datos?
No, ¿verdad?
Por eso debes procurar que el código se comporte como cabe esperar, sin
sorpresas. Las sorpresas en el software no le suelen gustar a nadie. Más
que nada porque casi nunca son buenas.
Veamos algunos ejemplos más de sorpresas (quizás leyéndolas aquí te hagan
gracia).
Los ejemplos son en JavaScript y casi todas tienen que ver con esos 10 días
interminables en los que Brendan Eich dio a luz a la criatura.
¿Qué resultado genera cada una de las siguientes líneas de código?
[] + []
[] + {}
{} + []
{} + {}
¡SPOILER ALERT!
No hagas scroll hasta que lo hayas pensado.
…
…
…
Aquí tienes la solución:
[] + [] // ""
[] + {} // "[object Object]"
{} + [] // 0
{} + {} // NaN
Probablemente, si no has visto la famosa charla WAT de Gary Bernhard es muy
difícil que supieras el resultado y aunque la conocieras seguramente ni te
acuerdes. (Si no la has visto la tienes aquí, es muy buena solo dura 4
minutos)
Bien. Este comportamiento es totalmente inesperado. Parece como si el
runtime de JavaScript estuviera diciendo:
“Mmmm no puedo sumar esto, pero aquí tienes unos cuantos resultados
extraños para que te entretengas”.
Menos mal que TypeScript nos protege de este tipo de cosas.
Veamos otro ejemplo.
Adentrémonos en el fascinante mundo de mezclar números y strings. A ver si
eres capaz de intuir lo que hace este código:
"3" + 4 + 5
3 + 4 + "5"
¡SPOILER ALERT!
No hagas scroll hasta que lo hayas pensado bien.
…
…
…
Aquí tienes la solución:
"3" + 4 + 5 // "345"
3 + 4 + "5" // "75"
¿Podría ser más sorprendente el resultado?
Difícilmente.
Es curiosa la forma que tiene JavaScript de mezclar peras con manzanas… O
mejor dicho, de convertir manzanas en peras y luego mezclarlas.
Principio 2: No programes con null
Seguro que más de una vez (incluso en tu propio código) te has encontrado
con problemas de este tipo:
TypeError: null is not an object
TypeError: null or undefined has no properties
Esto sucede cuando se propaga el valor nulo por la aplicación y se genera
una excepción no controlada.
A pesar de estos errores seguimos usando null alegremente en nuestro
código.
La cosa empeora en JavaScript porque además de null tenemos undefined, dos
valores para indicar la ausencia de valor. Qué podría salir mal…
Bien.
La sentencia null nació por un hecho fortuito en la década de 1960.
Tony Hoare, ganador del premio Turing, lo agregó al lenguaje ALGOL W,
porque en aquel momento le parecía práctico y sencillo de implementar.
Varias décadas después mostró su arrepentimiento. Estas fueron sus
palabras:
“Lo llamo mi error del billón de dólares…
En aquella época, estaba diseñando el primer sistema de tipos estáticos
para un lenguaje orientado a objetos.
Mi objetivo era garantizar que todo uso de referencias fuera absolutamente
seguro, con una verificación realizada automáticamente por el compilador.
Pero no pude resistir la tentación de habilitar las referencias a null,
simplemente porque era muy fácil de implementar.
Esto ha llevado a innumerables errores, vulnerabilidades y fallas en los
sistemas.
Lo que probablemente ha causado miles de millones de dólares de dolor y
daños en los últimos cuarenta años.”
Tony Hoare, en la Qcon Conference de Londres en 2009.
Asignar valores a null es un problema, pero devolverlos en funciones aún lo
es más.
Ya que obliga al cliente a preguntar si la respuesta es nula para tomar la
decisión de qué flujo debe continuar.
Esto provoca un acoplamiento oculto entre la API y quien la utiliza.
Recuerda, en un buen diseño debemos buscar el mínimo acoplamiento y la
máxima cohesión.
Además, cuando devolvemos null lo único que conseguiremos es que se
propague un estilo de programación defensiva a lo largo de nuestro código,
lo que empeora la legibilidad e incrementa la complejidad accidental.
Muchos lenguajes añaden azúcar sintáctico para preguntar por los nulos,
mediante operadores como el Elvis o null coalescing operator.
Esto no significa que nos quiten de encima las comprobaciones ni los
peligros de trabajar con null o undefined, simplemente simplifican su
sintaxis.
Para manejar la ausencia de valores sin usar nulos existen técnicas como la
mónada Maybe o el patrón objeto nulo.
Principio 3: CQS (Command Query
Separation)
“Las funciones que modifican el estado no deberían devolver valores y las
funciones que devuelven valores no deberían modificar el estado”
- Bertrand Meyer
El principio de diseño Command-Query Separation, separación de comandos y
consultas en español. Fue introducido por primera vez por Bertrand Meyer en
su libro Object Oriented Software Construction.
Ojo. No lo confundas con CQRS, qué es un patrón de arquitectura de
software. Aunque es cierto que CQRS está basado en CQS.
La idea fundamental de este principio es tratar de dividir las funciones de
un sistema en dos categorías claramente diferenciadas:
Consultas (queries): devuelven un valor y no alteran el estado del sistema.
Siempre devuelven un valor.
function findByName(name: string):User[]{
/*...*/
}
Comandos (commands): son funciones que cambian el estado del sistema, es
decir, generan lo que se conoce como un side effect (efecto secundario).
También pueden ser conocidos como modificadores (modifiers) o mutadores
(mutators). No deberían devolver ningún valor (void).
function deleteUser(user: User):void {
/*...*/
}
Este principio de diseño tiene dos ventajas fundamentales. La primera es
que promueve el uso de funciones puras (en el siguiente principio veremos
de que se tratan).
Y la segunda es que delimita claramente donde se produce un cambio de
estado en el código. Lo que ayuda a prevenir bugs. Y es que como dice
Martin Odersky (creador de Scala):
El estado es la raíz de todos los males.
Principio 4: Funciones Puras
Las funciones puras son uno de los pilares de la programación funcional. Al
trabajar con funciones puras, garantizamos que nuestra lógica sea
predecible, más fácil de testear y menos propensa a errores.
Pero, ¿qué significa exactamente que una función sea pura?
Determinismo: siempre devolverá el mismo resultado para los mismos
argumentos. No importa cuántas veces la llames o en qué contexto lo hagas,
el resultado será el mismo. Esto significa que no debe depender de ningún
estado externo que pueda cambiar entre llamadas.
function add(a: number, b: number): number {
return a + b;
}
En el ejemplo, add es una función pura. Si pasas 2 y 3, siempre obtendrás
5, sin importar cuántas veces la llames o del estado del sistema en ese
momento.
Sin efectos secundarios: Una función pura no debe alterar el estado del
sistema ni producir efectos secundarios, como modificar variables globales,
escribir en una base de datos o enviar datos a una API. Todo lo que hace
una función pura es calcular un valor y devolverlo. Nada más y nada menos.
Las funciones puras ofrecen varias ventajas:
Facilidad de testeo: Dado que las funciones puras siempre devuelven el
mismo resultado para los mismos argumentos, se pueden testear fácilmente en
aislamiento. No necesitas simular el estado del sistema o lidiar con
dependencias externas.
Composición: Las funciones puras se pueden combinar entre sí para construir
operaciones más complejas de manera segura y predecible.
Paralelismo: Como las funciones puras no dependen del estado externo ni lo
modifican, son inherentemente seguras para ejecutar en paralelo. No tienes
que preocuparte por las condiciones de carrera.
Principio 5: Cláusulas de guarda
El último principio del que te voy a hablar es del uso de cláusulas
de guarda.
Este principio no es tan popular como los que ya hemos visto, pero es
extremadamente útil para mejorar la legibilidad y la mantenibilidad de tu
código.
Las cláusulas de guarda, también conocidas como aserciones o
precondiciones, son comprobaciones que se realizan al principio de una
función para verificar que las condiciones necesarias para su ejecución se
cumplen.
Si estas condiciones no se cumplen, la función termina inmediatamente,
evitando así la ejecución del resto del código. De esta manera salimos de
la función lo antes posible.
Veamos un ejemplo que calcula el precio total de un conjunto de artículo en
base a la cantidad:
Ahora veamos el mismo ejemplo con cláusulas de guarda.
Cómo puedes observar la lógica condicional se ha simplificado muchísimo.
Dejando el código más legible y fácil de entender.
Aunque es cierto que en este código no se cumple el principio de usar
funciones puras. Esto lo podríamos mejorar usando el tipo monádico Either,
aunque esto queda fuera del alcance de esta guía.
Por dónde seguir
Verás.
En el 2020 (en pleno apocalipsis) me junté con mi colega Carlos Blé (el
fundador de Lean Mind) con la intención de crear la mejor formación posible
para developers profesionales.
La formación que nos hubiera gustado encontrar a nosotros cuando empezamos.
Después de dedicarle más de dos años a desarrollar los contenidos, de esa
asociación no salió un curso, sino varios.
Los cursos son tan buenos que esa es la formación que hacen los nuevos
programadores que se incorporan en Lean Mind, su empresa de consultoría, da
igual que sean Juniors o Seniors.
Ahora estas formaciones ayudan a cientos de developers profesionales de
decenas de empresas a llevar sus carreras a otro nivel.
¿No me crees? Haces bien.
Échale un ojo a los testimonios (que por supuesto me he inventado) de
algunos de los programadores que se han formado con nosotros y me cuentas.
Uno de estos cursos es Diseño Sostenible, Ingeniería y Artesanía del
Software con TypeScript.
Si te interesa la tienes en el enlace de abajo.
Diseño Sostenible, Ingeniería y Artesanía del
Software con TypeScript