archivo

Archivo de la etiqueta: Sonar

Cuando se habla de programación orientada a objetos se dice que un buen diseño es aquel que consigue una alta cohesión y un bajo acoplamiento. Como es lógico, estoy de acuerdo, no obstante de los dos conceptos la cohesión, tal vez por entenderse menos su utilidad, es el hermano pobre, es decir, si se tiene en cuenta alguno de los dos (algo que desgraciadamente no ocurre demasiado, ya que no se suele prestar suficiente atención a la calidad del diseño y de la codificación) se suele echar más en cuenta al acoplamiento, además de ser más inteligible por ser más evidentes los problemas existentes cuando el acoplamiento es alto.

La cohesión mide la relación entre los distintos elementos que componen una clase: atributos y métodos. Una baja cohesión da lugar a las denominadas clases tutti frutti, clases que pretenden hacer muchas cosas y diferentes.

Cuando se tienen clases con baja cohesión implica principalmente:

– Falta de comprensibilidad: La clase no sirve a un aspecto concreto, sino a muchos. Cuando pensamos en un objeto lo hacemos entendiendo que sus partes son necesarias para que exista su todo (es decir, las partes por sí solas tienen poca utilidad y es mediante su relación cuando dan entidad al objeto y hacen que en su conjunto (el todo) valga más que la suma de las partes por separado). Cuando tenemos clases cuyos elementos forman un todo, pero ese todo no es identificable con nada, nos encontraremos probablemente con clases que tienen una baja cohesión, con métodos que no guardarán relación con otros muchos de la misma y que servirán a propósitos distintos. Habrá que tener bien documentada la clase para mantenerla posteriormente, porque si la coge un equipo de desarrollo distinto o ha pasado mucho tiempo desde que se implementó resultará complicado entender para qué sirve y qué utilidad tienen muchos métodos y atributos de la misma.

– Dificulta la mantenibilidad: Además de por la disminución de la comprensibilidad de la clase, al tener más código del que debería tener, se incrementará en consecuencia la complejidad ciclomática de la misma y por tanto será más costoso realizar cambios.

– Dificulta la reutilización: Cuando tenemos una baja cohesión quiere decir que hemos modelado una realidad rara, un híbrido de muchas cosas. Se ha implementado una clase para algo muy específico y lo concreto pocas veces es reutilizable.

– Suelen tener un acoplamiento alto (en comparación a una cohesión baja): Tiene su lógica ya que la clase tendrá más métodos que si los diferentes objetos reales que están dentro de la misma se hubieran encapsulado en sus correspondientes clases. Más métodos implican por regla general llamadas a más métodos de clases y paquetes externos y en consecuencia un mayor acoplamiento.

– Tendentes a sufrir modificaciones: Como estas clases son híbridos de varias la probabilidad de tener que sufrir operaciones de mantenimiento crece (tanto como clases distintas pudieran contener en su interior), lo cual tiene su importancia sobre todo si tenemos en cuenta que este tipo de clases son más complicadas de mantener.

Una forma de medir la cohesión la tenemos con la métrica LCOM4, propuesta a partir de la métrica inicial LCOM1 de Chidamber y Kemerer. Esta métrica forma parte del conjunto de las mismas que calcula Sonar haciendo análisis estático de código.

Hace poco tiempo tuve una reunión con un proveedor para revisar, entre otras cosas, la calidad del software del producto que están desarrollando, mediante la realización de un análisis estático de código utilizando Sonar.

La verdad es que pese a que todavía queda más de la mitad de la construcción las métricas eran bastante decentes pese a que se reutilizaba dentro de la aplicación un software desarrollado hace años y que tenía algunas carencias en este sentido.

¿Cuáles han podido ser las claves?

– Que el proveedor sepa que el producto va a pasar por Sonar y que el cliente sabe interpretarlo ya es de por sí un elemento motivador importante para esmerarse. Además conoce qué reglas aplicamos y qué plugins tenemos instalados, por lo que no hay trampa ni cartón y puede reproducir en su entorno de desarrollo los resultados que posteriormente vamos a obtener nosotros.

– El proveedor utiliza un entorno de integración continua integrado con Sonar, por lo que cada cierto tiempo obtiene las métricas relacionadas con la calidad del software en cada desarrollo. Eso en sí mismo no supone nada, ya que lo importante no es solo tener un proceso que te permita obtener las métricas sino que se revise, interprete adecuadamente y se aleccione al equipo de desarrollo cuando se empiezan a detectar desviaciones en la calidad del software. Cuanto antes se detecten los problemas menos esfuerzo y por tanto menos coste tendrá solucionarlo. En este caso el responsable del equipo de trabajo revisa semanalmente Sonar y advierte de las carencias que se van encontrando.

