La importancia de programar bien

10 Oct 2024 10 min (0) Comentarios

¿Qué sucede cuando tenemos métodos o funciones que ejecutan acciones inesperadas? Lo más probable es que nuestro código explote, en este post, vamos a ver como evitar tener situaciones inesperadas. 

 

 

 

1 - El código de calidad existe

 

Como en estos casos nunca hay nada a ciencia cierta, pero sí puedo hablar desde la experiencia

 

Personalmente tengo una forma de programar que a no todo el mundo le agrada, ya que una de mis premisas es que mis aplicaciones nunca están en un estado invalido

Qué quiero decir con un estado invalido? Muy sencillo, que mientras el estado de mi programa sea correcto, el funcionamiento correcto del código está garantizado, de esta forma cuando el programa peta, lo más probable es que tenga un bug, o las asunciones que se han hecho son falsas. 

 

Cuando aplicas las herramientas y técnicas correctas al código, la calidad del mismo sube. Yo cuando digo que un código es de buena calidad, suelo fijarme principalmente en 3 aspectos:

 

1 - Que haga el trabajo que se supone que va a hacer.

2 - Que sea fácil de leer y entender.

3 - Que situaciones esperadas no produzcan situaciones inesperadas. 

 

Con estos 3 aspectos, puedes decir que es software de buena calidad, porque hace lo que se espera, lo hace bien y sin romperse. Adicionalmente y dependiendo de la situación puedo meter un cuarto punto que es, que lo hace de forma eficiente. Pero, no todas las aplicaciones necesitan este cuarto punto. 

 

Los puntos podrías pensar que son sentido común, pero no lo son tanto pues el segundo se consigue principalmente con años de experiencia y revisiones de código por parte de otros desarrolladores. 

 

Pero el tercero, no solo influyen los años de experiencia y la revisión de código por parte de otros compañeros, sino que también influye el lenguaje que estemos utilizando y las herramientas que dicho lenguaje dispone que nos puedan facilitar la vida. 

 

 

2 - Anticipando lo Inesperado: La Clave de un Código Predecible

 

Desafortunadamente este post viene dado principalmente para desahogarme de situaciones que he vivido recientemente. Y así, de paso, explico como hacer las cosas bien.

 

A lo largo de este post, nos vamos a centrar principalmente en el tercer punto que he mencionado antes, que situaciones esperadas no produzcan situaciones inesperadas. Para ello lo que tenemos que hacer es convertir situaciones inesperadas en situaciones esperadas. 

Parece muy sencillo, pero no lo es. Cada día lo es más ya que tenemos herramientas como el análisis estático de código , en prácticamente cada lenguaje. El tema de usar tipos también ayuda (si, soy partidario de usar tipos), y luego las herramientas de cada lenguaje, ya bien sea el compilador  de C# o EsLint (ya se que es un anaḷísis de código estático) en JS, o en ruby podemos usar….. Nada, en ruby le podemos poner tipos pero nada más :D . 

 

La idea es sencilla, analizar el código para evitar tener situaciones inesperadas. 

En este post, todo el código es C# y en mi opinión, con lo que yo he trabajado es de lo mejor para mantener un código que garantice estados constantes y predecibles. 

 

Nota: El término en inglés es “invariance de un software” pero no se como traducirlo, y veo más sencillo decir lo que es.  

 

Luego veremos como el siguiente código, si tienes todo bien configurado y sabes lo que estás haciendo no es posible en C#, desafortunadamente hay gente que no mira mucho lo que hace o no tiene tiempo para pararse a pensar y puede contener errores, y si no tenemos algo que nos diga que lo estamos haciendo mal, podemos acabar con una situación inesperada:

 

Vamos a suponer que recibimos de una API o de la base de datos, o de cualquier sitio en verdad, un objeto vehículo, que entre otras propiedades contiene la velocidad en kilómetros. 

 

Y cómo nuestros usuarios están en Estados Unidos simplemente lo convertimos a millas. 

public class Vehicle
{
    public decimal SpeedInKm { get; set; }
}

decimal CalculateMiles(Vehicle vehicle)
{
    return vehicle.SpeedInKm * 0.62137119223733m;
}

A primera vista este código esta bien, pasamos el vehículo, cogemos las millas y hacemos el cálculo.

 

Pero haaaaay amigo, ¿qué pasa si el vehículo o la velocidad es null? O es undefined como en JS? 

Que llegados a este punto, nuestro código va a petar y va a saltar una excepción. 

 

 

3 - Utiliza código nulable

 

La primera de las acciones que podemos cubrir es tener el código nullable. Debemos tanto habilitar nullable dentro del csproj, como en nuestra definición del tipo podemos especificar que el valor de la velocidad puede ser null utilizando el carácter ?.

public class Vehicle
{
    public decimal? SpeedInKm { get; set; }
}

//En el csproj
<PropertyGroup>
    <Nullable>enable</Nullable>
</PropertyGroup>

De esa forma, el compilador nos va a dar un error y tenemos que cambiar el resultado del método. 

decimal? CalculateMiles(Vehicle vehicle)
{
    return vehicle.SpeedInKm * 0.62137119223733m;
}

 

