jueves, 9 de enero de 2014

El entorno y sus partes

La vez pasada estaba recordando un experimento sencillo que se suele explicar en las escuelas sobre ondas y la velocidad de la luz.  El experimento consistía en observar la deformación aparente que se produce a una cuchara cuando se introduce un vaso lleno de agua hasta la mitad, algo relacionado con la ley de snell si no estoy mal, luego otro día estaba leyendo sobre  como se alimentan algunas células (y el ciclo de krebs creo) y por alguna extraña razón identifiqué en ambos casos una relación que podría ser interesante a la hora de modelar ciertos problemas computacionales.

Las relaciones se dan entre un elemento y su entorno, tenia entonces esta idea en la cabeza de que los valores en las propiedades de algunos elementos varían según los valores en las propiedades de su entorno, de esta manera en el primer caso la velocidad de un rayo de luz varia según el indice de refracción del material (o ambiente) en el cual se propaga, no es igual en el vacío que en el aire o en el agua. Por otro lado cuando desarrollamos software utilizando OOP definimos clases que encapsulan los datos de sus propiedades, si por ejemplo yo quisiera modelar el primer caso, tendría una clase llamada RayoDeLuz que tendría un atributo velocidad, uno podría modelar el atributo velocidad como un método ObtenerVelocidad(..) que recibe como parámetro el material sobre el que se propaga el rayo de luz y así hacer el calculo pertinente para devolver la velocidad.  Una vez que se ha definido una clase para representar un material, es difícil encontrar una forma de alterar el método ObtenerVelocidad para que funcione con otros materiales cuya influencia en los valores de velocidad están afectados por otro tipo de propiedades diferentes a las previamente establecidas, toca cambiar una interfaz, toca cambiar el método, toca establecer casos específicos para cada tipo de ambiente.

Ahora consideremos que los datos son similares al rayo de luz, la representación de la información estaría dada por el medio en el cual se propaga dicha información, si por ejemplo los datos viajan por la red su representación sería diferente a su representación en la capa de aplicación o la de presentación, entonces, ¿Podríamos lograr que la información "mutara" en cada cambio de medio de propagación?   Sera interesante codificar espacios reutilizables que pudieran albergar datos de diferentes tipos y adaptar la información entre si, por ejemplo, si tengo un objecto OBJ inmerso en un medio o espacio llamado MODELO y quiero enviar la información del objeto por la red, solo tendría que mover OBJ de MODELO a RED serializando automáticamente y agregando la información que haga falta. Los ORM se convertirían en un caso particular de este paradigma donde el espacio de origen es un modelo de datos y el de destino una base de datos relacional.  Podríamos tener ambientes de ejecución especiales para diagnosticar problemas haciendo que la ejecución misma de un algoritmo dependa del ambiente en el que se desenvuelve.  Si existieran ambientes heredados podríamos agregar aspectos nuevos al codigo existente, pero bueno la verdad se me acabó el tiempo y no terminé la idea, espero seguir otro día desarrollandola.

lunes, 30 de diciembre de 2013

ALGUNAS PREGUNTAS SOBRE LA GESTIÓN DE REQUERIMIENTOS

Se suele decir que un buen código tiene en el fondo un buen diseño, sin embargo, un buen diseño solamente se puede verificar teniendo claros los objetivos del producto cuyo desarrollo ha sido emprendido. ¿Quiere decir esto que las únicas características susceptibles de ser medidas están asociadas directamente al dominio de los problemas que dicho desarrollo soluciona?  De no ser así, ¿existe un mínimo de características de naturaleza subyacente que emerge de manera implícita en cada uno de los requerimientos del desarrollo?

Para ponerlo en términos mas simples, cuando decidimos construir software por ejemplo un editor de texto un requisito puede estas asociado a la capacidad del sistema para escribir contenidos por parte de un usuario.  Sin embargo, un requerimiento funcional de este tipo no puede estar completo de ninguna manera sin  tener en cuenta el numero de palabras que deben ser escritas en un tiempo de referencia.  Es decir. No nos sirve que se puedan escribir palabras si cada letra se demora un minuto en procesarse, seria en la practica un sistema completamente inútil.

Si acordamos entonces que existen requerimientos subyacentes a este primero nuestra especificación crecería notablemente y nada sería capaz de garantizarnos que nuevos requerimientos no presenten la misma situación recurrentemente y hasta el infinito.  ¿Cuál seria entonces la granularidad de los requerimientos de manera tal que sea viable establecer con exactitud las caracteristicas funcionales del producto y todos los requerimientos hijos relevantes?  Pareciera que la respuesta se encuentra en las bases mismas del lenguaje. Nada que sea lo suficientemente claro de manera general debe ser reformulado con el fin de esclarecer detalles redundantes.  Los requerimientos técnicos comunes deben plantearse de manera no redundante para poder alcanzar la completitud y la sencillez de manera sensata en nestras definiciones.

Si vieramos una especificacion de un sistema como un arbol donde los nodos de dicho arbol son requerimientos . ¿podriamos construir un algoritmo para construir dicho arbol minimizando el numero de niveles y maximizando la descripcion del problema?  ¿Podríamos realizar una clasificación automática sobre los conjuntos de tipos de requisitos técnicos generales que son comunes a un subconjunto de requerimientos?  Probablemente basados en la experiencia previa algunos algoritmos existentes de clasificación y optimización podrían afinar con precisión cada vez mayor en el tiempo las predicciones de asociaciones o relaciones entre requisitos funcionales y técnicos, logrando una mayor robustez en las definiciones y los objetivos de nuestros desarrollos.

Otra pregunta que podríamos hacernos sería, ¿Existe alguna forma de poder detectar en un esquema evolutivo de los requerimientos aquellas tareas que al final del ciclo de vida del desarrollo no habrán generado valor alguno en el producto final?  Resolver este tipo de interrogantes pueden representar mejoras significativas en la productividad de los equipos, sin embargo, algunas parecen estar asociadas con la visión estratégica de la gestión de los proyectos y otras parecen estar relacionadas con la forma en la que crecen las expectativas a medida que avanzamos en la construcción de los conceptos fundamentales del sistema en cuestión.

Seria interesante poder armar un modelo sencillo con las premisas esperadas con el fin de poder determinar si este tipo de interrogantes sobre la gestión de los requisitos puede abordarse de manera general o si estamos destinados a lidiar sin ninguna generalización clara con las dependencias aparentemente aleatorias entre los criterios que determinarán la calidad del software entregado.  Espero que esto se analice en una entrada posterior.



jueves, 5 de diciembre de 2013

Profiling de aplicaciones

Profiling de aplicaciones

El profiling de aplicaciones es un estilo de análisis dinámico de software en el cual una herramienta llamada comunmente profiler recolecta información sobre el tiempo utilizado durante la ejecución de un programa por parte de los componentes a evaluar con el ánimo de identificar demoras o situaciones que puedan representar problemas de rendimiento de la aplicación. De manera general lo que se busca con este tipo de pruebas es identificar los tiempos o el uso de los componentes que conforman nuestra aplicación. 

El análisis realizado al utilizar la información recolectada durante la ejecución de la aplicación en cuestión estará relacionado directamente con el método utilizado para obtener los datos, por lo que el tipo de herramienta utilizada se vuelve de vital importancia para tener una buena idea de la calidad o resolución de los datos obtenidos.  Como regla podríamos decir que a medida que obtengamos mayor exactitud en los datos recolectados, mayor será el impacto en el rendimiento de la aplicación monitoreada. A continuación se describen los tipos más comunes de profilers según la forma utilizada para monitorear la ejecución y se presentan adicionalmente algunas ventajas y desventajas de los mismos.

Basados en eventos

Este tipo de herramientas permiten la verificación de la mayoría de los tipos de evento presentados durante la ejecución de la aplicación objetivo, por ejemplo, el inicio de un llamado a una función o la carga de una biblioteca dinamica.  Dado que la información se relaciona directamente los eventos producidos, este tipo de aplicaciones puede impactar el performance de la aplicación de manera general, por lo que es usual que se utilice en ambientes de desarrollo y pruebas de investigación pero no en escenarios donde afectar el rendimiento del sistema pueda representar un riesgo como en el ambiente de producción.  La resolución de este tipo de herramientas es por lo general mejor que la de otro tipo de profilers por la cantidad de datos recolectados pues no tienen pérdidas significativas de información relacionada con los eventos interceptados.

