Seguimos con nuestra lista de artículos cleaning the code en la contamos nuestra visión - la de dev&del y Hello, World! - de como deberíamos programar para ser considerados buenos artesanos. En este caso vamos a hablar de la importancia de la nomenclatura en la programación
¿Qué es el código limpio?
Sí, lo sé. El objetivo principal del artículo no es contar qué es el código limpio en general, pero creo que no está de más recordar en unas pocas líneas que entiendo por código limpio.
Obviamente, nuestro código tiene que compilar y hacer lo que nos han pedido. En caso contrario puede ser muy claro, diáfano y sencillo pero no sirve para el encargo que tenemos. Que escribir código limpio tampoco nos despiste del objetivo final que no es otro que hacer las aplicaciones que nuestros clientes nos encargan. Como dice Uncle Bob: «Any fool can write code that a computer can understand. Good programmers write code that humans can understand.».
Pero también es cierto, que muy fácilmente hacemos código que “funciona” pero es farragoso. Podemos decir que un código limpio es legible, fácil de entender y que hace lo que se espera de él según lo lees. Un código bien escrito lo debería poder entender casi casi cualquiera, aunque no sepa o no sepa mucho de programación. Está claro que alguien que no sepa programar no va a poder entender todos los detalles, pero al menos sí debería ser capaz de tener una idea lo que pretende hacer el código que le estás enseñando. Si no pasa esto el código muy limpio no parece que sea.
Además, un código limpio, bien estructurado, bien nombrado, con una buena arquitectura por debajo dificulta la introducción de nuevos bugs y por tanto no solo facilita la vida de los programadores, sino que reduce su coste de mantenimiento: WIN-WIN.
¿Es importante la nomenclatura?
Pues sí.
Programar implica escribir. Y escribir código implica que estamos nombrando piezas de código constantemente por todo nuestro código, ya sean variables, nombres de clases, nombres de ficheros, métodos, interfaces…
Por encima, lo hacemos todos los programadores, da igual que estés escribiendo tu primer Hello, World! o lleves 25 años tirando líneas. Cada vez que tocas un código estás nombrando nuevas piezas de código, editando o leyendo nombres existentes.
Una buena nomenclatura es el primer paso hacia un buen código. Para mi es el punto por el que empezar tomando consciencia de la importancia del código limpio y es el primer paso que te puede llevar a programar con cabeza y con una calidad aceptable. Por encima, es probablemente el punto en el que es más fácil comenzar a mejorar y empezar a hacerlo medianamente bien. Aunque llegar a la excelencia ya es gallo de otro cantar.
Esforzarnos en poner nombres correctos nos ayudará a mejorar en otros muchos aspectos de código como el Principio de Responsabilidad Única. Según vas aprendiendo conceptos, los vas aplicando y en base a esos conceptos intentas poner nombres coherentes te vas dando cuenta de, por el mismo nombre, hay cosas que no cuadran.
Vamos pues con ciertas normas de nomenclatura
Nombres que revelen la intención
Los nombres de las diferentes piezas de código deben revelar claramente la intención del objeto.
Por ejemplo: si tenemos dos variables que se llaman ic y icc, probablemente, pasados un par de meses, no sabremos qué dato contienen. Sin embargo, si se llaman contadorCursos y contadorCursosCompletados no hay dudas.
Los nombres de las piezas de código tienen que ser inequívocos y meridianamente claros, a poder ser que no necesites información de contexto para entenderlos o saber qué contiene esa pieza en su interior.
Si un nombre requiere de un comentario para explicarlo, cámbialo.
Ejemplo:
getPassedCoursesIds(list: Array<any>): Array<number> {
let thelist: Array<number> = [];
list.forEach(element => {
if(element.passed){
thelist.push(element.id);
}
});
return thelist;
}
En este fragmento de código. ¿Qué tiene list? ¿Qué incluimos en thelist? Element no nos dice nada.
Podemos cambiarlo por este otro código:
getPassedCoursesIds(coursesList: Array<any>): Array<number> {
let passedCoursesIds: Array<number> = [];
coursesList.forEach(course => {
if(course.passed){
passedCoursesIds.push(course.id);
}
});
return passedCoursesIds;
}
Una vez refactorizado podemos ver que el código es mucho más claro donde el parámetro de entrada vemos que son la lista de cursos (coursesList), la lista de ids de cursos aprobados que vamos a devolver se llama passedCoursesIds. Dentro del for del array el elemento a revisar es un curso, pues le llamamos course.
Usa nombres que se puedan pronunciar y buscar
Por suerte la mayoría de los programadores somos humanos. Y a los humanos se nos dan bien las palabras que por definición son pronunciables, por eso la importancia de usar una buena nomenclatura. Un nombre pronunciable facilita su comprensión y memorización. Por mucho que yo no sea ningún fanático de la memorización, cuando estás viendo código si este es fácil de recordar menos esfuerzo tienes que dedicar en recordar puntos triviales y puedes enfocarte en la parte complicada del código que estás leyendo.
El desarrollo de software es una tarea social que implica comunicación dentro del equipo. Si usamos nombres que seas capaz de pronunciar no quedarás como un idiota cuando intentes explicar lo que hace cierto método.
Como programador estamos todo el santo día buscando textos dentro del código. Si usamos textos que sean fáciles de buscar nos ahorrará horas y horas de trabajo tedioso y aburrido localizando ciertas partes de nuestro código. Usa palabras competas. Si usas nombres demasiado cortos, o solo con iniciales y letras no podrás buscarlos dentro del código, ya que o la búsqueda devuelve demasiados resultados o tienes que recordar exactamente el uso y orden de siglas usado.
Mira este ejemplo:
private genymdhms: Date;
private modymdhms: Date;
Compáralo con:
private genenerationTimestamp: Date;
private modificationTimestamp: Date;
Sinceridad
Por favor, no mintamos con nuestro código.
El nombre de una pieza de código tiene que hacer lo que promete, solo lo que promete, todo lo que promete. Si para nombrar un método correctamente y sin mentir necesitas añadir «Y» o «And» probablemente tienes que empezar a leer sobre el principio de responsabilidad única.
public class FicheroNoEnviadosBusTasklet
protected String getDatos(RegistroOutbound registroOutbound, Integer count) {
StringBuilder datos = new StringBuilder();
datos.append(escribirRegistro(registroOutbound, count));
List<Integer> numeroMensajes = new ArrayList<>();
if (registroOutbound.getIdMensaje() != null) {
numeroMensajes.add(registroOutbound.getIdMensaje());
mensajeService.actualizarRegistrosBus(numeroMensajes, Instant.now(), 0);
}
return datos.toString();
}
}
¿Te chirría algo en este código?
GetDatos -> ¿Qué datos recupera? ¿Se suele recuperar algo que no sean datos?
Podríamos poner otros nombres igual de útiles. Por ejemplo: GetInformacion o GetAlgo
Se llama getDatos, pero encima actualiza registros en otro sistema.
Evita codificaciones
Algún técnico modernillo me dirá que decir que nos olvidemos de la notación húngara no hace falta porque ya nadie se acuerda. Pero sí, todavía se usa. Así que por favor, olvídate de ella.
Con los IDEs actuales no necesitamos codificaciones extras para nombrar variables. Aunque el tipo de una variable cambie, probablemente no cambie su nombre. Y si su nombre no cambia no solo es inútil, sino que encima miente.
Ya nos llega con aprender lenguajes y frameworks para tener que aprender codificaciones que no aportan nada.
Ejemplo: numCoursesInt
Es obvio que el número de cursos es un número entero. Ese Int no aporta nada para entender el código.
Uso de convenciones
Aunque en un primer momento dijimos que nuestro código debe poder ser leído por cualquier persona. Realmente eso es una exageración porque nuestros lectores en el 95% de los casos serán programadores o aspirantes a ello. Por lo tanto, debemos usar convenciones ya sean genéricas o específicas del lenguaje (estándares). Por ejemplo, si en un lenguaje se suele usar camelCase, pues usémoslo.
Es cierto que en ocasiones estas convenciones pueden parecer que van en contra del código limpio, pero si son tan estándares que todos lo leemos mejor así sí estamos ayudando a la interpretación del código. Como muestra un botón:
for (var i = 0; i < courses.length; i++) {
/* */
}
Es cierto que una variable i en principio no nos dice nada, pero todos los programadores usamos i como contador dentro de un bucle, por lo tanto sí nos da la información suficiente e incluso nos es ya más fácil leer este código así a que si en lugar de i le ponemos a la variable «contador».
Una palaba por concepto
Para conceptos comunes en nuestra aplicación debemos, como regla básica de nomenclatura, utilizar siempre la misma palabra.
Si para recuperar una entidad usamos get, debemos usar siempre get en todos los métodos de recuperación: getCourses, getActiveCourses, getStudents. No debemos mezclar para el concepto recuperar varias palabras, por ejemplo: getCourses, fetchActiveCourses, obtieneStudents.
Esto facilita la búsqueda de código a una persona que no conoce bien el proyecto y evita duplicidades, si un programador ve que en otras clases se usa get y va a otra y ve que el método, por ejemplo, getClientesPotenciales no existe lo creará. Si resulta que ya existe un obtieneClientesPotenciales, resultará en que tienes dos métodos diferentes que hacen lo mismo.
Nomenclatura asociada a proyecto
Evitar usar el nombre del proyecto en nombres de clases, métodos, entidades, etc.
El proyecto puede cambiar de nombre, cambiar de cliente o generalizarse. Cuantas menos referencias al nombre del proyecto o cliente haya mejor. Sí ya sé que a priori suena raro que un proyecto cambie de nombres, pero yo personalmente ya he pasado por un proyecto que en menos de un año ha cambiado de nombre 4 veces.
Evita cosas que te pueden causar problemas y ningún beneficio.