Clean Code: hacia un trabajo legible

Clean Code: hacia un trabajo legible

Al comienzo de mi viaje como programador, mi principal preocupación era ver que el código funcionara como se especifica. El sentimiento sigue siendo indescriptible. Sin embargo, además de funcionar correctamente, el código debe ser legible.

La mayoría de los proyectos de software se construyen en equipo, por lo que es esencial que todos los involucrados entiendan el código escrito previamente.

Incluso, después de un tiempo, la persona que escribió el código puede olvidar los detalles de implementación y necesitar leer y comprender el código para incluir alguna mejora o mantenimiento. Por estas y otras razones, este artículo se propone demostrar algunas técnicas que contribuyen a mejorar la legibilidad del código.

Sobre el aprendizaje continuo


Las implementaciones que se refactorizarán en este artículo se realizaron a medida que aprendí conceptos básicos. Por lo tanto, presentan muchas oportunidades de mejora en aspectos como la legibilidad, el rendimiento y la escalabilidad, por ejemplo. La identificación de estas oportunidades refleja la experiencia adquirida casi dos años después de la implementación original.

Estoy seguro de que en dos años podré pensar en mejoras aún más significativas.

Contexto y reglas del Negocio

El código refactorizado es parte de un proyecto que construí durante un bootcamp. Es una red social dirigida a los amantes del vino. Todas las interacciones se realizaron a través de cofradías, lo que permitió generar publicaciones, likes y agendar encuentros presenciales. Refactoricemos las implementaciones de la funcionalidad para crear gremios y agregar nuevas personas.

Antes de ver la implementación, hablemos un poco sobre las reglas comerciales para crear fraternidades:

  • La persona tiene la opción de agregar uno o más participantes, informando a los respectivos correos electrónicos;
  • Agregar más participantes es opcional;
  • Por defecto, quien crea la cofradía tiene la condición de canciller (o Chancellor), lo que permite agregar, eliminar personas y moderar contenidos.

El desorden

Escrito en JavaScript usando NodeJS y Sequelize, el proyecto fue construido usando la arquitectura MVCS (Model View Controller + Services). El código en el que trabajaremos a continuación se refiere a la implementación del método brotherhoodCreator de BrotherhoodController y el método addMembers de BrotherhoodService, los cuales pueden verse a continuación:

Controller brotherhoodCreator