Profilers estadísticos

Los profilers estadísticos por otra parte funcionan recolectando muestras periódicas, es decir, interrumpiendo la ejecución de la aplicación analizada y verificando la tarea realizada en el momento de la interrupción.  Típicamente este tipo de profilers utilizan llamados a funciones del sistema operativo para suspender los procesos y obtener el valor del program counter o puntero a instrucción (EIP, RIP) periodicamente y realizar un recorrido llamado stackwalk en el que se identifican las funciones en la pila de llamados a partir de la navegación entre cada uno de los frames en la pila del hilo en ejecución.  Numéricamente los valores obtenidos con este tipo de herramientas son de una precisión inferior a los obtenidos con los profilers basados en eventos y esto se debe a que la información analizada corresponde únicamente a la obtenida para los puntos de ejecución muestreados, sin embargo, una cualidad de este tipo de herramientas radica en su bajo impacto en el rendimiento de la aplicación, logrando que el programa objetivo funcione más cerca de su velocidad real.

Profilers de instrumentación

Otro tipo de herramientas abordan el problema de obtención de datos modificando la aplicación que se desea analizar, al código agregado se le denomina instrumentación y dependiendo de los puntos instrumentados (por ejemplo si decidiéramos instrumentar todo el código) dentro de la aplicación puede suponer un gran impacto en el rendimiento de la aplicación medida.  Para que las pruebas sean efectivas y correctas este tipo de herramientas dependen en gran medida de lo específica que sea la instrumentación realizada y no brindan gran flexibilidad sobre el impacto en la aplicación.  Desafortunadamente para la identificación de problemas suele requerir que se tenga una idea de los componentes implicados en la situación para poder instrumentarlos, hecho que no siempre es evidente.

Salida de profilers


Según el tipo de salida generada los profiler peden clasificarse en dos categorías, la primera corresponde a aquellas herramientas que únicamente calculan el tiempo promedio por llamado sin preocuparse de diferenciar entre el tiempo propio del método o función involucrada y el tiempo consumido por los llamados a otros métodos por parte de dicha función, a este tipo de herramientas se les conoce como profilers planos o flat profilers.   La segunda categoría está asociada con las herramientas cuya salida contemplan en su análisis las diferentes rutas del código y el contexto de la ejecución y pueden diferenciar entre el tiempo própio consumido por una función del tiempo consumido por cada uno de sus llamados, a estas herramientas se les conoce como profilers de gráfos de llamadas y son más detallados y elaborados que los profilers planos.

miércoles, 4 de diciembre de 2013

No olvides la escalabilidad

¿Qué es escalabilidad?

La escalabilidad se puede definir informalmente como la posibilidad que posee un sistema para incrementar su capacidad de atención a medida que el número de solicitudes o usuarios se incrementa.   Es en otras palabras la forma en la cual un sistema puede adaptarse a los cambios potenciales en la demanda sin perder calidad en el servicio prestado.   De esta manera, se debe considerar la escalabilidad al momento de adquirir y producir aplicaciones informáticas, por ejemplo, suponga que usted administra una universidad y para efectos administrativos se necesita un sitio web donde se puedan publicar las notas de cada uno de los estudiantes, inicialmente la institución educativa tendrá 1000 estudiantes y se adquiere un software capaz de manejar 500 usuarios por minuto, como todos los estudiantes no entran al mismo tiempo a consultar sus notas, es probable que el sistema adquirido cumpla con la capacidad, sin embargo a medida que la institución adquiere prestigio en su entorno, el número de estudiantes se incrementa digamos que a unos 5000, es muy probable que durante periodos de entregas de notas estos nuevos estudiantes logren superar la capacidad permitida por la aplicación usada para la administración. Una primera solución es encontrar otra aplicación que supla la necesidad, sin embargo, si la tendencia en crecimiento se mantiene para el siguiente año se tendrá nuevamente el mismo problema.  Este sencillo ejemplo nos sugiere que las aplicaciones deberían poder adaptarse  a los cambios en la demanda de los servicios prestados por las mismas, y este tipo de situaciones son las que hacen necesario que las empresas productoras de software  deban evaluar lo que se define como escalabilidad para cada una de las aplicaciones que se producen de tal manera que siempre exista un mecanismo claro  que pueda ser utilizado para aumentar su capacidad y conservar la calidad y los niveles de servicios adecuados para las operaciones realizadas. 
En la mayoría de las ocasiones dicho mecanismo estará relacionado con agregar nuevo hardware a la infraestructura de operación, por ejemplo agregando mas máquinas o mejor hardware.  Teniendo en cuenta que ambas opciones pueden generar un incremento de la capacidad, la escalabilidad de aplicaciones puede clasificarse a partir de las siguientes categorías.

Escalabilidad Vertical

La escalabilidad vertical es la manera de conseguir mayores niveles de atención incrementando las capacidades de los recursos específicos consumidos por las aplicaciones, por ejemplo, aumentando el tamaño de la memoria principal, el número de CPUs de una máquina, el espacio en disco  o agregando tarjetas de red más potentes.
Aunque es cierto que en primera instancia esta puede ser una forma sencilla de aumentar la capacidad de los servicios prestados por nuestras aplicaciones, el sentido común nos indica que existe un límite asociado con el hardware que podemos actualizar, es decir, si nuestras tendencias de crecimiento están acordes con la ley de Moore, no debería haber ningún problema, pues todos los años contaríamos con componentes de hardware superiores a los del año anterior, pero ¿que ocurre si estamos creciendo por encima de la velocidad a la cual crece la capacidad del nuevo hardware?  Esta es una pregunta que puede llegar a ser un poco molesta, porque plantea aspectos del diseño de las aplicaciones que debieron ser considerados mucho antes de poder identificar que este tipo de problemas se podrían presentar, dependeremos entonces de la posibilidad de que nuestra aplicación pueda escalar horizontalmente.

Escalabilidad Horizontal


Como se mencionó anteriormente la escalabilidad vertical tiene un límite, y este se produce cuando ya no existe el hardware que necesitamos para proveer con la misma calidad a nuestro creciente número de usuarios dentro de la operación.  Si pensamos en la escalabilidad vertical como el hecho de aumentar el tamaño de un tanque de agua utilizado para suministrar un fluido a una población, podríamos visualizar la escalabilidad horizontal como el incremento del fluido suministrado agregando cada vez mas tanques de manera tal que la escalabilidad horizontal nos proporcionará un mecanismo repetible indefinidamente a medida que el número de usuarios se incremente, logrando que la capacidad del sistema se adapte cada vez a la nueva demanda.

Metricas y valores esperados

Métricas y valores esperados

Qué es una métrica y para qué sirve?

De manera sencilla una métrica se puede definir como una medida o un conjunto de medidas que determinan una característica de un sistema.  Por ejemplo, en un sistema de aprendizaje una métrica podría estar dada por el número de estudiantes reprobados en un curso, este valor nos dará una idea de una característica de nuestro sistema escolar por ejemplo para la metodología de los docentes.   Las métricas al igual que en el resto de disciplinas, se utiliza en el software para cuantificar aspectos de interés de modo que nos permita tener una visión clara y objetiva de lo que estamos midiendo y establecer mecanismos para mejorar dicha característica.  En esta entrada nos centraremos en las métricas de rendimiento de las aplicaciones, que como se espera nos ayudarán identificar problemas de rendimiento y poder tomar acciones sobre los mismos.  Dentro de los beneficios de proporciona la utilización de métricas en cualquier área del conocimiento y consecuentemente en el software tenemos:         
  • Análisis: Podremos analizar y comprender las capacidades del sistema en cuestión de manera objetiva.
  •  Control: Se pretende poder realizar ajustes utilizando la información de dichas métricas para mantener las características en los estados deseados.
  •   Predicción: Una vez se ha iniciado un estudio con el uso de métricas sobre una característica de un sistema se hace más fácil predecir situaciones que de otra manera serían imposibles de entender.
  •  Mejora: Las métricas nos proporcionan herramientas para mejorar las características que cuantificamos.  Si se manejan objetivamente, mejorar una métrica en la mayoría de los casos es equivalente a mejorar la característica en cuestión.


