Cómo Identificar Código que Apesta (Code Smell)

¿Eres capás de reconocer cuando tu código, o el de otros, está pobremente escrito o tiene mala calidad o está mal organizado? ¿Crees haber desarrollado el “olfato” de programador necesario para saber intuitivamente cuando debes refactorizar tu código? Continúa leyendo y te daremos algunas pistas para desarrollar ese “olfato”.

Todo programador con determinada experiencia, va creando un especial sentido del olfato que le permite saber intuitivamente cuándo el código que tiene en frete “apesta” (code smell), es decir, es un código de mala calidad, pobremente escrito, que aunque funciona, puede dar muchos dolores de cabeza en el futuro. La llamada hediondez del código (code smell en inglés, o también conocido por código que huele o apesta) es cualquier síntoma en el código fuente de un programa que posiblemente indica un problema más profundo. Las hediondeces del código usualmente no son bugs o errores concretos de programación y en realidad no impiden que el programa funcione correctamente. En cambio, indican deficiencias en el diseño que puede ralentizar el desarrollo o aumentan el riesgo de errores o fallos. Ante una situación como esta, es necesario refactorizar el código para hacerlo más claro, legible, mantenible, eficiente y menos propenso a errores.

Ahora bien, si eres un programador novel o quizás autodidacta, desarrollar la habilidad para identificar el código que “apesta” puede tomarte algunos años de aprendizaje y práctica, por esa razón, te ofrecemos un breve resumen de algunas señales o indicios que te podrían ayudar a identificar un código que “apesta”.