El uso de Sonar detecta y apunta las problemáticas, pero no todas se solucionan fácilmente, ya que si se parte de un diseño de la aplicación que no es bueno probablemente el resultado no será bueno, es decir, la calidad del software no empieza cuando se codifica sino mucho antes y eso hay que tenerlo en cuenta. En cualquier caso, integrar el uso de Sonar dentro del proceso de desarrollo me parece un acierto total ya que esperar al final para obtener las métricas es peligroso, ya que aunque se puedan mejorar cosas siempre serán muchas menos (y costará más esfuerzo) que si las mejoras se han ido realizando mientras se realizaba la construcción.

Hace poco me comentaba un proveedor que los resultados obtenidos con Sonar tras realizar el análisis estático del código de las aplicaciones que desarrollaban en su empresa estaban muy condicionados al framework de desarrollo y a las soluciones de generación de código que se utilizaban.

Yo le comenté que no dudaba un ápice, al contrario, del framework de desarrollo utilizado, ya que no solo respetaba las reglas de diseño y codifición establecidas en nuestro libro blanco, sino que además, facilitaba el desacoplamiento de la aplicación a nivel vertical por la existencia de diferentes capas con responsabilidades concretas y además permitía disminuir tiempos de desarrollo. Como todo en la vida es mejorable, pero el nivel que tenía era bastante alto.

Tampoco dudaba lo más mínimo de las soluciones de generación de código, es más me parece muy apropiado que se utilicen este tipo de estrategias ya que además de dar uniformidad a los desarrollos (cogidos de la mano del framework) permiten ahorrar costes de desarrollo.

Calidad y reducción de costes de desarrollo, van unidos de la mano de los frameworks y de los generadores de código. Evidentemente no aseguran nada, ni calidad ni reducción de costes, pero son la base para sustentar desarrollos de calidad a menor coste.

Lo que venía a decirle al proveedor que independientemente de que el framework y la generación de código condicionen una determinada solución, siempre existe el factor humano, ya que hay clases que se tendrán que codificar a mano (incluso subsistemas de negocio completos) y por supuesto la posibilidad de refactorizar determinadas clases determinadas por el framework, sobre todo cuando tengan un alto nivel de acoplamiento.

Es decir siempre es posible mejorar la calidad del código para mejorar la mantenibilidad del sistema. ¿Qué supone un esfuerzo? Por supuesto que sí, pero la clave, como he comentado en tantas ocasiones no consiste en intentar llegar a la perfección, ya que además de no conseguirse, aproximarse a la idea que tengamos de perfección requerirá un esfuerzo (y unos costes) que no merecen, en absoluto, la pena. En estos casos, hay que centrarse en quick wins, centrarse en clases en las que se puede mejorar la codificación y/o bien es necesario realizar una refactorización y en donde estas modificaciones permitan mejorar la calidad global de la aplicación desde el punto del vista del mantenimiento.

Sonar es una herramienta muy útil para detectar este tipo de clases, aunque también sería posible detectar clases candidatas a un alto acoplamiento, antes de desarrollar, mediante la realización de diagramas de clases en los que se refleje las dependencias entre las mismas. Cada una de esas alternativas tiene sus ventajas e inconvenientes. La gran ventaja de Sonar es que te permite medir la calidad desde múltiples puntos de vista e indicarte de manera sencilla qué clases fallan en según qué métricas, su inconveniente es que ya has codificado y todos sabemos que cuánto más tarde se detecten los problemas en la construcción, más costoso resulta corregirlos.

En Sonar, a la hora de revisar el cumplimiento de reglas, una de las que aparece muy frecuentemente en el software que nos entregan es la regla PMD “Security – Array is stored directly” que se produce cuando uno de los parámetros que aparece en la definición de un método, de un constructor o de un setter (es bastante frecuente que cuando los accesores son autogenerados por el IDE o por generador de código se produzca este tipo de incumplimientos, salvo que se haya tenido en cuenta previamente) es un array y en el interior del método se realiza una asignación de dicho objeto a otro (que no es local) sin realizarse previamente una clonación (se tendría lo que algunos denominan acoplamiento entre objetos por compartir direcciones de memoria).

Si no clona el objeto resulta muy peligroso, ya que el número de objetos que apuntan a las mismas direcciones de memoria aumenta y en consecuencia se incrementa la posibilidad de errores colaterales, los cuales además en estos casos no son siempre triviales de localizar.

Nos centramos mucho en los requisitos funcionales y en cierto modo es lógico que así sea, ya que si una aplicación no cumple el propósito y expectativas que se tiene sobre la misma de nada vale que la calidad del producto y del desarrollo sea impecable.

Parto de la base de que no es nada fácil conseguir lo que el usuario o el cliente busca, entre otras cosas porque es complicado que ambos tengan del todo claro lo que quieren (sí lo pueden tener en la cabeza, pero una aplicación son muchos detalles y es difícil que tengan en cuenta todos) y también que los analistas entiendan e interpreten a la perfección lo que les están contando.