Es importante tener en cuenta que solamente aquellas características que son susceptibles de ser medidas podrán manejarse utilizando métricas, por suerte, en la evaluación del performance de las aplicaciones casi todas las características se pueden medir.  Aunque parezca extraña esta aclaración, existen aspectos de la vida que no se pueden medir, como la moral , la felicidad y muchos otros aspectos relacionados con cuestiones humanas, en términos generales lo medible está asociado a unidades físicas, como tiempo, espacio, memoria, etc.  Las métricas están basadas en conceptos medibles que son relaciones abstractas entre las características de las entidades medidas.

En la evaluación del software existen algunos objetos o entidades de interés particular en el estudio del comportamiento de las aplicaciones.  A continuación presentamos algunas:

  •          Servicios
  •          Productos
  •          Procesos
  •          Recursos


Las características o atributos según su cercanía con los usuarios pueden ser internos o externos, dependiendo de si los usuarios son conscientes de la existencia de la característica.  Dentro de las características que nos interesan medir para el performance de aplicaciones encontramos dos que suelen ser importantes.

  •          Comportamiento en el tiempo
  •          Utilización de recursos


Usualmente el comportamiento en el tiempo es afectado por la utilización de los recursos y los recursos son retenidos por problemas de lógica con un rendimiento pobre.  Por ejemplo si una aplicación web utiliza una base de datos y la aplicación tiene problemas de desempeño, dicha aplicación podría provocar bloqueos en los recursos de la base de datos, de la misma manera si los recursos no fueran suficientes en el servidor de bases de datos se podrían presentar pérdidas de rendimiento en la aplicación web que la usa.  Casi todas las métricas de performance están asociadas a cuantificar aspectos que se relacionan con el tiempo o con los recursos. 

Una métrica consta de un método de medición por ejemplo una formula y una escala para los valores obtenidos a partir de dicho método.   Las métricas pueden ser directas si solo dependen de la característica o atributo que se está midiendo o indirectas si dependen adicionalmente de otras métricas y se formalizan a partir de una función de medición.

Las métricas de rendimiento por lo general son independientes de los escenarios, de las plataformas, del hardware y de la lógica de negocio que se esté probando.

Categorización de métricas

Según la cercanía con el usuario

  •          Internas: La característica que evalúa la métrica depende únicamente de factores internos del diseño y los usuarios no tienen consciencia de su existencia.


  •          Externas: Los Usuarios son conscientes o tienen un contacto directo con la característica que evalúa la métrica en cuestión y sus valores pueden depender de aspectos específicos de la lógica de negocio de la aplicación.

Según sus valores deseables

Las métricas de performance según los valores deseados se pueden categorizar en tres tipos:

  •          Deseablemente decrecientes: Este tipo de métricas son aquellas en las cuales esperamos que un mejor comportamiento de la característica que pretende medir en el sistema esté asociado con la disminución del valor de dicha métrica, es decir, mientras menor sea el valor será mejor.  Un ejemplo de esta categoría es el tiempo de respuesta de una aplicación, se espera que mejoras en el rendimiento se reflejen en la disminución de los valores de dicha métrica.


  •          Deseablemente crecientes: A diferencia de la categoría  anterior, a este conjunto de métricas pertenecen aquellas en las que un incremento de los valores obtenidos se asocia con una mejora en el rendimiento de la aplicación.  Como ejemplo podemos ver el throughput o la productividad del sistema mientras mayor sea su valor mayor mejor será el rendimiento del sistema.


  •          Valores nominales deseables: En ocasiones existen características de nuestras aplicaciones cuyos indicadores o métricas asociadas no convienen que sean demasiado bajas o demasiado altas sino que se encuentren en lo que llamamos un valor nominal, el cual es el valor óptimo o deseado para la característica analizada.  Un ejemplo de este tipo de métricas es la utilización de la CPU, si el valor es muy bajo para un gran volumen de transacciones, puede deberse a problemas de rendimiento de la aplicación en cuestión desperdiciando el recurso específico y si el uso de CPU es muy alto, la aplicación podría estar impactando otros aspectos del servidor como la estabilidad del propio sistema operativo haciendo que todo fluya de manera más lenta y riesgosa.  Podríamos decidir de manera arbitraria que el valor nominal fuera un 50%.

Según el marco de referencia

Dependiendo de si una característica se evalúa en un solo contexto o si existen valores de referencia con los cuales se contrastan las mediciones, las métricas se pueden clasificar en las siguientes categorías:

  •           Absolutas: Son aquellas métricas que dependen únicamente de mediciones realizadas en la aplicación que se está midiendo, por lo cual únicamente reflejan el comportamiento de la característica evaluada de manera individual o aislada. Un ejemplo de este tipo de métricas sería el tiempo de respuesta de un método en una clase.


  •           Relativas: Las métricas pertenecientes a este grupo definen el comportamiento de la característica evaluada en relación a un comportamiento base que en muchos casos corresponde a una segunda aplicación de referencia, por ejemplo, la diferencia entre los tiempos de respuesta de dos aplicaciones.  Las métricas pueden ser relativas a otras métricas o a valores nominales.

Según la naturaleza de la medición

  •          De recursos: La característica medida se relacionada con recursos del sistema, por ejemplo, el número de escrituras en disco debido a un evento dado.


  •          De tiempo: La característica evaluada es una duración o un valora asociado al tiempo como el periodo o la frecuencia de un evento.


  •          De complejidad: Este tipo de métricas pretende cuantificar la complejidad computacional del sistema, por ejemplo, para el número de ciclos en el código para una operación particular.  Aunque puede que estas no midan directamente temas de desempeño, puede que perdidas en el desempeño estén relacionadas directas o indirectamente con los aspectos medidos por las mismas.



  •          De calidad: Encontramos en algunas ocasiones métricas que no miden ni recursos, ni tiempos, ni temas de complejidad, sin embargo, el mejoramiento de las mismas puede representar un mejoramiento en el rendimiento del sistema. Un ejemplo podría ser la tendencia en un comportamiento de la aplicación como la estabilidad o cualquier aspecto del diseño.

sábado, 30 de noviembre de 2013

Pruebas de performance

¿Qué son las pruebas de rendimiento?

De manera sencilla podemos definir las pruebas de rendimiento como un conjunto de pruebas que son realizadas a una aplicación para determinar el comportamiento del performance de la misma, identificar puntos potenciales de mejora o garantizar los niveles de servicios esperados por los clientes del sistema.  Actualmente este tipo de pruebas son realizadas en la mayoría de los proyectos de tecnología únicamente a aquellas aplicaciones de ejecución crítica que puedan afectar la operación del negocio, aunque cada vez más se evidencia la necesidad de realizar este tipo de pruebas para los diferentes aspectos del código producido y consecuentemente los proyectos de tecnología hacen cada vez un mayor uso de pruebas de stress y de carga en un mayor número de compontes de sus sistemas.   
Cabe recordar que las pruebas de rendimiento pueden realizarse en diferentes niveles del desarrollo, pueden planearse y ejecutarse mucho antes de que se haya finalizado el desarrollo en su totalidad, por ejemplo, podríamos realizar pruebas de rendimiento a cada uno de los componentes que integran el desarrollo en conjunto con las pruebas unitarias de los mismos y a medida que se vayan integrando podríamos realizar otras pruebas de integración entre componentes que aseguren que no existan problemas de rendimiento en la forma en la cual interactúan a partir del diseño del sistema.  Realizar pruebas de rendimiento de manera temprana es una buena forma de establecer garantías parciales en el mediano plazo que nos permitan avanzar hacia los objetivos de performance definidos con una incertidumbre menor.

¿Por qué son necesarias?

