La importancia de elegir nombres apropiados en ingeniería de software
“There are only two hard things in Computer Science: cache invalidation and naming things.”
- Phil Karlton
Cuando comenzamos a programar solemos cometer el error de pensar que una forma de optimizar nuestro código es abreviando todo símbolo que creamos (nombres de variables, métodos y clases), con la seguridad de que recordaremos qué significa cada acrónimo.
Más adelante, conforme vamos interactuando con otros desarrolladores, empezamos a darnos cuenta de que debemos ser un poco más explícitos en la nomenclatura dado que todos se quejan por no entender una sola línea de nuestra implementación; sin embargo, tratamos de preservar esta pulsión de mantener las cosas “lo más pequeñas posibles”, entonces hacemos un pacto: ok, renombraré esta clase y su método x7::p_a7 por ExternalService::PushCU; todos sabemos que ‘CU’ significa ‘created user’ dentro de nuestra aplicación. Y entonces el equipo sigue adelante.
Luego, llegan los chicos nuevos al equipo, y comienzan a hacer preguntas “tontas y obvias”, como perdón, qué significa CU?, o qué es realmente el externalService? una API nuestra o de terceros?, y notamos que les lleva mucho tiempo comenzar a ser productivos, y que a pesar de que han pasado varios meses, aún siguen haciendo preguntas como si fueran novatos. Entonces ya resignados, decidimos modificar nuestro código y hacerlo “a prueba de tontos”: por ejemplo, renombramos ExternalService::PushCU a UserService::PushCreatedUser. Pero aún así, siguen llegando consultas sobre cómo funciona internamente el método PushCreatedUser., dado que no sólo se envía el usuario creado, sino que además se actualiza este usuario en la base de datos estableciendo el día y hora actual en un campo de fecha llamado “enviado”, y si la comunicación con el servicio falla, se envía un correo electrónico a admin@miempresa.com.
Para continuar con la lectura, debemos ponernos de acuerdo en que ninguno de los tres enfoques anteriores son correctos, ya que:
- Los nombres cortos no harán tu código más rápido: todos sabemos que en lenguajes compilados (y varios transpilados) los símbolos son sustituidos, por lo que en tiempo de ejecución su impacto es nulo. En el caso de los lenguajes interpretados como PHP, el impacto es despreciable en comparación con operaciones de E/S o lectura/escritura, más aún cuando utilizamos algún mecanismo de optimización como OpCache.
- La jerga interna y los acrónimos no forman parte de una definición formal de negocio: son sólo eso: abreviaturas que usamos para comunicarnos internamente, son parte de la moda y de la gente que las utiliza y no ofrecen expresividad, ni autodefinición, ni significado alguno.
- Los nombres de las clases y métodos deben expresar de forma clara y concisa su intención: si el nombre no describe explícitamente cada acción interna definida, entonces el nombre no es ni claro ni conciso.
Sabemos que hay vasta bibliografía relacionada con buenas prácticas de diseño y escritura código, y podríamos destacar a Robert Martin y su afamado libro Clean Code y pasarnos horas hablando al respecto. También podríamos rasgarnos las vestiduras y clamar los 5 principios SOLID recitándolos de memoria como si fueran un mantra, pero no es el objeto de este artículo. Por ello y para no perder más tiempo, vamos a plantear el tópico con un ejemplo.
Caso práctico
Veamos el siguiente ejemplo en pseudo-PHP:
<?php
class Post {
public int $userId;
public \DateTime $createdAt;
public string $type;
public bool $canComment;
}
class PostRepository {
...
function getPostsByUserId($userId)
{
$posts = $this->repository->find("userId = $userId and createdAt > 2021-09-01 00:00");
foreach ($posts as $post) {
if ($post->type == 'public') {
$post->canComment = true;
}
}
return $posts;
}
}
Desde luego que es un ejemplo atípico, pero creo que todos nos hemos topado con código similar a éste en algún momento de nuestras vidas. En fin, analicemos las acciones que se llevan a cabo dentro de getPostsByUserId:
- ✅Se buscan posts por ID de usuario y ❌mayores a una fecha predefinida.
- ❌Se altera el estado de cada post cuando se da una condición específica: esto es muy común verlo en actualizaciones de código realizadas a las apuradas ó por programadores que no comprendieron el flujo de trabajo establecido, ó ambos casos.
Entonces, el nombre del método no define correctamente qué hace dicho método y, por analogía, la cláusula del contrato no es transparente, dado que oculta información. Y sí, esto es un gran problema dado que no estamos transmitiendo la intención de nuestro código de manera clara hacia afuera.
Si quisiéramos cambiar el nombre por uno representativo, sería algo así como:
getPostsByUserIdAndCreatedSince20210901AndWhenThePostIsPublicThenMarkItAsCommentable
Entonces, ¿es el nombre el problema? Claro que no. El problema es que estamos desarrollando diferentes responsabilidades dentro de un mismo método, lo cual nos está pidiendo a gritos que refactoricemos.
Hagámoslo entonces:
<?php
class Post {
public int $userId;
public \DateTime $createdAt;
public string $type;
public funcion canComment (): bool
{
return $post->type == 'public';
}
}
class PostRepository {
...
function getPostsByUserIdSince20210901($userId)
{
return $this->repository->find("userId = $userId and createdAt > 2021-09-01 00:00");
}
}
Dejemos de lado que estamos modificando una interfaz externa (reemplazando un atributo público por un método) y que tendremos que actualizar además las llamadas que lo consuman, ya que la mejora lograda vale la pena. Sepan disculpar también la fecha estática en la consulta, la cual preservamos a los fines de ilustrar el cambio en el nombre.
El concepto que quiero destacar es cómo afecta la nomenclatura en las decisiones de diseño y estructuración de nuestro código, por lo que voy a omitir los detalles dado que este no es un tutorial de refactoring. En este caso, elegir un nombre absurdo, pero descriptivo para nuestro método, nos ayudó no sólo a transmitir claramente la intención, sino que además nos invitó a mejorar nuestro código, haciéndolo más fácil de comprender, depurar y probar. Esto fue gracias a que, intentando establecer un nombre expresivo, fuimos capaces de comprender el alcance y las responsabilidades del método.
Algunas recomendaciones personales:
- Si aún no lo hiciste, lee acerca de los principios SOLID y busca ejemplos sobre cómo implementar cada principio.
- Busca un mentor: alguien que te haya demostrado tener experiencia y pasión por la ingeniería de software y sepa explicar conceptos difíciles de forma simple.
- Investiga sobre normas y recomendaciones de formato de código para el lenguaje que estás usando: por ejemplo, en el caso de PHP tenemos las PSR.
- Muéstrale tu código a un compañero y pregúntale si lo entiende: no me refiero a la revisión de código (CR), sino a que lo hagas antes, que te comente qué entiende que hace tu código. En caso de que la respuesta no sea la esperada, pregúntale qué cambiaría para que le resulte más claro el cometido del mismo.