Foto de Nicola Barts

Después de trabajar con varios equipos, a menudo se repiten decisiones que conducen a soluciones subóptimas. Aquí tienes algunos ejemplos:

  • Idealmente, deberíamos hacerlo de otra manera, pero «no podemos debido a los plazos establecidos». Por ejemplo, utilizar un algoritmo ineficiente para procesar grandes cantidades de datos, sabiendo que será necesario optimizarlo más adelante.
  • De todas formas, «en algún momento tendremos que reescribir la solución, así que sigamos adelante y no interrumpamos el proceso con refactorizaciones o mejoras por ahora».
  • Ignorar la calidad del código: en algunos casos, se da prioridad a la entrega rápida del software sin considerar la calidad del código. Esto puede resultar en un código desordenado, difícil de entender y mantener. Y quien siempre se lleva la peor parte de esta decisión es omitir la documentación adecuada, no seguir las convenciones de nomenclatura o no realizar pruebas exhaustivas.

Entiendo que este cambio será difícil, pero alguien debe liderarlo. Ya sea como arquitecto de software, líder de equipo, desarrollador senior o aspirante a uno de estos roles, es importante que te tomes en serio la calidad. Evita los atajos, no acumules deuda técnica y no permitas que surjan problemas que compliquen la arquitectura, la implementación o cualquier otra fase de la entrega del software.

Y como siempre digo, la vida no es solo blanco o negro, hay matices de grises: si es necesario tomar atajos, documenta de inmediato las tareas técnicas compensatorias y asegúrate de incorporarlas pronto para eliminar esa pequeña concesión de deuda técnica.

A continuación, te mostraré un ejemplo en C# que ilustra qué es la deuda técnica y cómo se puede compensar mediante refactorización:

Supongamos que tienes una clase en C# que maneja el registro de usuarios en un sistema:

public class UserManager
{
    public void RegisterUser(string username, string password)
    {
        // Todo el código necesario para registrar al usuario en la base de datos
        // ...
        //  Todo el código necesario para enviar un correo de confirmación al usuario
        // ...
        //  Todo el código necesario para recuperar la contraseña
        // ...
    }
}

En este caso, podemos identificar una deuda técnica. La clase UserManager está sobrecargada con múltiples responsabilidades, ya que no solo se encarga del registro de usuarios, sino también del envío de correos de confirmación y de realizar acciones adicionales como la recuperación de la contraseña. Esto viola el principio de responsabilidad única.

Para compensar esta deuda técnica, podemos aplicar refactorización extrayendo estas responsabilidades en clases separadas y, de esta manera, mejorar la claridad y mantenibilidad del código:

public class UserManager
{
    private readonly IUserRepository userRepository;
    private readonly IEmailService emailService;
    private readonly IActionService actionService;

    public UserManager(IUserRepository userRepository, IEmailService emailService,
IActionService actionService)
    {
        this.userRepository = userRepository;
        this.emailService = emailService;
        this.actionService = actionService;
    }

    public void RegisterUser(string username, string password)
    {
        userRepository.RegisterUser(username, password);
        emailService.SendConfirmationEmail(username);
        actionService.PerformAdditionalAction();
    }
}

En el ejemplo refactorizado, hemos creado interfaces y clases separadas para la gestión de usuarios, el servicio de correo electrónico y las acciones adicionales. Ahora, la clase UserManager se encarga solo del registro de usuarios y delega las responsabilidades correspondientes a las clases especializadas.

Este enfoque elimina la deuda técnica al seguir el principio de responsabilidad única y facilita el mantenimiento y la evolución del código a largo plazo.

Recuerda que esta en tu mano hacer las cosas bien, es una cuestión de filosofía de trabajo e interiorizarlo.

Otro día os contaré la frase de los test: «no tengo tiempo para programar los test», es decir que no pruebas ni de forma manual, por qué el tiempo que tardas en hacerlo a la vieja usanza es  casi lo mismo que tardas en programarlo en código wink