Podría pensarse que las pruebas de rendimiento pueden ser ejecutadas de manera posterior al despliegue de nuestras aplicaciones, sin embargo, debemos tener claro que las pruebas de rendimiento tienen como objetivo fundamental anticipar los problemas de performance que puedan ocurrir una vez la aplicación esté en producción.  Una vez que se haya llevado a cabo la entrega de un desarrollo, será mucho más difícil encontrar formas de sobrellevar problemas de desempeño detectados en la operación ya que en esta etapa contaremos con un menor control sobre las características que podremos cambiar para cumplir los nuevos objetivos.
Las pruebas de rendimiento adicionalmente nos ayudan a visualizar tiempos muertos de las aplicaciones que de otra manera no hubieran podido detectarse, redundando en un mayor conocimiento las tareas que consumen mayor tiempo, de los límites del código producido y adicionalmente en la identificación de los puntos más frágiles de la aplicación.  Cada uno de estos aspectos es de vital importancia si deseamos descubrir mejores formas de realizar las operaciones pertinentes. 

¿Cómo pueden ayudarnos?

Las pruebas de rendimiento pueden hacernos la vida más fácil o más difícil dependiendo de la forma en la que se gestione la ejecución de dichas pruebas, si no se tienen claros temas del proyecto relacionados con costos, tiempos de entrega y parámetros específicos de las pruebas realizadas podemos entrar en un ciclo infinito de pruebas rechazadas. 
Es importante entender los conceptos o las características que se están midiendo y como se alinearán dichas mediciones con los objetivos del proyecto antes de iniciar un plan de pruebas que probablemente arrojen resultados de poco valor que no nos permitan mejorar nuestros productos. 
Otra forma en la que las pruebas de performance pueden ayudarnos está relacionada con la capacidad de predecir comportamientos a partir de los volúmenes de datos u operaciones de manera tal que podamos ser capaces de entregar guidelines o documentación clara sobre el rendimiento que debe ser esperado por parte de los usuarios del sistema, por ejemplo, si hemos construido una aplicación para construir pantallas de captura de datos en la web y entregamos una documentación que establezca que el tiempo promedio de procesamiento para un control en una página tiene un tiempo promedio de 1 ms bajo ciertas condiciones, con seguridad podremos establecer los requisitos de hardware mínimos para que dicha premisa se cumpla y adicionalmente podremos evitar mediante políticas de desarrollo que los usuarios del sistema intenten construir páginas con 3000 controles que demorarían en promedio 3 segundos en cargar lo que podría ocasionar otro tipo de problemas en el desempeño general de la aplicación.

Etapas generales de una prueba

Una prueba de rendimiento por lo general se encuentra divida en tres fases.  A continuación se presenta una breve descripción de cada una de ellas.
Construcción: En esta etapa de la prueba se identifican los casos que serán susceptibles de ser probados, se define la manera en la cual se realizará la prueba y se construyen scripts o artefactos que permitirán su ejecución. 
Ejecución:  El objetivo en esta fase es el de utilizar los artefactos construidos para la ejecución de la prueba con el fin de obtener información sobre las métricas, los casos y/o características definidas durante la construcción.
Análisis: Una vez que se haya recolectado la información que se ha obtenido durante las ejecuciones de las pruebas de rendimiento se deberá proceder a analizar la información según para poder tomar acciones los resultados encontrados.
A pesar de que puede parecer sencillo, la obtención de los datos de una prueba con un nivel de confianza relativamente bueno puede suponer algunos retos a la hora de poner en marcha el plan.  Temas como el aislamiento de los ambientes, el estados de los recursos, temas de configuración de la aplicación y los errores humanos pueden repercutir en la invalidez de las pruebas realizadas, derivando en la necesidad de repetir la prueba una y otra vez hasta que se logue la confianza suficiente en los datos y los procedimientos utilizados.



Validez de pruebas

Como se mencionó anteriormente las pruebas pueden invalidarse por un gran número de razones de diferente naturaleza, por esta razón sería una buena idea tener claros los escenarios más comunes que puedan presentarse para que una prueba pierda su validez.  En esta sección presentaremos algunos de estos escenarios e intentaremos detallar ejemplos asociados con los mismos.

Aislamiento del ambiente de pruebas

Un caso común en las áreas de tecnología de las empresas hoy en día corresponde al reúso de recursos para los ambientes de IT, ser reutiliza la infraestructura con el objetivo de reducir costos, por esta razón, encontramos ambientes virtualizados que comparten hardware entre sí con el fin de optimizar las operaciones, estas situaciones pueden representar un riesgo al momento de realizar pruebas de rendimiento, ya que si el ambiente que ha sido destinado para pruebas también se utiliza en otro tipo de tareas como por ejemplo como ambiente de desarrollo, los resultados de nuestras pruebas podrían no ser del todo confiables. 
Teniendo como base el planteamiento anterior podemos inferir que para que la validez de nuestras pruebas no se vean afectadas por operaciones sobre el ambiente externas a los escenarios probados es fundamental que el ambiente de pruebas esté físicamente aislado de otros ambientes.  Esto no quiere decir que el ambiente deba estar totalmente desconectado, por ejemplo, de la red de nuestra compañía o que deba ser privado de cualquier servicio de comunicación con otros elementos de la infraestructura, lo que realmente nos dice es que los recursos destinados para pruebas deben estar lo suficientemente disponibles para garantizar que la aplicación probada no presentará pérdidas de rendimiento debido a elementos externos a la misma prueba y a las condiciones naturales de la plataforma sobre la que opera. 

 

Disponibilidad de recursos suficientes

Aún cuando el ambiente se encuentre físicamente aislado durante la realización de las pruebas de rendimiento, es posible que existan elementos locales dentro de las máquinas utilizadas, por ejemplo, componentes ajenos a la prueba que puedan consumir intensivamente recursos computacionales como podrían ser instalaciones imprevistas de aplicativos, ejecución de análisis de antivirus o condiciones específicas del sistema operativo pueden provocar una disminución en los tiempos obtenidos en la prueba de manera, hecho que podría confundirse con mejoras o disminución del rendimiento al ser comparados con pruebas que hayan sido realizadas en ausencia de la ejecución de dichos componentes.  Entonces podemos decir que se debe verificar que los recursos locales no están siendo afectados de manera intensiva por aplicaciones externas a la prueba.  De igual manera otras tareas en ejecución como la descarga de grandes volúmenes de información de internet durante el tiempo de la prueba puede afectar  recursos utilizados por la aplicación probada como el acceso a la red.

Invariabilidad del hardware y software

Una vez que se ha garantizado que las condiciones físicas y locales del ambiente de pruebas se perciben como estables podemos comenzar a realizar nuestras pruebas, pero en caso de utilizar diferentes ambientes para realizar las mismas debemos garantizar que las mediciones de tiempos se realicen utilizando recursos de hardware equivalentes, es decir, asegurar que a pesar de que las pruebas se hayan realizado en diferentes máquinas, las especificaciones del hardware utilizado sean exactamente iguales o muy similares, en caso de ser diferentes las pruebas son incomparables a menos que los resultados de las pruebas se evalúen en términos relativos, como porcentaje del tiempo consumido. 
Si una aplicación se prueba dos veces en ambientes con especificaciones de software diferentes, como por ejemplo, máquinas con sistemas operativos diferentes o con diferencias en parches instalados del sistema, es posible que las operaciones realizadas en ambas pruebas no se realicen de la misma manera, por lo cual nuevamente las pruebas no son comparables entre sí.  Lo más cómodo en términos generales al momento de ejecutar un conjunto de pruebas es seleccionar un único ambiente, aislado y controlado para ejecutar todas las pruebas pertinentes, de esta manera no deberemos preocuparnos por las diferencias de hardware o software pues el ambiente será igual para todas las muestras recolectadas haciendo que los resultados entre pruebas puedan se comparables entre si y tengamos una mayor confianza en los datos obtenidos.

Representatividad del tamaño de muestras