Señales de Código que Apesta y Cómo Eliminarlas

  • El primero y más claro indicio de código que apesta es el código duplicado: que se resume en que existe código idéntico o muy similar en más de una ubicación. Esto viola un principio elemental de la programación, el principio de no repetirse o DRY del inglés: Don’t Repeat Yourself. Siempre que te sorprendas usando el copy/paste piénsalo mejor y desiste, te puedo asegurar que hay mejore opciones. El código repetido es la odisea de los mantenedores del código, que se verán en apuros cada vez que tengan que actualizar código idéntico o muy similar en varios lugares a riesgo de olvidarse de algo. Cuando te encuentres en esta situación, simplemente pon el código repetitivo en una función y llámala desde donde lo necesites
  • Función o Método grande: un método o función que ha crecido hasta hacerse demasiado grande, tampoco es buena señal. Una función demasiado grande es difícil de procesar y de comprender. Si detectas una función con muchas líneas de código, revísala y divídela en funciones más pequeñas con un único y bien definido fin y asegúrate de que los nombre que emplees sean coherentes con ese fin

  • Clase grande: una clase que ha crecido hasta hacerse demasiado grande también puede ser un problema. Devídela en clases más pequeñas que igualmente se ocupen de una sola tarea. Aplica consistentemente el principio de Responsabilidad Única (Single Responsibility)

  • Demasiados parámetros en la definición de una función o método: una larga lista de parámetros empeora la legibilidad y la calidad del código. Mientras más parámetros tenga un método, más complejo y largo será, por tanto es saludable reescribirlo y mejorarlo. Quizás puedas agrupar algunos de esos parámetros en algún objeto y así reducir su número

  • Bloques condicionales muy largos y complejos: se cuidadoso con los bloques condicionales muy complejos, particularmente con aquellos que ganan en complejidad sistemáticamente. En estos casos es aconsejable usar algún patrón de diseño, como: Strategy, Decorator o State

  • Malos comentarios: un comentario que explique el “qué” en lugar del “porqué” es, casi siempre, un comentario innecesario. Asegurate de que tu código sea lo suficientemente claro y explícito como para que de su simple lectura, se pueda comprender el “qué”. Usa los comentarios solo para el “porqué” y mantenlos actualizados cuando modifiques el código. Un exceso de comentarios en un método o función puede significar que estás abarcando demasiadas cosas

  • Envidia de características: métodos de una clase que usan excesivamente métodos de otra clase. Si detectas esto, puede ser que necesites mover algunos métodos y ponerlos en la clase donde pertenecen realmente; usa la lógica y el sentido común. Una pista, revisa si los parámetros o las variables locales que estás empleando son atributos de alguna otra clase, si es así, esta puede ser la clase correcta para tu método

  • Herencia rechazada: es cuando una clase sobrescribe los métodos de su clase base, de tal manera que el contrato de la clase base no es honrado por la clase derivada. Este es el Principio de Sustitución de Liskov, según el cual una clase derivada o subclase debe poder sustituir exitosamente (honrar el contrato) a su clase base en cualquier momento. Si te encuentras en una situación como esta, es posible que debas revisar/modificar tu jerarquía de clases. Recuerda que lo fundamental en el empleo de la herencia es la reutilización del código

  • Clase perezosa: una clase que hace muy poco. En este caso será necesario que revises la asignación de responsabilidades entre tus clases, quizás esta “clase perezosa” debe formar parte de alguna otra clase en tu jerarquía. Busca aquella clase que hace más uso de tu “clase perezosa”, es muy posible que puedas integrarlas a ambas en una única clase mucho más completa y consistente

  • Clase dato: son aquellas clases que escribimos solo con el fin de contener datos. En este caso recuerda que las clases fueron ideadas para contener datos y comportamientos (métodos), si falta alguno de ellos, pues puede que no necesites una clase propiamente dicha

  • Exposición indecente: es cuando una clase expone o hace públicos muchos de sus atributos y métodos. Diseña una adecuada y mínima (que comparta solo lo estrictamente necesario) interfaz pública para tus clases, lo demás, hazlo privado. Si no estás seguro de si un atributo debe ser público o privado, hazlo privado en principio, ya podrás hacerlo público más adelante si es que realmente lo necesitas

  • Clases que cambian en cascada: si cuando introduces cambios en una clase, debes a la vez cambiar otras, es aconsejable refactorizar de manera tal que los cambios solo afecte a una clase a la vez

  • Clase intermediaria: si una clase delega todas sus funcionalidades a otras clases, para qué existe realmente? Deshazte de los intermediarios siempre que sea posible, pero ojo, hay patrones de diseño donde puedes encontrar clases como estas que están ahí de manera intencional: Adapter, Facade

  • Complejidad artificiosa: refleja el uso forzado de patrones de diseño demasiado complicados, donde uno más simple sería suficiente. Esto sucede muchas veces cuando tratamos de encontrar la “súper solución” a un problema de poca complejidad. Recuerda que la mejor solución casi siempre es la más simple. Si el problema a que te enfrentas es poco complejo, trata de hallar una solución sencilla, sin demasiada parafernalia, recuerda que: “Simple es mejor que complejo”. Lo patrones de diseño son muy, muy importantes, pero a veces hacen más complejo el diseño sin necesidad

  • Generalidad excesiva: escribe código que resuelva tus problemas actuales, el código para resolver problemas futuros, déjalo para el futuro, cuando esos problemas se hagan reales. No pienses en agregar nuevas funcionalidades que no necesitas, solo por si acaso mañana son necesarias, porque probablemente no las necesitarás

  • Identificadores excesivamente largos: el uso de convenciones de nombres para proporcionar desambiguación que debería estar implícita en la arquitectura de software es otro problema que puedes encontrar a menudo. Busca nombre consistentes y cortos para tus variables, funciones, métodos, constantes, clases, módulos y paquetes; nombres que expresen la función que cumplen dentro del código

  • Identificadores excesivamente cortos y poco comunicativos: el nombre de una variable debe reflejar su función, a menos que sea obvio. Esta es la otra cara del punto anterior y el consejo es el mismo

  • Tipos embebidos en los identificadores: evita incluir el tipo de dato en los nombre de variables, funciones o métodos, esto, además de ser redundante, te obliga a cambiar el nombre si por alguna razón te vez obligado a cambiar el tipo

  • Código muerto: es aquel código que en algún momento escribimos y que debido a modificaciones posteriores ha dejado de cumplir su función dentro de nuestra base de código, es decir, es código que no se ejecuta más y por tanto, resulta innecesario, así que la solución es eliminarlo

  • Uso excesivo de literales: estos deben codificarse como constantes con nombre, para mejorar la legibilidad y para evitar errores de programación. Adicionalmente, los literales pueden y deben ser externalizados en archivos/scripts de recursos cuando sea posible, para facilitar la localización del software si se pretende implementar en diferentes regiones.

Lecturas Recomendadas

Para profundizar en el estudio de estos temas recomendamos la lectura del Capítulo . “Bad Smells in Code” del libro “Refactoring: Improving the Design of Existing Code.”, por Martin Fowler, así como el Blog Post: “Code Smells” publicado en CODING HORRORS por Jeff Atwood.

Por el momento esto es todo, si este artículo te resultó interesante y/o útil, compártelo para que otros también puedan acceder a él. Déjanos tus comentarios y podremos mejorar y ampliar nuestros contenidos.

Gracias de antemano,

lpozo

2 comentarios

    • dcruz en 22 agosto, 2018 a las 5:40 pm

    Jjajaja, de todo eso está lleno mi código pobremente escrito 😀

    1. Bueno, entonces ya sabes cómo puedes arreglarlo,
      happy coding,
      lpozo

Los comentarios han sido desactivados.