IMPORTANTE en C#, en las nuevas versiones no tenemos que cambiar el cuerpo del método, el compilador automáticamente añade un if a la hora de compilar el código, se puede observar si ves el código que se genera tras la compilación:

nullable IL rider

Pero, en otros lenguajes vas a tener que poner el IF de forma manual. Y por supuesto comprobar cada propiedad que puede ser nulo en cada punto que necesite ser utilizada.

 

Como nota adicional, en C# recomendaría habilitar nullable dentro del csproj en todos los proyectos que utilices. Ya que el tema de los nulos es un problema muy serio, especialmente en aplicaciones antiguas. 

 

 

4 - Las alertas son errores

 

Personalmente, llevo años predicando que los warnings que nos da nuestro código son errores. Esto quiere decir que todas esas alertas amarillas que vemos cuando hacemos la build no debemos ignorarlas sino que debemos arreglarlas. 

 

Por qué? Porque cada alerta que ignoramos, es un punto donde nuestra aplicación va a petar, no que puede petar, sino que petara. La pregunta no es si lo hará, sino cuando. 

 

Volvamos a poner el código como al principio: 

public class Vehicle
{
    public decimal SpeedInKm { get; set; }
}

decimal CalculateMiles(Vehicle vehicle)
{
    return vehicle.SpeedInKm * 0.62137119223733m;
}

Ahora en nuestro caso de uso vamos a pasarle el objeto vehículo, pero nulo:

CalculateMiles(null);

Aquí, qué crees que pasa? 

 

Pues tenemos dos situaciones

1 - Si tenemos nullable deshabilitado, nada, no pasa nada. 

2 - Si tenemos nullable habilitado nos mostrará un warning. “Possible null reference”.

 

En ambos casos el código compila y lo podemos poner en producción, por supuesto si sucede que un valor nulo entra en nuestro método CalculateMiles el código petara y saltará una excepción.

 

En C# podemos habilitar en el csproj la propiedad TreatWarningsAsErrors lo cual convertirá ese warning en un error y no nos permitirá compilar el programa.

<PropertyGroup>
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

Así que lo que tenemos que hacer es arreglar el problema para que no salte un error.

 

Que en nuestro caso es especificar tanto el parámetro de entrada como el de salida como nullable y verificar si el vehículo es null antes de acceder a la velocidad.

decimal? CalculateMiles(Vehicle? vehicle)
{
    return vehicle?.SpeedInKm * 0.62137119223733m;
}

Y ahora ya se compila y el código está protegido de romperse. 

 

Una nota adicional con TreatWarningsAsErrors, lo más probable es que si lo indicas en un código que lleva funcionando varios años tengas miles de alertas, así que lo que yo recomiendo es habilitarlo en proyectos nuevos. 

 

 

5 - Arregla los problemas de raíz

 

Especificar que el vehículo puede ser nulo e ir indicando o comprobando si es nulo en cada paso puede no ser lo correcto.

 

Me explico, en este caso, lo que estamos haciendo es si el vehículo es nulo, la velocidad es nula, pero si el vehículo es nulo, para que vamos a comprobar la velocidad, o cualquier otra propiedad que exista en ese objeto, ya sabemos que es nulo, no tiene sentido. 

 

Así que en este caso en concreto lo que deberíamos hacer es un exit early, lo que quiere decir, no procesar nada que venga después de validar que es nulo; En este ejemplo sencillo es simplemente un if. Te darás cuenta de que si pones un if para salir del código puedes modificar el método para que no acepte nulos: 

Vehicle? currentVehicle = externalApi.GetVehicle();

if (currentVehicle is null)
{
    return;
}

CalculateMiles(currentVehicle);

decimal CalculateMiles(Vehicle vehicle)
{
    return vehicle.SpeedInKm * 0.62137119223733m;
}

Lo que significa que vamos a garantizar un estado válido del sistema siendo muy beneficial a largo plazo. 

 

 

 

Conclusión

 

El ejemplo que hemos visto es un ejemplo sencillo pero que espero te haya permitido ver las diferencias entre hacer las cosas bien y no.  

 

El uso correcto de tipos no solo va a mejorar tu código a la hora de estar siendo ejecutado en producción, sino que cuando alguien tiene que hacer un cambio sabe lo que puede suceder.

Si por ejemplo tienes un método que recibe una lista y la devuelve filtrada, por norma general tu esperas que esa lista te la devuelva, pero puede darse el caso de, que si no hay ningún elemento la lista sea devuelta como null. 

Por qué alguien haría eso? Pues ni idea, pero este caso fue el que hizo que escribiera este post. Por cierto null no es lo mismo que string vacío, ni que una lista vacía entre otros. 

 

Para finalizar, si mezclas lo que has visto hoy en este post junto a lo que vimos sobre código inmutable conseguirás un código consistente, y de buena calidad. 

 

Cada mejora que le pongas al código es tiempo que ahorras en el futuro, pero no solo tú, sino tu equipo, y cada bug que no llega a producción es una situación donde el cliente está contento con el producto, que al final son quienes pagan las facturas. 

 


Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2024 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café