Aún cuando el ambiente esté aislado, las condiciones locales del sistema sean uniformes y no existan diferencias de hardware o software entre las pruebas realizadas, debemos entender que el estado de las máquinas en la que se hospedan nuestras aplicaciones está sujeto a variaciones no predecibles, como por ejemplo el uso de la memoria virtual, caídas repentinas de la disponibilidad de ciertos servicios como la red o tecnologías asociadas con la reducción del consumo energético de los equipos y procesadores. 
Por el motivo anteriormente expresado, es necesario tener un tamaño de muestreo representativo, con el fin de descartar los casos atípicos y poder establecer un comportamiento promedio o general de las características que deseamos cuantificar.  Mientras mayor sea el número de muestras recolectadas tendremos una visión más completa del comportamiento medido.  Determinar el número de pruebas a realizar para obtener un nivel de confianza aceptable puede llevarnos a un análisis más formal y estadístico, aunque usualmente este tipo de métodos no son utilizados con frecuencia por lo que se trata de realizar las pruebas con el mayor número posible de iteraciones o usuarios, por ejemplo por encima del número promedio real.  Tener un número muy bajo de pruebas puede invalidar los resultados obtenidos a partir de las mismas.

Repetitividad o repetibilidad de Casos

Otro factor que suele ser determinante para identificar la validez de una prueba puede estar relacionado con la capacidad que tiene la prueba de ser "repetible", para exponer la importancia de este concepto, plantearemos un escenario en el cual una aplicaciones realiza un conjunto de tareas diferentes de manera aleatoria, si realizamos las pruebas de dicha aplicación ¿cómo podemos asegurar que todas las pruebas miden los mismos aspectos?, de hecho, no lo hacen, por lo que se evidencia la necesidad de que los casos probados sean perfectamente reproducibles de modo que podamos establecer comparaciones sobre los resultados obtenidos.  En algunas ocasiones la repetibilidad puede estar asociada a los diferentes caminos lógicos que una aplicación puede tomar durante su ejecución y en otras ocasiones dichos cambios dependerán de los datos suministrados como entrada de los algoritmos implementados. 
Algunas veces es casi imposible lograr que el número de llamados a todas las funciones sean exactamente igual entre pruebas, sobre todo si lo que intentamos comparar está relacionado con mejoras debido a modificaciones sobre los algoritmos codificados.  En estos casos es posible realizar las comparaciones normalizando los datos, por ejemplo  si una aplicación ejecutó diez veces el método A() en la prueba 1 y 30 veces el mismo método en la prueba 2 podremos comparar los resultados dividiendo el tiempo T1(A) entre 10 lo que nos dará el tiempo promedio de A durante la prueba 1 y podemos hacer lo mismo para la prueba número 2 obteniendo el tiempo promedio de A durante la prueba 2 al restar T2(A) - T1(A) podremos establecer la diferencia entre los tiempos promedio aún cuando las dos pruebas no coincidan en el número de llamados. 
Analizar tiempos de funcionalidades entre pruebas con diferente número de llamados y sin normalizar los tiempos puede ser un factor para invalidar las conclusiones obtenidas en las pruebas realizadas.

Confiabilidad de herramientas y mediciones


Bueno, como último aspecto a considerar para evitar que nuestra prueba sea confiable tenemos la confiabilidad de las herramientas que son utilizadas para realizar las mediciones de las características que deseamos evaluar.  Obtener datos a partir de herramientas que puedan retrasar en demasía los tiempos de la aplicación a probar puede llevarnos a conclusiones erróneas sobre el comportamiento de la misma.  En los posible las herramientas utilizadas deben estar certificadas o ser reconocidas como herramientas que arrojan resultados confiables y que tienen el menor efecto posible en la prueba.

Performance de Aplicaciones

Generalidades del performance

Día a día se escuchan en las áreas de tecnología de las grandes empresas frases que asociamos con el rendimiento de las aplicaciones, en algunos casos por ejemplo escuchamos que el performance de cierta característica de nuestra aplicación es bueno o malo, de igual manera relacionamos términos como "cuellos de botella" , "contención" y "throughput" con temas de rendimiento de aplicaciones, sin embargo, cuando nos detenemos a pensar sobre lo que significa el performance de  aplicaciones solemos no tener una idea clara sobre lo que realmente significa y esta será la pregunta que nos ocupará durante la lectura de este capítulo.

¿Qué es el performance de aplicaciones?

En primera instancia podemos definir el performance desde el punto de vista del usuario como la capacidad que tiene una aplicación de realizar las operaciones que le competen cumpliendo a cabalidad con las expectativas de procesamiento de los usuarios.  Teniendo en cuenta esta primera aproximación se evidencia que el buen o mal rendimiento por parte de una aplicación será una categorización subjetiva dependiendo de los requerimientos técnicos esperados por parte de los usuarios, llevándonos a concluir que lo que para un usuario es un rendimiento aceptable para un sitio web para otro puede no serlo, normalmente la percepción del rendimiento está asociada al volumen de operaciones y datos que dicho usuario opera y dicha percepción irá cambiando a medida que los volúmenes de información y cálculos se van incrementando en el tiempo. 
De lo anterior se puede intuir que nuestras expectativas a la hora de diseñar y construir una aplicación determinan en cierta medida el resultado que se obtendrá en términos de desempeño, sin embargo, podemos observar que el hecho de que los volúmenes de información manejados por dos aplicaciones distintas sean similares no quiere decir que el rendimiento de ambas aplicaciones sea igual, aunque al usuario pueda percibirlas por igual en producción, aún mas, que el performance de una aplicación se perciba como bueno no implica que la manera en la que dicho software realiza sus operaciones sea la óptima.  Teniendo en cuenta este segundo punto tendremos que buscar definir el rendimiento de una manera más objetiva de modo tal que frente a dos aplicaciones se pueda establecer cual cuenta con el mejor rendimiento. 

Subjetividad y objetividad de los datos

La manera más sensata de conseguir una aproximación objetiva podría ser establecer métricas que se encuentren directamente relacionadas con las expectativas de un único usuario de referencia, por ejemplo, para un usuario gerente de un banco y para una aplicación web de su intranet una buena métrica podría ser el número de créditos que pueden ser ingresados al sistema durante una hora, para un usuario en una planta de producción una buena métrica podría estar relacionada con el número de piezas revisadas por minuto.  Estas métricas que se han definido podrían diferenciar claramente las características de rendimiento entre dos aplicaciones de funcionalidades similares, sin embargo para poder establecer un criterio de evaluación realmente objetivo que nos permita determinar las características y límites de performance de las aplicaciones, dicho criterio deberá abstraerse de las condiciones y las expectativas del negocio, llevándonos a clasificar las métricas para la evaluación del desempeño en dos categorías, la primera de ellas serían las métricas de negocio y las segunda categoría estaría dada por las métricas de técnicas ambas relacionadas con el performance. 
Una combinación efectiva de ambos tipos de métricas nos ayudarán a evaluar las condiciones de rendimiento de manera objetiva sin alejarnos de los objetivos de negocio que perseguimos con la implantación de nuestra solución en la organización. 
Todo esto está muy bien, pero ¿Qué tipo de métricas técnicas nos permiten evaluar el desempeño de nuestras aplicaciones de manera objetiva? bueno, las métricas indicadas estarán relacionadas con las operaciones (computacionalmente hablando) que realice la aplicación en cuestión.  Para una aplicación web es muy probable que el número de solicitudes procesadas por segundo sea una buena medida de cuanto es capaz de hacer dicha aplicación. 
En términos generales podríamos decir que el rendimiento general de una aplicación será mejor mientras más operaciones por unidad de tiempo realice, sin embargo, aunque pareciera que esta métrica únicamente dependiera de el número de operaciones procesadas por nuestro software y del tiempo,  la realidad es que otros aspectos como el la cantidad de recursos que son consumidos por el sistema pueden hacer que el número de operaciones por segundo disminuya sustancialmente razón por la cual nos interesará establecer métricas relacionadas con el consumo de recursos como memoria, cpu, red.
El performance de las aplicaciones por tanto puede concebirse como  el conjunto de valores asociados con métricas claramente definidas que determinan la capacidad de procesamiento del sistema computacional en cuestión así como su calidad y el análisis de performance podría verse como el conjunto de las técnicas y herramientas orientadas al estudio del mejoramiento de los valores para cada una de dichas métricas.

