Cambiando Legacy Code en GeneXus

Michael Feathers en su libro  “Working Effectively with Legacy Code” define codigo legado (legacy code) como todo  código que no tiene pruebas.
Code without tests is a bad code. It doesn’t matter how well written it is; how well structured it is; how well encapsulated it is.Without tests there is no way to tell if our code is getting better or worse.
Si nos guiamos por esa definición (que me gusta), puedo decir que la mayoria del codigo que tenemos es Legacy Code y que podemos mejorarlo de muchas formas.

Luego de algunos años de práctica, puedo decir que en mi experiencia siempre es mejor tener las pruebas lo  mas cerca del código que sea posible. pues agregan mas valor a mi código.  Por lo tanto, creo que es válida decir que tener Pruebas Unitarias (que prueban solo un objetos procedure o data provider) agrega mas valor que una prueba de integración (que prueban módulos o integración entre módulos) y a su vez agrega mas valor que pruebas de interfaz de usuario.

Prueba Unitaria > Prueba de Integración > Prueba de interfaz de usuario. 

En la version GeneXus 16, se pueden tener pruebas dentro del IDE de GeneXus, con lo cual podemos hacer pruebas unitarias de una forma mucho mas sencilla.

Voy a poner un ejemplo bien sencillo que usé la semana pasada.

Había un procedure que estaba contando registros que tenia problemas de performance.  No conocia dicho código y vi que hacia mas de 10 años que no se modificaba y por suerte era poco usado.

Revisando la navegacion, veía que estaba leyendo una tabla y por cada registro iba a la tabla hija y contaba los registros. 

Antes de cambiar nada, decidí crearle pruebas unitarias al mismo. Para esto ahora contamos con la posibilidad de usar la opción de "Create Unit Test" que crea automáticamente los objetos para probar en forma unitaria un procedure o un data provider.

Esto lo que crea es un objeto para probar, un data provider con los datos a probar y un SDT intermedio donde se guardaran los resultados de las corridas. 


Puse algunos valores en los filtros de la consulta, para poder probarlo. 
Aunque no conocía ni los datos ni el procedure, el tener varias corridas y sus resultados, da una mayor tranquilidad a la hora de cambiar código que no domino.  

Hice una corrida,vi cuantos registros dieron esos resultados y los puse en él los resultados esperados de 53 ,4 y 0.

Hice otra ejecución de la prueba, y hasta que todo salio verde y me permite ver que las 3 corridas, estan demorando mas de 4 segundos. Las tablas tiene varios millones de registros cada una, por lo que no es trivial saber si ese tiempo es mejorable.




Revisando el código y las navegaciones, se puede ver que el for each que está dentro puede eliminarse y es muy posible que esto mejore la performance, pues se tiene


 Sin conocer el código, es difícil saber si ese cambio tiene efectos en su funcionamiento. Hago una ejecución de las pruebas luego de cada cambio.

Evolución de los cambios de código. 


Original:


Saco el Order y el for each del medio


 Ejecuto la prueba unitaria y veo que no rompi nada y ademas mejoró el tiempo de ejecución.


Cambio los OR por WHEN



Vuelvo a ejecutar la prueba y veo que mejora aún más el tiempo de ejecución.



Para mejorar un poco y hacer mas entendible el código (y porque además la condición de filtrado se podia usar también en una pantalla web y una generación de Excel), decidi usar la extension LSI Extensiones, para extraer las condiciones y crear un Data Selector.


El Data Selector creado puede ser utilizado en otros objetos de la KB.

Y el código queda

Conclusiones. 

Con pequeños pasos, logramos mejorar la performance de 4 segundos a menos de 0.8 segundos. Además se realiza solo una consulta a la base de datos para hacer el COUNT()  en vez de miles como se hacían antes.

El tener las pruebas unitarias, permite hacer el cambio con tranquilidad, asegurando que al menos en los casos que se prueban los resultados sean los mismos.

Ademas, si las pruebas unitarias están bien diseñadas y el estado de la base de datos es uniforme en datos para todo el grupo de trabajo, se puede subir estas pruebas al server para poder usarla cada vez que necesitemos hacer un cambio.

Las pruebas unitarias que vienen integradas con GeneXus van a mejorar mucho tanto la calidad de código, como el diseño pues naturalmente todo queda mas facil de probar y también de modularizar.


Comentarios

Entradas más populares de este blog

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Aplicación monolítica o distribuida?

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.