(https://codepen.io/jairocket/pen/gOjBEMz) <insertar codepen>.

Service addMembers

(https://codepen.io/jairocket/pen/mdjzoRx) <insertar codepen>.

Confuso, ¿no? Primero, la mayor parte de la implementación de brotherhoodCreator se replica en addMembers. Los bloques if y else anidados hacen que la lógica algorítmica sea difícil de entender. El array fmembers no tiene un nombre intuitivo y, finalmente, algunas constantes se declaran como si fueran variables. Mucho trabajo, ¿eh?

Don't Repeat Yourself

Este principio, también conocido como el acrónimo DRY (o No Te Repitas, en español), se refiere en este contexto a la recomendación de evitar la repetición de código. Uncle Bob afirma en Clean Code que esta práctica "puede ser la raíz de todos los males en el software". El hecho es que esta práctica desordena el código y dificulta su lectura. En nuestro ejemplo, se duplica la implementación de la funcionalidad para agregar participantes a fraternidades.

Para resolver este problema, en los casos en que el controlador brotherhoodCreator deba agregar personas, debe hacerlo llamando al servicio addMembers.

El problema es que, analizando la implementación original, vemos que en el controlador se obtiene el id de la hermandad de la hermandad creada, mientras que en el servicio esta información se obtiene a través de la propiedad params, del objeto req. Para hacer que el método sea reutilizable, convertimos la identificación de la hermandad en un parámetro. De esta manera, la información se obtiene de manera uniforme.

Después de los cambios y la corrección de las declaraciones constantes, nota cómo la implementación del método brotherhoodCreator se volvió más simple y legible:

Ahora, podemos verificar fácilmente que el método crea un nuevo gremio basado en la información obtenida del objeto req; registra a la persona que creó la cofradía como canciller; si hay correos electrónicos contenidos en req.body.members, los agregamos a la fraternidad; y, finalmente, redirige a la persona a la ruta de fraternidad creada.

En cuanto al método del controlador responsable de llamar al servicio addMembers, se ve así:

Destaco también la creación de servicios específicos para la funcionalidad que crea una nueva cofradía en el banco y para la que otorga al responsable de crear la cofradía la condición de canciller (o Chancellor):

AddMembers

Ahora podemos manejar el servicio addMembers. Antes de agregar más personas a la hermandad, se deben seguir los siguientes pasos:

  • Verifique si el correo electrónico ingresado pertenece a alguien ya registrado en esa cofradía;
  • Si todos los correos informados ya son parte de la hermandad, no se debe tomar ninguna acción;
  • Si hay correos electrónicos informados que aún no forman parte de la cofradía, es necesario verificar si estos correos electrónicos están registrados en la aplicación;
  • Se enviará una invitación a los correos electrónicos no registrados;
  • Los perfiles asociados a los correos electrónicos registrados se agregarán a la fraternidad, sin estatus de Chancellor.


Como hemos visto, puede intentar agregar uno o más correos electrónicos a la vez. Esto implica que el valor de req.params.members puede contener un correo electrónico (string) o una lista de correos electrónicos (array de strings).

En la implementación original, manejé este problema usando un bloque if/else: si la propiedad contiene una array de correos electrónicos, a través de un loop for in, el algoritmo anterior se ejecuta para cada correo electrónico contenido en la matriz. De lo contrario, se ejecutará el mismo algoritmo solo para la cadena recibida.

Dado que prácticamente se ejecuta la misma cantidad de código en los bloques if y else, podemos eliminar el bloque else con lo siguiente:

Así, garantizamos que, a partir de esta declaración, siempre trabajaremos con un array de strings.

Múltiples responsabilidades

Otro problema es que el servicio addMembers tiene demasiadas responsabilidades. Idealmente, cada método debe realizar una acción específica, evitando efectos secundarios.

De esta forma, podemos crear services específicos para cada una de estas acciones, haciendo el código más legible, escalable y sencillo de mantener.
Tenga en cuenta que al crear services específicos para cada paso del algoritmo, comenzamos a aplicar conceptos de programaçión declarativa, cuando informamos QUÉ se debe hacer, sin exponer el CÓMO. Es decir, en el controlador informamos que el siguiente paso, por ejemplo, es agregar personas, pero los detalles de cómo se agregarán estas personas serán responsabilidad del servicio y no se explicarán en el controlador.

El primer servicio extraído lo llamaremos filterNewMembers. Recibe una lista de correos electrónicos y el id de cualquier cofradía y devuelve qué correos electrónicos de la lista no están registrados en la cofradía informada. He tratado de usar nombres que sugieran lo que hace la función. Si tienes alguna sugerencia de nombre, me encantaría verla en los comentarios.

Al segundo servicio lo llamaremos inviteNewMembers. Este, a su vez, recibe un correo electrónico y una identificación de usuario. No devuelve datos, pero envía una invitación al e-mail informado en nombre del usuario correspondiente al id pasado como parámetro.

Al tercero lo llamaré fetchUserByEmail, que busca en la base de datos el perfil asociado al correo electrónico pasado como parámetro:

El cuarto y último lo llamé addRegularMember, que agrega un perfil a una hermandad, sin perfil de canciller:

Por lo tanto, la implementación final del método addMembers se ve así:


En resumen

El método filterNewMembers filtra los correos electrónicos que pertenecen a personas que no forman parte de la comunidad. Si todos los e-mails ingresados ​​pertenecen a personas que ya forman parte de la hermandad, la persona es redirigida a la página de la hermandad, sin agregar a nadie. Si hay un correo electrónico no registrado, verificamos si ese correo electrónico está registrado en la plataforma. En caso afirmativo, se agrega a la persona; de lo contrario, se envía el correo electrónico de invitación.

En este artículo, refactorizamos la implementación de un código bastante confuso para hacerlo mucho más legible. Para ello, cambiamos el nombre de las constantes y eliminamos las estructuras condicionales anidadas. Además, al comprobar que los métodos realizaban varias acciones más allá de lo propuesto, extrajimos la implementación de estos efectos secundarios, creando nuestros propios métodos, nombrándolos de forma descriptiva.

Para profundizar en el tema, recomiendo el libro Clean Code, de Robert C. Martin y la investigación sobre Programación Declarativa. Gracias por leer este artículo hasta aquí. Espero que te ayude a producir un código más limpio en tus proyectos. Termino citando al tío Bob: "si todos dejáramos nuestro código más limpio que cuando lo empezamos, simplemente no se degradaría".

Saludos.

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.