Importancia del performance

Como se ha mencionado en el capitulo anterior, el performance de las aplicaciones que construimos es una parte importante dentro de los objetivos de negocio de una organización, temas como la ejecución de las operaciones críticas de una empresa pueden estar siendo afectadas por  pérdidas en el rendimiento de los sistemas de información utilizados para gestionar dichas operaciones y estos temas de negocio son en general los que deben motivarnos a realizar planes para el mejoramiento del desempeño.  La importancia de obtener aplicaciones con un buen performance se visualiza de manera más evidente cuando se contemplan los riesgos asociados, es decir, los problemas potenciales que pueden provocarse por no tener en cuenta el desempeño de los sistemas construidos.  A continuación se presentan algunos de los riesgos que pueden generar este tipo de problemas.

Daños en las relaciones con los clientes

La manera más obvia en la que nos afecta un bajo desempeño del software está ligada con la disminución en la capacidad que posee una organización para proveer sus productos o servicios a los clientes, una disminución en los niveles de servicio, tiempos de entrega o productos fabricados puede afectar la imagen de la organización de manera tal que los consumidores asocien una empresa con un mal manejo de la actividad comercial a la que se dedica, los daños en la imagen pueden tomar mucho tiempo en repararse y comúnmente reparar las causas de los daños no mejora inmediatamente la percepción de los consumidores. Para el caso específico del software como producto, una aplicación que ha sido identificada como de bajo rendimiento hará que los usuarios antiguos dejen de utilizarla y probablemente desviará el mercado potencial hacia otras alternativas equivalentes, es decir,  nuestra competencia.

Efectos en la comunicación

En el mundo de las arquitecturas empresariales de hoy, las aplicaciones suelen estar conectadas entre sí ejecutando sus tareas de manera colaborativa y coordinada, de esta manera la velocidad del sistema general puede comenzar a presentar una disminución en las operaciones realizadas debido al bajo rendimiento de una de muchas aplicaciones dentro del ecosistema.  Este tipo de situaciones, por lo general terminarán afectando los tiempos de respuesta de algunos servicios de la organización y a todas sus dependencias, haciendo más difícil el cálculo de la pérdida y multiplicando la misma.

Pérdidas de dinero

Los problemas relacionados con la capacidad en la operación de una organización no solamente afectan la imagen y la confianza de nuestros consumidores en la calidad de los productos, en ocasiones, los retrasos producidos en las entregas o transacciones en general pueden desencadenar pérdidas significativas de dinero para las empresas ya sea por el pago de multas, clausulas de incumplimiento, por reprocesamiento innecesario de trámites o por términos pactados con anterioridad en las contrataciones relacionados con los niveles de servicio.  A veces, los ingresos de la organización puede estar relacionado de manera directa con el volumen de información que es capaz de procesar un sistema de información específico.

Perdida de la competitividad

Sumado a los problemas anteriormente descritos, es probable que se comience a tener problemas de velocidad respecto a nuestros competidores y a la oferta de los productos o servicios en el mercado.



Performance como estrategia de mercadeo

Cualquier producto o servicio que se esté presente en el mercado es susceptible de tener competencia y como es algo natural los productos y servicios relacionados con el software no son la excepción.  Aún cuando nuestros productos sean lo suficientemente innovadores y novedosos como para que no exista competidor alguno, la posibilidad de competencia siempre está presente cada vez que se abre un nuevo nicho en el mercado y si queremos conservar nuestra ventaja competitiva tendremos que mejorar permanentemente las características de dichos productos de manera que satisfagan las necesidades de nuestros consumidores.  El rendimiento de nuestras aplicaciones no está exento de esta problemática de manera tal que en ocasiones las evaluaciones de desempeño nos proporcionan herramientas para presentar a nuestros consumidores la eficiencia con la que nuestro software realiza las operaciones esperadas.  
Debemos tener en cuenta que los usuarios de las aplicaciones persiguen un objetivo claro y es el de poder hacer más en el menor tiempo posible y el hacer más estará directamente relacionado con dos características principales del producto.  En primera instancia tenemos la cantidad de operaciones de negocio que podemos procesar, la cual se relaciona directamente con el rendimiento de las aplicaciones, el segundo aspecto en el cual pueden estar interesados nuestros clientes, está relacionado con la capacidad que brinde nuestros sistemas para poder crecer el volumen de las operaciones a medida que los negocios crecen, este aspecto claramente se relaciona con la escalabilidad del sistema.   En esta sección se discutirán algunos temas sobre como exponer a nuestros usuarios las ventajas de rendimiento y escalabilidad de nuestras aplicaciones.

Hablar un mismo idioma

Cuando intentamos presentar la eficiencia de nuestros productos, como ya hemos dicho anteriormente debemos tener en cuenta que a los consumidores de nuestras aplicaciones no les interesa saber cuántos bytes por segundo, accesos a bases de datos o transacciones pueden realizar nuestros programas de computadora,  se debe tratar de expresar las características de performance en términos de beneficios para la operación del negocio si realmente deseamos diferenciarnos, por ejemplo, si nuestra aplicación web puede procesar un promedio de 100 solicitudes por segundo podremos presentar esta información en términos de el número de aprobaciones para créditos que se pueden realizar al día en nuestro sistema de información, de esta manera tendrá más valor para el usuario saber que en un día laboral podrá procesar 2'880.000 de créditos que saber el número de consultas a la base de datos.  De esta manera se debe hablar en el idioma del negocio y no en términos técnicos que puedan confundir o ahuyentar a quienes serán los usuarios del sistema ofertado.

Performance como factor diferenciador


Algunas veces las empresas productoras de software liberan distintas versiones de sus productos con el objetivo de generar popularidad o recordación entre los usuarios potenciales, de esta manera, encontramos versiones de evaluación y algunas con limitaciones respecto a sus funcionalidades.  Nuevamente trabajar en el performance y en la escalabilidad de nuestras aplicaciones nos dará herramientas para convencer y conectarnos con aquellos usuarios que han estado atentos a las versiones limitadas y que están buscando poder obtener el mayor rendimiento posible para su operación, es una forma de plantear que si está contento con las limitantes, ¿podría imaginar lo que haría funcionando al máximo de capacidad?  Este concepto utiliza el performance como un factor diferenciador entre lo que ofrecemos como muestra y lo que ofrecemos como producto.  Algunas veces las diferencias están asociadas a limitaciones en el uso, por ejemplo, un sistema cuya versión gratuita se distribuye con una limitante en el número de usuarios que pueden operar en ella, atrayendo a usuarios con mayores necesidades a utilizar la versión paga con un número de usuarios indefinidos en producción.

domingo, 3 de marzo de 2013

Let the context decide



Introduction 

All developers in some moment in their lives have faced (or will be facing) the need of create a graphic editor, an ER model editor, a flowchart editor, whatever... but a common problem is that every time we start a project like these, we start without a common way to implement that, there are patterns like MVC, MVP, MVVM and others M-something. These patterns are useful but in the most of cases we find a slight line in implementation where every looks like the same, separating responsibilities is sometimes quite tricky and the more user interaction is added, the more complicated is to support these changes in developments, usually because we must to modify many components in the implementation.  Well in this article we discuss a way (maybe not the best) to solve the user interaction problems.  In general is not only useful for graphic editors but these are good examples for this article.  I hope you like it.   

Background 

Let's suppose that we must to create a shapes editor, then we start creating a class model but the user interaction behavior for every shape is different, however, the device actions set performed by users are the same for every shape.  A user can click, move, release its mouse, and is the same for other devices, the device actions are finite, but ¿why is so difficult to understand the device inputs and give a meaning to these actions? Well the answer is that the meaning for some action depends on the 'context' it is raised. Let see an example.
A user press a mouse button, if there is no shape in the mouse position a selection task must start, but if there is a shape in that position we could think that user wants to select or drag that shape, we can code this in an lazy way, but, what if this action produces different meanings depending on the shape functionality for example, if we have a box with an expand-button? or maybe a shape with an internal “draggable” element?  We must to manage all these conditions in the editor but without a well-structured way to do that all becomes a mess. 
What I propose in the title of this article is not to manage the context for those actions directly, but let the context itself decide which must be the consequences for a specific action.  Working in that way, every time a new meaning is needed or appears for a specific context is easier to maintain the code, every time a new context appears in the development is easier to create and connect with the existing development.  The class diagram shown bellow display a general idea about the code posted for this article and it is organized to read from left to right and from top to bottom. 

