miércoles, 3 de marzo de 2010

En busca de código repetido

Uno de los problemas que más me quitan el sueño en los últimos tiempos, es el código repetido.

¿Por qué tener código repetido es un problema? La respuesta creo que es conocida por todos. El problema es que eventualmente las distintas versiones que se tienen se terminan desfazando, y empiezan a aparecer errores por tocar solo una. Además después de un cierto tiempo, es muy difícil saber cual de todas las versiones es la correcta, porque en general los cambios no se hacen en todas parejo.

Entonces, ¿cuanto código repetido es aceptable? Esta pregunta indudablemente es más abierta, y pueden haber diferentes criterios. ¿Una línea de código duplicada está bien?

Por ejemplo, si tengo el siguiente código (GeneXus 9.0), ¿está bien?:
if DocImpres = 'S'
    call(WGLLogDe,&msg1, &msg2, 'OC', 'CMANUDOCOC', &GlLogRef, '', &confirmo)
    if &confirmo = 'N'
        Call(PGLog, &msg1, &msg2, 'OC', 'CMANUDOCOC', &GlLogRef, '', &PedirDet)
    endif
else
    Call(PGLog, &msg1, &msg2, 'OC', 'CMANUDOCOC', &GlLogRef, '', &PedirDet)
endif
Nótese que los dos call al procedure GLog son idénticos... Además, los literales 'OC' y 'CMANUDOCOC' se usan varias veces en el código...

Haciendo algunos cambios al programa para eliminar estas repeticiones, llegamos a algo así:
&logGrabado = 'N'
&GLLogSis = 'OC'
&TpoModId = 'CMANUDOCOC'
if DocImpres = 'S'
    &logGrabado = udp(WGLLogDe, &msg1, &msg2, &GLLogSis, &TpoModId, &GlLogRef, '')
endif
if &logGrabado = 'N'
    Call(PGLog, &msg1, &msg2, &GLLogSis, &TpoModId, &GlLogRef, '', '')
endif
¿Es mejor que el código anterior? Yo creo que sí, pero seguramente para un caso tan simple haya discrepancias...

Pienso que cuando se trata de 2 o más líneas de código es más fácil estar de acuerdo que no es bueno tener dicho código repetido.

Ahora, el problema, es como detectar esta situación.

Cuando el código repetido está dentro del mismo objeto, entonces es bastante fácil de detectar. No es trivial cuando se tienen programas grandes (más de 500 líneas por ejemplo), pero es posible.

El mayor problema es cuando el código repetido está en varios objetos. En esos casos no es para nada fácil de detectar.

Pienso que se podría automatizar, aunque no parece ser trivial. Algunas heurísticas que se me ocurre se podrían utilizar:
  • Analizar las navegaciones de los objetos, para ver si hay navegaciones iguales. Esos objetos son candidatos a analizar. No necesariamente quiere decir que se pueda mejorar el código, pero sí vale la pena revisarlo.
  • En general cuando se da que hay código repetido, el mismo está en el entorno de la llamada a un objeto, o en torno al uso de un determinado atributo. Por ejemplo, tengo un procedimiento que me devuelve un valor, y luego hago algo con dicho valor, pero en varios lugares lo que hago es exáctamente lo mismo...
¿Alguien ha tenido la misma inquietud? ¿Existe alguna herramienta que me permita buscar en una KB GeneXus los posibles problemas de código repetido?

6 comentarios:

  1. Hola Marcos.

    Casualmente hace años estoy en la misma inquietud.

    En una época hice algunas implementaciones de prueba pero no logré nada muy bueno (hay que lograr implementar un buen parser GeneXus para luego poder crear una buena abstracción del mismo para lograr analizar patrones para encontrar clones).

    Es un tema muy complejo, espero lograr avanzar en el correr del año con el proyecto Genoma como para poder incluir luego de ese proyecto algo de detección de clones al mismo.

    Existe mucha literatura al respecto, tengo un sitio en donde se encuentra un gran compilado sobre el tema por si alguien quiere ponerse un poco más al tanto
    http://twitter.com/3dgiordano/status/9438337663

    Siempre existe la posibilidad de irse a la solución más simple, y experimentar... si alguien tiene tiempo y quiere hacerlo, sugiero usar alguna implementación como SIMIAN que posee soporte de búsqueda de clones en texto plano, volcando los códigos fuente de pgms GX a texto plano podrían encontrar algo http://www.redhillconsulting.com.au/products/simian/index.html

    (En el link de recursos hay una sección Tools en donde existen otras implementaciones, SIMIAN tiene la particularidad de tener una implementación .NET para quienes quieran integrarlo con extensiones GX).

    ResponderEliminar
  2. David, gracias por la información.

    Por el lado de Simian capaz que se puede hacer algo... Hay que ver si la comparación de texto plano tiene alguna "inteligencia" como no considerar los espacios en blanco y demás.

    Voy a ver si le puedo dedicar un rato.

    ResponderEliminar
  3. El tema suena "divertido". Varias puntas: codigo repetido, legibilidad, modularización,re-uso, en fin, creo que muchas puntas.

    Una idea capaz que es super simple pero que capaz aporta ¿que tal armar Data Selectors a partir de las navegaciones?

    Lo veo para el lado de KBDoctor es decir, analizar el código y sugerir los cambios. Ya que lo haga una herramienta solito se complica

    Nada, una idea "chota" capaz

    ResponderEliminar
  4. Marcos:
    Me gusta que hayas planteado este tema en publico. Es una area de oportunidad de mejora del software desarrollado, pues siempre que hay codigo repetido, hay posibilidades de refactoring o de reutilizacion.

    Una vez encontrado el codigo repetido, puede merecer la pena la creacion de una herramienta (gxpatterns?) para generar esos objetos, o partes de objetos, o como dice GusCarr, hacer el refactoring para mejorar la calidad del codigo.

    En base a lo que mando David, estuve probando con simian, con los fuentes generados (en mi caso en C#) y aparecieron varias cosas interesantes que voy seguir investigando.

    La contra de trabajar con el codigo generado, es que es mas trabajoso identificar cual es el codigo que se identifico como repetido en el codigo Genexus, pero el que identifique los objetos que lo contiene, ya es un gran paso.

    Tambien hay gran cantidad de codigo, que Genexus genera en forma muy similar en todos los objetos, por lo que hay muchos "falsos positivos", que hay que eliminar.

    La ventaja que tiene el enfoque es que las herramientas funcionan tal como estan, sin tener que hacer ninguna modificacion.

    ResponderEliminar
  5. Enrique : Probaron el simian con los xmls de navegación en lugar del fuente generado? Falsos positivos vas a tener igual, pero deberían ser menos y los casos más claros de identificar.

    ResponderEliminar
  6. Pablo:
    No probe usar el simian con los archivos de la navegacion.

    Cuando lo use con muchos archivos, no logre que terminara en un tiempo razonable.

    Tenemos un utilitario que genera un distribute por cada objeto (KBCVSP - Control de versiones sin pretensiones) y vamos a comparar estos fuentes primero.

    Hay varios archivos que se pueden comparar para buscar duplicados, que pueden ser:

    Archivos de navegacion = Para encontrar navegaciones parecidas, pueden servir para hacer refactoring y poner Data Selectors.

    Archivos xpz = Para encontrar codigos parecidos.

    ResponderEliminar