Precisamente porque el desarrollo de un producto tiene asociado inherentemente tareas de mantenimiento correctivo y evolutivo es necesario ir más allá de que el producto funcione, entendiendo que el diseño y codificación realizados deben hacerse pensando en que habrá que modificar la aplicación, para ello desde un primer momento hay que pensar también en la calidad de la aplicación desde el punto de vista de la arquitectura, diseño y construcción.

No tener en cuenta estos detalles provocarán en el sistema desarrollado una deuda técnica que se arrastrará en los mantenimientos y tenderá a empeorar salvo que se decida atajar, ya que el software tiende a deteriorarse con los mantenimientos, siendo este deterioro muy sensible en aquellos casos donde el producto no ha tenido una ejecución buena desde el punto de vista de la calidad del código.

Sonar permite ver de manera muy sencillo lo que esconden las aplicaciones debajo de la alfombra a través de las diferentes métricas que permite obtener. Es cierto que solo son un conjunto de métricas y que hay que saber interpretarlas de manera individual, colectiva y en el contexto de cada aplicación, pero permiten dar una visión objetiva y externa tanto a clientes como a proveedores, ya que no hay trampa ni cartón si ambas partes conocen el juego de métricas y reglas que se le van a pasar al código.

Desde mi punto de vista Sonar es una herramienta de implantación obligatoria en los departamentos de desarrollo y/o de calidad del software, ya que además de permitir detectar aspectos del desarrollo que generan deuda técnica, permiten determinar la complejidad (y por tanto tiempo y esfuerzo) de los mantenimientos y puede ser utilizada para establecer determinados acuerdos de niveles de servicio en cuanto al software que se entrega.

Haciendo una revisión de algunos incumplimientos de reglas detectados por Sonar, nos llamó mucho la atención a un proveedor y a mi un incumplimiento que se repetía en distintas partes del código de una aplicación Java basado en una regla de CheckStyle denominada “Parameter Assignment”, ya que ninguno de los dos entendíamos por qué.

¿Cuándo se produce el incumplimiento de esta regla? Cuando dentro del cuerpo de un método se asigna un valor a un parámetro de entrada.

Para intentar entender los motivos acudí a Internet y al libro “Refactoring: improving the design of existing code” de Martin Fowler y Kent Beck y básicamente es el siguiente:

Mejorar la comprensibilidad de los métodos por la confusión que existe en muchos casos con el paso de parámetros a los métodos en Java. En Java siempre se realiza un paso de parámetros por valor, es decir, el método trabaja con una copia del parámetro enviado por el método llamante por lo que cualquier posible cambio en dicho parámetro dentro del mismo no afecta hacia fuera de él, no obstante la confusión la tenemos cuando el parámetro que se envía es un objeto en lugar de un tipo primitivo, en ese caso también existe un paso por valor, pero la copia en esta caso es una referencia a memoria por lo que los cambios en este caso si pueden tener su reflejo en el método llamante. Como pese a que el paso del parámetro es siempre por valor, pero el comportamiento varía en función de si lo que se pasa es un objeto o un tipo primitivo una forma de darle un tratamiento homogéneo es tratar el parámetro de entrada como una “constante” y que si hay que hacer operaciones se haga con variables u objetos temporales.

Además de tratarse de una regla de estilo (claridad, comprensión y homogeneización), en mi opinión (y es solo mi opinión) también es una forma de prevenir cambios no deseados en el valor de un objeto mediante al acceso a su contenido en los métodos donde se pasa el valor de su referencia en memoria.

Sonar permite calcular muchas métricas de carácter individual, como por ejemplo Cobertura, Duplicated Lines, Complejidad Ciclomática, RFC, LCOM4, etc…, pero también dispone de métricas que ofrecen resultados en función de poner en relación una serie de métricas diferentes.

Un ejemplo de ello lo tenemos en Technical Debt (base como he comentado en otros artículos de lo que es la filosofía y fin último de Sonar, tal y como dice su lema “Put your technical debt under control) y otro lo tenemos por ejemplo en las métricas proporcionada por el plugin de Sonar denominado SIG Maintainability Model.

Como indica la página que el plugin tiene en Sonar, se trata de una implementación del Modelo de Mantenimiento del Software Improvement Group (SIG).

¿Qué es el Software Improvement Group? Es una firma de consultoría holandesa con sedes en otros países europeos, que según especifica en su página web se basa en dar una serie de resultados, basados en hechos (métricas objetivas) y no en opiniones, proporcionando evaluciones de riesgos imparciales, objetivas,verificables y cuantitativas relacionados con los sistemas de información corporativos que se quieran estudiar.