The first class we see is the DashboardBase class which is a user control and its responsibility is to catch the control inputs for all devices (mouse, keyboard, pencil, whatever).  This class uses a Renderizer to print the view state in the control's client area Graphics. Additionally the dashboard references an InteractionManager which processes all InteractionEvent instances built by the dashboard control depending on the event raised.  The InteractionManager class uses the View class and its responsibilities are:
1. Manage the context for the view.
2. Adapt the diagram information (for example managing coordinates spaces) to the control depending on some properties like zoom, offset.

The Selection class is only a property used by the view to store the selected shapes and it is not important in order to show the idea. The Diagram class is the model we are creating or editing, this class contains for this example Layers and Shapes.  To keep it simple we only define a BoxShape and a LineShape.  When the view detects that an action have been performed, it analyzes whether exists a shape capable to solve the context, if a shape is found that shape must solve the context depending on its features.  In that way, if the shape is a BoxShape it can define the context depending on a drag area, a rotation glyph or a re-size corner, all these with different consequences. OK now we have the context, so, how it is used?  That's the wonderful part, every context share a common interface called IInteractionContext. This interface contains basically three methods.  
Begin: When the context is activated a transaction is started. 
ProcessEvent; This method filters all the user interactions that this context can manage, including events available to commit or rollback the transaction modifying the view for the specified event.  
Commit: This method defines in every context, the way all data must be applied to the diagram model or how must be modified the view (in the Selection case for example). 
As you can see, we define in the top classes implementing the IInteractionContext.  These classes decide for every context which is the meaning for all the interaction events available for the context.  We can observe the following contexts:  

ShapeResizeContext: Context used to re-size a BoxShape. 
SelectionMoveContext: Context used to move a shape in the Diagram. 
MoveLineVertexContext: Context used to move the start point and end point for a line. 
SelectRectangleContext:  Context used to select all shapes in a rectangle. 

Results


A single image about the code execution result at Shapes Test

   

sábado, 13 de octubre de 2012

CUANDO LOS ERRORES NO ESTAN EN EL CÓDIGO


La mayoría de las veces estamos buscando los orígenes de los problemas en nuestro código fuente y puede que encontremos muchos o pocos, quien sabe, pero en otras ocasiones el código fuente solamente es el reflejo de otros aspectos de las organizaciones. A este tipo de situaciones es a la que nos vamos a referir en esta ocasión.

Una aspecto fundamental que hay que entender es que los desarrolladores son seres humanos aunque aveces no lo parezcan, la consecuencia lógica es que sus acciones afecten los resultados y el ambiente de trabajo, a continuación presento una serie de conductas y efectos producidos por las mismas dentro del código. Como es de esperarse solamente se presentan aspectos negativos ya que los positivos no generan problemas, adicionalmente pueden establecerse combinaciones y de hecho considero que cada uno tiene un poco de todos por lo que se deben analizar los comportamientos presentados desde una óptica de extremos para ser comprendidos.

El paranoico laboral.

Este es personaje que pocos identifican y que mantiene oculto sus pensamientos reales, se concentra permanentemente en la idea de que lo pueden despedir en cualquier momento, hace chistes en casi cualquier situación relacionados con el despido, se siente inseguro sobre su trabajo y actúa de acuerdo a su paranoia. Es el tipo de persona que nunca revela información sobre lo que hace porque su inseguridad es tan grande que piensa que si los compañeros entienden como funcionan sus desarrollos ya no será necesario para la organización o podrá ser sustituido. Esta persona suele escribir un código prácticamente cifrado e incomprensible para garantizar su subsistencia, como consecuencia le hace miserable la vida a todo el equipo de trabajo y sacrifica su propia tranquilidad con tal de mantener custodiado esos algoritmos que después de un tiempo ni el mismo entiende. La productividad general se ve afectada en gran medida si este individuo trabaja los aspectos principales de los productos.

El inmediatista

Otro tipo de desarrolladores que a menudo nos encontramos son aquellos que nunca se preocupan por entender las dimensiones de lo que hacen. Esto es, frente a un bug o nuevo desarrollo solo visualizan el resultado inmediato de lo que están tratando y pierden el punto de vista general. El inmediatista por lo general toma decisiones de la forma 'Si x vale 2 entonces funciona...como x vale 1 cuando no funciona entonces le sumo uno', todo esto sin preocuparse que se supone que representa x, donde se usa, en que parte del código está parado... es el que coloca la mayoría de los try..catch sin sentido, es una persona que simplemente hace lo que se le pide, no va mas allá, solo piensa en que no se le acumule el trabajo. La consecuencia lógica de esta conducta es que por cada cosa que este personaje arregle daña 5 creando una inestabilidad en el código producto de una codificación al azar.

El revolucionario

Este personaje siempre se encuentra en desacuerdo con la forma en la que se realizan las actividades de trabajo, anda en una búsqueda permanente de una solución mágica que hace que todos los problemas desaparezcan, la raíz de todos los males, el código que nos va a sacar del hueco. Este tipo de desarrolladores se siente a cada momento entre la espada y la pared, porque sus desarrollos nunca han funcionado y nunca es su culpa, está aburrido de que cada nueva solución traiga mas problemas y siempre busca nuevas tecnologías que solucionarán todo. Estos desarrolladores por lo general no viven alineados con los objetivos y dificultan la comunicación que es vital en este tipo de trabajo y hace que fluya todo en la dirección correcta.

El hombre código

Es admirado por su capacidad analítica y por ser capaz de enfrentarse a situaciones desconocidas, sabe de todo y no se especializa en nada, pero por encima de estas características es admirado por su gran capacidad de producir toneladas y toneladas de código, a veces no tan bueno, pero, a quien le importa, ES MUCHO CÓDIGO!!!. Es tan bueno programando que no necesita diseñar nada, sus diseños (cuando le toca diseñar) son extremadamente complejos. Para este individuo todos los problemas se arreglan depurando el código y los conceptos pasan a un segundo plano, lo importante es el código y piensa en forma algorítmica hasta en su vida cotidiana. Como este personaje abarca gran parte de la "producción" es uno de los elementos que mas bugs inyecta en los fuentes, crea esquemas incomprensibles para el resto de los mortales dentro del equipo.

El evasor de trabajo

De estos hay muchos, es ese tipo de persona que nunca se hace responsable de lo que hace siempre anda pensando que todos quieren ponerle mas trabajo a el, no es consiente de que el resto de las personas trabajan igual o quizás mas duro que el. Esta situación hace que siempre esté postergando sus tareas, y cuando se le pregunta sobre algo que desarrolló, siempre depende de otra cosa que el no hizo y por eso "NO SABE". Cuando el problema es generalizado dentro del equipo de desarrollo los tiempos de entrega se ven afectados por un carrusel del que es casi imposible salir, preguntando aquí y allá para saber quien conoce el estado actual del tema.

El "buena gente"

Esta persona no le dice que NO a nada, es amable con todos y siempre anda colgado con el trabajo, pero sigue aceptando mas trabajo, consecuentemente acapara gran parte de las funcionalidades y hace que todo tenga que ver con el. Sus compañeros evasores se aprovechan de esta situación para transferir su carga laboral a esta persona que en ultimas no termina siendo mas que una víctima de sus propias acciones.

El personalizado

Este ser se lo toma todo de manera personal, su código no puede ser cuestionado porque eso representa una ofensa a su intelecto, los problemas para este individuo nunca son académicos, siempre están relacionados con la manera de ser de los demás miembros del equipo y consecuentemente dificulta la comunicación creando fricciones innecesarias. Por lo general es una persona que trabaja muy bien solo y muy mal en equipo.

El superproductivo

