Estas abreviaturas todo programador debe conocer tan bien como la tabla periódica.
En varios años trabajando en la industria he visto todo tipo de abreviaturas: serias y divertidas. Y, para ser honesto, a todos los desarrolladores les gustan las abreviaturas. Algunas simplemente facilitan el habla, por ejemplo, MVP o PoC (está claro que, al crearlas, nadie se esforzó demasiado). Otras también suenan divertidas: por ejemplo, SOLID, DRY y KISS.
En general, hay tantas abreviaturas en la jerga de los desarrolladores, que he preparado un pequeño resumen de las más (y algunas menos) comunes, por si acaso se te olvidó qué significan.
DRY
Empecemos con una abreviatura elemental que seguramente te has encontrado muchas veces. DRY (del inglés dry – seco, secar) es un principio fundamental de desarrollo. Se expande como Don’t repeat yourself – «no te repitas».
Cuando escribas código, siempre piensa en cómo puedes reutilizar ese fragmento, qué puedes destacar en una función o clase universal, hacerlo un módulo. No se trata de crear bibliotecas para cada función que se repite, me refiero a una lógica muy similar que aparece en varios lugares, que quizás tenga sentido sacar a una función. Y si se define la misma función en varios lugares, se puede sacar a un módulo común. Por último, si utilizas el mismo módulo con frecuencia, es probable que puedas convertirlo en una biblioteca.
En otras palabras, don’t repeat yourself, ¿entiendes?
Este principio es útil siempre, independientemente de la plataforma o el lenguaje. Supongamos que necesitas automatizar un determinado comportamiento. Para no escribir varias veces la misma lógica y no inflar el código sin necesidad, intenta generalizarlo y sacarlo a un elemento independiente.
KISS
Esta abreviatura (del inglés kiss – beso, besar) siempre me ha gustado, tanto por su forma como por su significado: Keep it simple, stupid («Hazlo más simple, tonto«) o, si a alguien no le gusta que lo llamen tonto, hay una variante Keep it stupid simple («Que todo sea simple hasta la desvergüenza«), que transmite mejor el significado de la abreviatura.
Al resolver un problema, puedes entusiasmarte tanto que, sin darte cuenta, ya estás haciendo overengineering o, como me gusta decir yo, disparando con un cañón a los gorriones. La tarea al final, por supuesto, se resolverá, pero se podría haber hecho mucho más fácil y elegantemente.
No discuto que haya situaciones inversas. Un código demasiado simple o una arquitectura demasiado simple pueden resultar ineficaces, y entonces habrá que añadir un poco más de complejidad a la lógica. Pero aún así, hay que preguntarse cada vez si se cumple el principio KISS.
Comprueba si tus cadenas lógicas son lo suficientemente comprensibles. ¿Bastará el conocimiento de tus compañeros para entenderlas? Un código simple y un diseño simple reducen el riesgo de errores y, además, es más fácil leer ese código. En general, ¡no te olvides del KISS!
SOLID
Esta es otra regla general de la programación. Se expande así:
- Single responsibility principle (principio de responsabilidad única).
- Open-closed principle (principio de apertura/cierre).
- Liskov substitution principle (principio de sustitución de Liskov).
- Interface segregation principle (principio de segregación de la interfaz).
- Dependency inversion principle (principio de inversión de las dependencias).
Resulta que son cinco principios diferentes en uno (del inglés solid – sólido, denso, resistente). Veamos cada uno por separado.
1. Single responsibility principle, principio de responsabilidad única
Dice que cada una de tus funciones debe realizar una sola tarea.
Si estás familiarizado con los sistemas *NIX, como las distribuciones Linux, macOS y otras, seguramente has tenido que ver con su terminal y sus comandos, como ls
o cd
. Siguen estrictamente el principio SRP: realizan solo un tipo de tarea (por ejemplo, cambian de directorio o muestran una lista de su contenido). No encontrarás una utilidad que permita realizar varias tareas a la vez (en nuestro ejemplo, cambiar de directorio y mostrar una lista de su contenido). Esto incluso se llama UNIX-way.
Para tener una sola responsabilidad, las funciones deben ser simples. Si necesitas un comportamiento más complejo, tendrás que combinar las entradas y salidas de varias funciones y hacer una composición.
Y aunque ahora hablo de funciones, el principio SRP es aplicable a casi todo. Tomemos, por ejemplo, la arquitectura de una plataforma. Es mucho más fácil de mantener y desarrollar si, en lugar de un megamódulo responsable de todo, tienes varios microservicios, cada uno de los cuales es responsable de su propia pequeña tarea. Pero, una vez más, lo mismo ocurre con las funciones: las funciones más simples y comprensibles son más fáciles de mantener, leer, comprender e incluso de escribir.
2. Open-closed principle, principio de apertura/cierre
Dice que tus módulos o bibliotecas (dependiendo de cómo se exporte el código) deben estar abiertos a la extensión (por ejemplo, la extensión del comportamiento), pero cerrados a la modificación (porque nadie quiere molestarse con el módulo de otro).
Si te das cuenta de que para añadir un nuevo comportamiento a tu código o para ampliar el comportamiento ya existente, tienes que modificarlo, ¡felicidades, has ignorado con éxito el principio de apertura-cierre! 🙂
Aquí tienes un ejemplo claro: el código que se muestra infringe este principio. Porque, si hay que añadir otra ciudad, habrá que abrir el propio archivo y hacer cambios en el array knownCities.
// non-open-mod.js
let knownCities = ["Madrid", "Barcelona", "New York"]
module.exports = {
isAValidCity: function(cityName) {
return knownCities.indexOf(cityName) != -1
}
}
Y aquí tienes cómo resolver este problema y respetar el principio de apertura/cierre.
// open-closed-mod.js
let knownCities = ["Madrid", "Barcelona", "New York"]
module.exports = {
isAValidCity: function(cityName) {
return knownCities.indexOf(cityName) != -1
},
addValidCity: function(cityName) {
knownCities.push(cityName)
}
}
Como vemos, con el método addValidCity podemos ampliar el comportamiento del código a nuestras necesidades, sin necesidad de modificar el archivo.
3. Liskov substitution principle, principio de sustitución de Liskov
Este principio, también conocido como LSP, nos acerca al máximo a la propia teoría de la programación. No profundizaré demasiado en ella, simplemente describiré la esencia del principio de sustitución.
Inmediatamente llama la atención que aquí tenemos que ver claramente con un principio de OOP, que ayuda a aplicar correctamente la herencia cuando es necesario y a utilizar alternativas cuando la herencia no es necesaria.
Veamos esto con un ejemplo. En geometría, un cuadrado es una variedad de rectángulo. En esencia, es un rectángulo con la misma anchura y longitud. Si intentas modelar esto con código, puedes obtener algo parecido a esto:
// lsp-sample1.js
class Rectangle {
width
height
constructor(w, h) {
this.width = w;
this.height = h
}
setWith(w) {
this.width = w
}
setHeight(h) {
this.height = h
}
}
class Square extends Rectangle{
}
En la abstracción no tiene sentido: los métodos setWidth y setHeight no cambian nada, no se puede obtener un cuadrado a partir de un rectángulo, y eso no es lo que necesitamos. Por lo tanto, el código anterior infringe el principio de sustitución de Liskov.
En otras palabras, LSP garantiza el uso correcto de la herencia en tu código. Así que, por raro que te parezca, vale la pena recordar este principio al crear clases.
4. Interface segregation principle, principio de segregación de la interfaz
Dice que no se puede obligar a los programadores que trabajan con tu código a utilizar métodos innecesarios. Después de todo, una interfaz es solo la interacción de las clases (un conjunto de métodos que deben implementarse). Por lo tanto, al crear interfaces (por ejemplo, en TypeScript), asegúrate de que los métodos que hay que implementar sean realmente necesarios para tus usuarios, y no pongas un montón de funcionalidad en una sola interfaz. Divide y vencerás.
bad-interface.ts
interface MyModule {
close(): void
open(connectionString: string): void
handleIE8Compatibility(): void
}
Veamos este código. Quizás al trabajar con MyModule a alguien le apetezca implementar los métodos close y open, pero si no es necesario asegurar la compatibilidad con IE8, el último de los tres métodos enumerados en el código seguramente no será necesario.
En otras palabras, el principio de segregación de interfaces tiene que ver con el diseño. Respetándolo, separa los métodos entre sí: que los usuarios decidan qué métodos aplicar y para qué tareas.
5. Dependency inversion principle, principio de inversión de las dependencias
El concepto de SOLID se cierra con el principio de inversión, o inyección, de las dependencias.
Clásicamente suena así:
- Los módulos de nivel superior no deben importar entidades de los módulos de nivel inferior. Ambos tipos de módulos deben depender de abstracciones.
- Las abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones.
Esta es una herramienta muy útil para muchos escenarios, por ejemplo, para las pruebas unitarias. Si pruebas un código que depende de una biblioteca de terceros, puedes inyectar una simulación de esa biblioteca para controlar el comportamiento del código. Veamos un ejemplo:
// no-dep-injection.js
const dbConn = //....
export function saveUser(user) {
return dbConn.save(user)
}
Aquí vemos que la conexión a la base de datos se declara y se ejecuta dentro de un solo módulo, y la función saveUser se exporta. Si ahora intentas probar el código, esta función intentará automáticamente realizar su tarea original: conectarse a la base de datos y guardar los datos del usuario en ella.
Sin embargo, si permites la inyección de dependencias y tomas la conexión a la base de datos (que es la dependencia) como segundo parámetro, puedes inyectar una simulación más adelante:
// injected-db.js
export function saveUser(user, dbConn) {
return dbConn.save(user)
}
Ahora incluso puedes cambiar las dependencias, por ejemplo, utilizar otro módulo para conectar a la base de datos, en el código no se reflejará, seguirá ejecutando la lógica necesaria.
La inyección de dependencias es una herramienta estupenda. Permite crear un diseño ampliable y es muy útil para quienes valoran la ampliabilidad en el desarrollo. Tenlo en cuenta en tu trabajo.
YAGNI
El principio, también conocido como You ain’t gonna need it («No lo vas a necesitar«), proviene de la programación extrema. Según él, solo hay que crear una funcionalidad cuando realmente se necesita.
Se trata de que en el marco de las metodologías Agile, hay que centrarse solo en la iteración actual del proyecto. Trabajar por adelantado, añadiendo al proyecto más funcionalidad de la que se necesita en este momento, no es una buena idea, teniendo en cuenta lo rápido que pueden cambiar los planes.
¿Qué quiero decir con cambio de planes? Las iteraciones en Agile son bastante cortas, es decir, recibirás algún tipo de retroalimentación en las primeras etapas del desarrollo, y potencialmente puede cambiar la dirección de todo el trabajo en el proyecto. Entonces, ¿para qué perder tiempo en una función que al final resulte totalmente inútil?
Sin embargo, quiero añadir que si utilizas métodos de desarrollo en cascada, donde todo el trabajo se planifica de antemano y todos intentan seguir el plan con precisión, el principio YAGNI no es aplicable.
BDUF
Y ya que hablamos de la metodología en cascada, no podemos olvidar el principio Big design up front («Diseño a gran escala desde el principio«), que se adapta perfectamente a ella. Afirma que la mayor parte del tiempo dedicado al diseño de una aplicación no se dedica en absoluto a escribir código.
El propio modelo en cascada implica que hay que pensarlo todo de antemano, esperando que gracias a esto no haya que perder tiempo después en buscar y eliminar fallos.
Por supuesto, como muchos otros principios descritos aquí, BDUF tiene sus detractores. Son especialmente propensos a atacarle los partidarios de las metodologías ágiles: creen que en un mundo dominado por Agile, es simplemente inútil.
SoC
Separation оf concerns (principio de separación de responsabilidades) es uno de mis favoritos. Lo utilizo al diseñar plataformas o al crear la arquitectura interna de un proyecto.
No lo confundas con el Single responsibility principle (principio de responsabilidad única) que ya hemos mencionado. SoC ayuda a combinar funciones o módulos en servicios independientes. La cuestión es que, al diseñar un sistema multifuncional (y normalmente es así), puedes agrupar las funciones en módulos en función de las tareas que realiza cada uno.
Ejemplo: una plataforma de blog, donde los usuarios pueden publicar entradas. Un sistema en una plataforma de este tipo puede ser perfectamente responsable de todo (gestión de usuarios, entradas de blog, análisis, etc.) e incluso hacer frente a sus funciones. Pero si sigues el principio de SoC, puedes llegar a una solución más interesante:
Este es, por supuesto, un ejemplo muy burdo, pero la esencia de esta arquitectura es que se divide la responsabilidad por módulos. Como resultado:
- Se obtiene la escalabilidad de cada bloque de funciones. Ahora, si es necesario, puedes escalar fácilmente el mismo módulo de gestión de usuarios (User management), por ejemplo, porque está sobrecargado de tráfico, mientras que el resto de la plataforma permanece sin carga.
- Se hace más fácil introducir cambios: la relación entre los elementos del código se ha debilitado, y, por ejemplo, podrás reescribir casi por completo el módulo de gestión de entradas sin afectar a otras secciones.
- La plataforma se vuelve más estable. Si uno de los módulos falla, la plataforma potencialmente podrá seguir funcionando. Con menos funcionalidad, por supuesto, pero aún así es posible.
SoC también se puede aplicar al crear API, arquitecturas de bibliotecas y cosas por el estilo. Su esencia es agrupar las funciones a tu gusto y para el beneficio de quienes las usen.
MVP
Si nos alejamos de la programación, podemos recordar otras abreviaturas que se utilizan a menudo en nuestro sector. MVP significa Minimum viable product (producto mínimo viable) y supone crear la funcionalidad mínima necesaria que ayuda a entender cómo funciona el producto en condiciones reales y si gusta a los usuarios.
Este método se utiliza a menudo para averiguar si vale la pena invertir tiempo en el producto y llevarlo a buen término. Gracias a MVP, la audiencia objetivo ya puede probar el producto y dar su opinión.
PoC
A diferencia de MVP, que requiere una planificación seria y grandes gastos de desarrollo, Proof of concept (prueba de concepto) suele ser una versión recortada de él. Se utiliza en la fase previa a MVP y solo tiene como objetivo confirmar o refutar la necesidad de una funcionalidad adicional.
En esencia, PoC es una especie de material de consumo, código temporal. No se escribe para implementarlo, sino para demostrar el proyecto. En principio, se puede incluir PoC en un producto real, pero ten en cuenta que para demostrar un concepto, a veces hay que crear varios PoC. Es una mala idea trabajar en todos ellos para obtener al final un solo producto actual. Así que se programa a propósito como una porquería, para que después no dé pena tirarlo.
Personalmente, me gusta mucho el concepto de PoC, pero precisamente como material de consumo. Es como Iron Man y su armadura Mark I: demostró de lo que era capaz, incluso teniendo solo un reactor de arco a mano; y Mark II ya era unas cuantas órdenes de magnitud mejor.
Conclusión
Todas las abreviaturas mencionadas en este artículo seguramente las has encontrado en las instrucciones y manuales o las has oído de tus compañeros. Ahora sabes qué significan y en qué contexto usarlas.