Uno de los servicios que proporciona la empresa SIG es la certificación de la mantenibilidad de los productos software según el estándar internacional ISO/IEC 9126. Dicho estándar clasifica la calidad del software en una serie de aspectos, uno de los cuales es la mantenibilidad basándose el mismo en la obtención de las siguientes métricas: Estabilidad, Facilidad de análisis, Facilidad de cambios y Facilidad de Pruebas.

Por tanto el plugin de Sonar SIG Maintainability Model, permite calcular dichas métricas, las cuales resultan significativas ya que determinan el grado de mantenibilidad de un software basado en un estándar internacional.

Una vez que hemos podido apreciar lo relevante que resulta este plugin, toca analizar como calcular cada una de sus métricas.

El cálculo se basa en la obtención en primer lugar de una serie de métricas más simples (ya que las métricas finales indicadas anteriormente son el resultado de la combinación de las mismas) y clasificando los resultados de dichas métricas en cinco posibles escalas (las cuales voy a ordenar de menor a mejores resultados desde el punto de vista de la mantenibilidad):

Volumen: Se obtiene a partir del número de líneas de código de la aplicación.

Escalas:

–: > 1310000 líneas de código.
-: > 655000 líneas de código.
0: > 246000 líneas de código.
+: > 66000 líneas de código.
++: > 0 líneas de código.

Duplicidades: Se obtiene a partir del porcentaje de líneas duplicadas en la aplicación:

Escalas:

–: > 20% líneas duplicadas
-: > 10% líneas duplicadas
0: > 5% líneas duplicadas
+: > 3% líneas duplicadas
++: > 0% líneas duplicadas

Pruebas unitarias: Se obtiene a partir del grado de cobertura del código a través de pruebas unitarias.

Escalas:

–: > 95% cobertura
-: > 80% cobertura
0: > 60% cobertura
+: > 20% cobertura
++: > 0% cobertura

Complejidad: Se obtiene a partir de la complejidad ciclomática de los métodos.

La forma de cálculo es un poco más compleja ya que se realiza en dos pasos:

1) Clasificar cada método en uno de los siguientes rangos en función de su complejidad ciclomática:

Muy alta: > 50
Alta: > 20
Media: > 10
Baja: > 0

2) A continuación la escala se obtiene a partir del porcentaje de métodos que se encuentran en los rangos definidos en el paso 1):

Escalas:

–: Resto de casos
-: C.C. Media <50%; C.C. Alta <15%; C.C. Muy Alta <5%
0: C.C. Media <40%; C.C. Alta <10%; C.C. Muy Alta <0%
+: C.C. Media <30%; C.C. Alta <5%; C.C. Muy Alta <0%
++: C.C. Media <25%; C.C. Alta <0%; C.C. Muy Alta <0%

Tamaño unitario: Se obtiene a partir del número de líneas de código de los métodos.

La forma de cálculo es un poco más compleja ya que se realiza en dos pasos:

1) Clasificar cada método en uno de los siguientes rangos en función de su número de líneas de código (LOC):

Muy alta: > 100
Alta: > 50
Media: > 10
Baja: > 0

2) A continuación la escala se obtiene a partir del porcentaje de métodos que se encuentran en los rangos definidos en el paso 1):

Escalas:

–: Resto de casos
-: LOC Media <50%; LOC Alta <15%; LOC Muy Alta <5%
0: LOC Media <40%; LOC Alta <10%; LOC Muy Alta <0%
+: LOC Media <30%; LOC Alta <5%; LOC Muy Alta <0%
++: LOC Media <25%; LOC Alta <0%; LOC Muy Alta <0%

Una vez calculadas las métricas simples, se calculan las métricas complejas que son las que definen la métrica de mantenibilidad de la ISO/IEC 9126, realizando una simple media de las escalas obtenidas en las métricas que definen cada métrica compleja:

Analizabilidad (Analysability) (A): [VOLUMEN], [DUPLICIDADES], [PRUEBAS UNITARIAS], [TAMAÑO UNITARIO].
Modificabilidad (Changeability) (C): [DUPLICIDADES], [COMPLEJIDAD]
Estabilidad (Stability) (S): [PRUEBAS UNITARIAS]
Testeabilidad (Testability) (T): [PRUEBAS UNITARIAS], [COMPLEJIDAD], [TAMAÑO UNITARIO].

De esta manera los valores de A, C, S y T oscilará cada uno entre — (valor más malo) y ++ (valor más bueno).

Estos cuatro valores se representarán en un gráfico donde cada eje representa el valor de estas métricas, teniendo cada eje tres escalas que estarán más separadas de la posición (0,0) que se corresponde con el valor (–) conforme más buena sea la métrica. En sentido horario, los ejes representan respectivamente a T, A, C y S.

Además de esa representación, el color del gráfico también es identificativo de la mantenibilidad de la aplicación, ya que tendrá un color entre el rojo (todas las métricas –) y verde (todas las métrica ++).