Esta persona siempre hace mas cosas de las necesarias, si le piden que haga un informe desarrolla un sistema de información para la generación automática de informes, por lo general entrega lo que se le pide, sin embargo, terminará dando mas soporte a todas las cosas que se inventó y que no tenia que hacer. La consecuencia de esta conducta es que se desperdicia el tiempo realizando las tareas innecesarias, tiempo que podría reducir los plazos de entregas de características o desarrollos de mayor valor.

Super-EGO

Esta persona es difícil de tratar, siempre esta proyectando una imagen de saberlo todo, de ser incuestionable, nadie sabe mas que el en ningún tema. Sus actitudes crean malestar en los demás miembros del equipo y maximiza sus logros y minimiza los de los demás. Este tipo de personas no trabajan bien en equipo y vuelven lento los procesos de diseño y construcción.

Otros factores

Otro tipo de situaciones que pueden afectar los procesos de desarrollo de nuestros productos está dado por el uso apropiado de las habilidades de los miembros del equipo. Si no conocemos dichas habilidades es muy probable que estemos distribuyendo el trabajo de una manera poco inteligente, de esta forma, las características y las habilidades pueden terminar cruzándose de maneras catastróficas. En otras ocasiones problemas como la extrema presión de tiempo, la presión por los costos de proyectos o la presión por la competencias hace que la calidad con la que se liberan las características no sea la esperada, estos son otros tipos de problemas que afectan el código sin estar inmerso en el, de los cuales hablaremos en otra ocasión.

Una vez visto a grandes rasgos ejemplos de formas en las que las conductas humanas pueden influir en la calidad de los desarrollos, deberíamos ser mas autocríticos con nuestras acciones y detectar las oportunidades de mejora sobre las mismas, teniendo en cuenta que en muchas ocasiones aspectos relacionados con el desorden y los intereses personales también pueden afectar los resultados. Espero les haya gustado el articulo y también espero sus comentarios.

MODELOS DE OBJETOS


Los lenguajes orientados a objetos trajeron consigo un número de ventajas relativas a conceptos significativos de la programación, sin embargo, en muchos aspectos la disponibilidad de las ventajas no implica su comprensión y aplicación, de esta manera, se puede tener un arma muy poderosa para el desarrollo de la cual solo se utilizan las partes básicas. Teniendo en cuenta lo anterior, los miembros de un equipo de trabajo no solamente deben tener conocimientos específicos de los lenguajes de programación y de las herramientas, deben además, desarrollar en sí mismos una capacidad analítica que les permita identificar de manera clara y objetiva cuales son los elementos de un problema que brindan soluciones particulares a otros problemas. De esta forma, un pensamiento lo suficientemente analítico le permitirá a los miembros del equipo de desarrollo trabajar en términos de funcionalidades, responsabilidades, reutilización y composición de soluciones. Una forma sencilla de combatir estos problemas es utilizar diagramas que nos permitan visualizar y desacoplar la lógica de los diferentes modelos que componen la solución de la lógica de otro tipo de tareas comunes como lo son la presentación y la interacción con el usuario.

Los modelos nos permiten identificar soluciones parciales a problemas específicos y genericos que en conjunto crean una capacidad de solución a los problemas objetivo, esto simplifica el entendimiento del problema y define los límites de funcionalidades en la implementación, de otra manera se puede implementar aspectos que no serán utilizados al final del desarrollo. Los modelos de objetos suelen brindar un conjunto de soluciones que se encuentran cerradas en un grupo de componentes que usualmente son determinados por los espacios de nombres en la programación orientada a objetos.

Se puede construir un conjunto de objetos que implementen muchas funcionalidades y no ser un modelo de objetos coherente, hay que tener mucho cuidado si no se termina implementando soluciones poco claras y difíciles de comprender desde el punto de vista del desarrollador. Al momento de diseñar un modelo de objetos tenga en cuenta los siguientes aspectos:

Generalidad: El diseño debe contar con un nivel de generalidad suficiente para resolver el o los subproblemas y al mismo tiempo estar preparado para el cambio, esto debe ocurrir en cada grupo de objetos que componen el modelo que da solución al problema planteado.

Capacidad de crecimiento: El modelo debe poder crecer en el tiempo y este crecimiento debe poder realizarse de manera incremental conservando el origen del diseño.

Agrupamiento de Componentes: Agrupar los tipos de objetos suele tener ventajas al momento de mantener la solución, se debe tratar de agrupar las funcionalidades comunes.

Independencia: En casi todos los casos los problemas suelen tener conceptos relacionados y teniendo en cuenta que estos conceptos se traducen en tipos de datos se deben identificar previamente las dependencias entre ellos para eliminar o reducir el impacto frente a los posibles cambios futuros.

Composición: La solución de un problema suele estar compuesta de soluciones específicas que usualmente son más pequeñas y que no necesariamente están dentro del mismo grupo que los tipos de datos de la solución total.

Unidades de datos: Se espera que los datos estén relacionados entre sí, de esta manera no se deberían tratar los datos de manera independiente sino como una entidad o unidad que encapsula la información, que debe corresponder a un concepto claramente identificado dentro del dominio del problema. Quien no ha vivido la tortura de mantener código en el cual todos los datos estan dispersos y en aumento? Analizar la información desde esta perspectiva nos da la posibilidad de crecer en la información de cada entidad o unidad de datos sin tener que alterar el comportamiento de la aplicación.

Tener en cuenta estos pequeños ítems puede complicarnos un poco el diseño, pero posteriormente puede evitarnos muchos problemas por lo vale la pena pensar en ellos.

Importancia del Reúso


Si los elementos de nuestro modelo tienen responsabilidades específicas y tienen una buena definición sobre sus dependencias se logra un buen balance en términos del reúso. Es posible que si un modelo de objetos se diseña sin tener en cuenta las características anteriormente mencionadas, se encuentre incompleto, es decir que frente a nuevas situaciones no se cuenten con funcionalidades previamente implementadas para resolver los nuevos problemas. Es por esto que debemos tener un grado de confianza frente a las funcionalidades existentes, preferiblemente inventariadas, de esta manera podremos saber con exactitud que funcionalidades se encuentran lo suficientemente completas para ser reutilizadas, de otra manera, se inicia el desastre ya que al no estar completo o acotado el modelo reutilizado puede sufrir modificaciones en el futuro y consecuentemente impactar las soluciones que dependen de dicho modelo.

El reúso es más que sólo utilizar componentes ya existentes en nuevas soluciones, se debe diseñar pensando en el reúso para poder crear las oportunidades a futuro, la idea aquí es muy clara, si se construyen soluciones específicas para un problema sin determinar las tareas comunes entre los diferentes problemas que lo componen, es probable que nunca podamos reutilizar un componente más allá de la simple casualidad.

Para incrementar el reúso en las aplicaciones se podría sacar partido de las jerarquías de objetos basadas en la herencia ya que estas nos permiten visualizar instancias de diferentes formas y de manera adicional proporcionan niveles de abstracción para la extensibilidad o el crecimiento. Como siempre todo en exceso es malo y estas jerarquías no son la excepción, pueden desencadenar problemas de rendimiento o si están definidas a la ligera se termina con muchas implementaciones vacías o con clases abstractas que lo resuelven todo por si solas y son difíciles de mantener. El reuso descontextualizado, aquel que se hace porque en alguna otra parte tambien se hace lo mismo, aquel que no define límites sobre las responsabilidades de los componentes suele terminar una desestabilidación continua de nuestros desarrollos en los que se corrige una funcionalidad y se dañan otras.

Concentrando responsabilidades


Es importante que existan en nuestras soluciones entes u objetos especializados en el procesamiento de cada tipo de información esto hace más fácil el mantenimiento de las aplicaciones ya que crea puntos específicos para la actualización de las características. Se debe evitar tener múltiples objetos que efectúan operaciones de la misma naturaleza, por ejemplo tener varias clases de acceso a datos tiene como consecuencia que frente a un requerimiento de un cambio específico en la forma de acceder a los datos abarcará múltiples elementos de la aplicación, creando la necesidad de modificar el desarrollo en varios puntos aumentando la posibilidad de agregar errores adicionales.