Checklist de requisitos no funcionales

Lunes, 15 septiembre 2014, 10:30 Deja un comentario

La presente entrada pretende ser un pequeño recopilatorio o lista de control de requisitos no funcionales. No es una entrada definitiva, y de hecho la idea es que vaya creciendo y actualizándose con el tiempo. Invito a todo el que quiera a que aporte correcciones, añadidos y observaciones.

Aunque me gustaría que fuese completa, no pretendo que sea exhaustiva. En las diferentes áreas y aspectos existen grupos de trabajo, estándares y procedimientos suficientemente completos si se quiere profundizar. Por ejemplo, el apartado de la seguridad, por su propia sensibilidad, está muy trabajado en este sentido y existen estándares como el  ISO 27002 o la iniciativa OWASP que contienen checklists y mecanismos de certificación muy completos y detallados. De igual manera, algunos aspectos como Usabilidad son especialmente ambiguos y poco detallados debido a mi falta de experiencia en ellos.

Requisitos

Un requisito es una propiedad física o funcional de un sistema o solución que representa la necesidad, con sus restricciones y detalles, que debe cumplir por diseño, para que cumpla el fin o utilidad con el que fue creado. Es, junto con el alcance, lo que nos dice “qué tiene que hacer y cómo” nuestra solución para que sea útil y tenga valor.

Si nos ceñimos a la norma ISO/IEC/IEEE 29148:2011 los requisitos deben ser:

  • Necesarios. Deben definir una capacidad, característica o restricción sin la cuál el objeto del proyecto o producto sería incompleto. Aunque también pueden documentarse como opcional, deseables, negociables…
  • De libre implementación. Aún siendo suficientemente completo su definición debería ser completamente independiente del modo en que se implemente. En la medida que independicemos la fase de diseño de la de análisis, daremos más libertad a la implementación y obtendremos un mejor producto.
  • Unívocos. Que su definición solo pueda interpretarse de una única manera.
  • Consistentes. Que no entren en conflicto con otros requisitos.
  • Completos. Describe de manera completa, sin deficiencias ni necesidad adicional de información, el requisito. También es necesario especificar el nivel y alcance del requisito, no es lo mismo que el sistema por diseño sea capaz de cumplir el requisito, que verificar de manera continuada y rigurosa que el requisito se cumple durante la explotación del sistema.
  • Singulares. La definición incluye un único requisito. Lo que luego nos puede ayudar a auditar y validar cada requisito de manera única y sencilla.
  • Posibles. Que sean técnicamente posibles, dentro de las restricciones propias del proyecto. Esto incluye que sean posibles bajo los análisis de riesgo o coste-beneficio.
  • Referenciable. Que pueda ser referenciado o identificado de manera única, ordenada y estructurada. Que tenga una codificación correcta y unívoca.
  • Verificables. Que existan mecanismos que permitan verificar que realmente el requisito se cumple y criterios definidos para validarlo

Aunque me gustaría también añadir una propiedad que se mencionaba en el estándar original de esta norma (IEEE 830)

  • Modificable/gestionable. Que existan mecanismos para, cuando sea necesario y dentro de los procedimientos y formalismos establecidos para la gestión del producto o proyecto, poder modificar la definición del requisito, su eliminación o la inclusión de nuevos requisitos siguiendo los criterios anteriores.

Los requisitos pueden ser funcionales o no funcionales:

  • Funcionales. Hacen referencia a funciones o resultados esperados del sistema, enfocados en resolver el problema o la operativa para el que fueron creados. Responden a la pregunta de qué debe hacer el sistema.
  • No funcionales. Hacen referencia a detalles, aspectos, atributos o propiedades del sistema enfocados en cómo se debe resolver el problema o la operativa objeto del sistema. Responden a la pregunta de cómo lo debe hacer el sistema.

Se podría decir que los requisitos funcionales representan la parte del mundo real que toca al sistema y los no funcionales la parte tecnológica.

El análisis completo de los requisitos funcionales es relativamente sencillo. Si se nos pasa alguno, es muy seguro que estemos olvidando una funcionalidad relevante del sistema. Los cambios, añadidos y eliminaciones de requisitos funcionales suelen tener impacto en el alcance del proyecto. Además, son aquellos donde el usuario o los no especialistas en tecnología aportan mucho más, siendo el feedback practicamente suyo por completo.

Sin embargo, hacer un análisis completo de los requisitos no funcionales es más complicado. Primero, porque los actores que más feedback pueden dar en el análisis de los requisitos no funcionales son necesariamente aquellos con conocimientos técnicos; y normalmente no están en el conjunto de actores que conocen la naturaleza del problema, sino en el de los analistas.

En segundo lugar y más importante, porque se confunden con facilidad con decisiones propias del diseño. Algo, que aparentemente, parece una restricción o exigencia técnica, es solamente un efecto de una decisión de diseño tomada, y su obligación se debe a la vinculación con este diseño. Si cambiamos el diseño, muy posiblemente el requisito cambie o pierda consistencia. Si un requisito no funcional puede cambiarse por otro o cambia si cambiamos el diseño, es muy probable que estemos hablando de una consecuencia del diseño de la solución, y no de un requisito como tal. Este aspecto es importante, porque los requisitos no deben condicionar la implementación (solo su resultado o producto). Por eso, el exceso de definición en este aspecto, no es todo lo beneficioso que pensamos.

Desgraciadamente un requisito no funcional pasado por alto puede tener efectos catastróficos en un diseño o implementación ya iniciados. Esta lista, como checklist que es, tiene como único objetivo facilitar que el análisis de requisitos sea completo y evitar que se nos pase por alto algún aspecto que podría ser útil a nuestro análisis. La gran mayoría de los requisitos de esta lista no van a aplicar ni tener sentido para el problema que estemos analizando, pero lo importante es que no se nos pase aquel requisito que sí es relevante, beneficioso o imprescindible.

Por último, el conjunto de los requisitos no funcionales tienen unos efectos en el reparto de la carga de trabajo de implementación que, por lo general, son mayores que el de los requisitos funcionales. Cada diferente requisito no funcional debe abordarse como una solución separada, heterogénea y poco acoplada con las de otros requisitos, con las consecuencias que esto tiene en el desempeño y en la curva de aprendizaje. Las plataformas más generalizadas como son Java o Microsoft .net, han infravalorado este aspecto de manera habitual. En Java suelen ser implementados por librerías de terceros, que aveces tienen su clon en versiones oficiales posteriores de la plataforma; en el caso de .net, es común que cambien bastante de versión en versión. Nuevas plataformas como Django o Ruby on Rails, se centran precisamente en la reusabilidad del código relativo estos requisitos con bastante acierto, facilitando y focalizando el trabajo del desarrollador en lo funcional.

Checklist

  • Seguridad
    • Seguridad física tanto de los sistemas como los medios de almacenamiento, líneas de comunicación, copias de respaldo.
    • Cifrado de datos, tanto en las comunicaciones, como medios de almacenamiento, copias de respaldo.
    • Auditabilidad, qué información y mecanismos existen para revisar la operativa de la solución, qué mecanismos garantizan la fiabilidad e integridad de esa información.
    • Control de acceso, cómo se autentican los usuarios, cuál es el modelo de autorización, cuál es el ciclo de vida de las credenciales y sesiones
    • Qué información está disponible en el acceso anónimo, qué damos a conocer de la organización, cómo protegemos nuestros sistemas del fingerprinting
    • Cómo protegemos la integridad de la información, cómo validamos que sea correcta, cómo impedimos su manipulación no autorizada
    • Cómo se gestionan los errores para que sean siempre controlados y no desvelen información no autorizada
    • Cómo impedimos la operativa no autorizada, cómo protegemos la manipulación de peticiones o servicios qué información es insertable mediante formularios, servicios o uploads de ficheros
    • Cómo se van a gestionar los incidentes de seguridad
    • Si nos vamos a ceñir a algún tipo de guía de seguridad o si va a ser necesario certificar el sistema contra algún estándar u obligación legal, bien por criterios de calidad o por exigencia externa
  • Capacidad y Escalabilidad
    • Qué niveles de rendimiento se necesitan:
      • Nivel mínimo aceptable para no mermar la experiencia de usuario
      • Número de usuarios concurrentes
      • Operaciones o transacciones por unidad de tiempo
    • Qué niveles de capacidad se necesitan:
      • Requisitos de memoria principal
      • Requisitos de memoria secundaria, terciaria y offline
      • Cachés de memoria intermedios
      • Requisitos de capacidad de la copia de respaldo
      • Tamaños de colas de mensajes y de servidores antes del desbordamiento o descarte de la petición
      • Número máximo de peticiones concurrentes por servicio
    • Si el sistema debe estar preparado para la escalabilidad vertical (más recursos por nodo) u horizontal (más nodos simultáneos por nivel)
    • Plan de capacidad o cómo se espera que evolucionen los requisitos anteriores con el tiempo
  • Disponibilidad
    • Qué componentes están replicados de manera activa o pasiva
    • Ciclo de vida de la información
      • Cómo discurre la información desde que se procesa hasta que se almacena en la copia de respaldo
      • En qué componentes se almacena de forma persistente y en cuáles volátil
      • Dónde se pueden introducir sondas para extraer información, bajo qué criterios o restricciones
      • Cómo y cuándo se historifica la información, cómo se borra, soft-delete (marcado del registro como borrado) o hard-delete (borrado físico de la información)
      • Qué estados de la información pueden considerarse estables o coherentes (¿un rollback destruye la transacción o la devuelve a un punto estable intermedio anterior?)
    • Cuál es el nivel de detalle o de precisión de la información
    • Si las operaciones y servicios son idempotentes o puras
    • La  copia de respaldo:
    • Plan de continuidad
      • Ante una caída de servicio cómo se espera continuar la actividad sin el sistema
      • Cuál es el tiempo máximo admisible de esta caída o cómo afecta a la actividad en función de este tiempo
    • Plan de recuperación
      • Ante una caída de servicio cómo se espera recuperar el sistema para que dé servicio
      • Cuál es el tiempo de recuperación estimado
  • Mantenibilidad
    • Qué arquitectura, framework, infraestructura de hardware o software base
    • Qué metodologías de trabajo
    • Qué estándares de codificación, nomenclaturas o convenciones
    • Qué herramientas de ciclo de vida de producto, de construcción del producto, de integración continua o de chequeo de software
    • Qué sistemas de versionado y nombrado de versiones, así como las políticas de versionado
    • Qué políticas de adopción/integración de librerías y de software de terceros
    • Qué políticas de gestión y configuración de entornos, qué políticas de gestión del cambio
    • Cómo se gestionan de logs y trazas y con qué herramientas
    • Qué políticas, procedimientos y herramientas de gestión de incidencias
    • Qué documentación se va a generar y en qué formato
    • Qué indicadores o estadísticas se monitorizan para conocer el estado o salud del sistema
  • Ciclo de vida
    • Cómo se despliega/repliega
    • Cómo se realizan las intervenciones o paradas del sistema
    • Cómo se realizan el upgrade de componentes, así como de los datos y parametrizaciones del aplicativo
  • Robustez y estabilidad
    • Cómo se gestionan los errores del sistema
      • qué información muestran al usuario
      • cómo se informa al usuario de la situación actual de su transacción y de cuáles son los siguientes pasos a seguir
      • qué información almacenan para la identificación y el análisis del error
    • Cómo se recupera el sistema de los errores, cómo vuelve a un punto estable parcial (punto de recuperación intermedio) o total
    • Cómo se gestionan los bugs desde su descubrimiento hasta su resolución, cómo se incluyen en las pruebas de regresión del sistema
  • Integridad y Consistencia
    • Qué es una transacción, qué componentes comprende, cuál es su alcance
    • Si se permiten faltas de sincronismo en la replicación de la información entre componentes (porque tarda en llegar de uno a otro) o de integridad (porque se permiten inconsistencias entre componentes)
    • Qué controles de integridad y coherencia de datos
  • Integración
    • Qué interfaces se definen con otras aplicaciones, de qué tipo
    • Qué componentes ya existentes en la organización se utilizan, cuál es la política de reusabilidad
    • Cómo de divide en módulos diferenciados, cómo se integran entre sí
  • Configuración
    • Cómo se configura y parametriza
    • Qué es parametrizable
    • Cuál es el alcance de cada parámetro: del sistema, por entorno, por usuario, por región…
    • Cuáles son los valores por defecto
  • Internacionalización
    • Cómo se soporta el uso de múltiples idiomas, qué idiomas se soportan, cómo se traduce la solución a otros idiomas
    • Cómo se parametrizan los diferentes aspectos regionales de formato numérico, fechas, aspecto…
    • Cómo se soportan las zonas y usos horarios, cómo se gestionan los cambios de horario, qué convenciones tiempo se utilizan
  • Legales
    • Cuál es la licencia o condiciones de uso de la aplicación
    • Cuáles son y cómo afectan las licencias y condiciones de uso del software de terceros
    • Qué requisitos o exigencias legales deben tenerse en cuenta
  • Usabilidad
    • Navegabilidad
      • Cuál es el mapa de navegación
      • Cuáles son los criterios de navegación (adelante, atrás, histórico, menus, error…)
      • Cómo se indica en todo momento en qué lugar nos encontramos
    • Ayuda en pantalla
      • Cuál es el nivel de detalle de esa ayuda
      • Cuál es la terminología utilizada
      • Cómo se referencia a las diferentes partes del manual o documentación
    • Aspecto
      • Colores, fuentes y maquetación
        • Cómo se usan para que estén acorde al sentido o significado de lo que muestran
        • Guía corporativa de diseño y estilo
        • Cómo se adapta el interfaz a dispositivos con poco tamaño o monocromo
      • Contenido
        • Cómo se maqueta y distribuye
        • Cómo es de legible el texto
        • Cómo se introducen y validan valores y formularios
        • De dónde se pueden obtener imágenes (banco corporativo de imágenes)
    • Responsible design
      • Cómo se adecua, transforma y maqueta el contenido en los diferentes dispositivos y tamaños
    • Qué estándares de formateado o visualización se siguen
    • Qué cliente se utiliza
      • Qué navegadores son soportados
    • Usabilidad
      • Cuál es el glosario de términos y comandos
      • Cómo se informa al usuario del estado actual de su operación:
        • En espera o ejecutando una acción, del estado  de dicha operación y del tiempo estimado de espera
        • En la llegada de eventos
        • Del resultado de una operación
      • Cómo se avisa de errores, alertas y se solicitan confirmaciones
      • Cómo se adapta el UI para mostrar las opciones posibles
      • Cómo se definen los atajos y el uso del teclado
      • Cómo se define el uso del ratón o de un dispositivo multitáctil
      • Cómo se habilita el deshacer/rehacer de operaciones, cómo se gestiona ese default

Referencias

http://leadinganswers.typepad.com/leading_answers/2009/03/nonfunctional-requirements-minimal-checklist.html

http://thoughts-agile.blogspot.com.es/2013/09/non-functional-requirements.html

http://www.eyefodder.com/2011/06/quality-software-non-functional-requirements.html

http://www.utdallas.edu/~chung/RE/IEEE830-1993.pdf

Licencia Creative Commons
Esta entrada de Juan Francisco Adame Lorite está publicada bajo una licencia Creative Commons Atribución-CompartirIgual 3.0 Unported.
Anuncios
Categorías:Arquitectura, Desarrollo

Ejemplo de arquitectura escalable

Domingo, 24 agosto 2014, 23:13 Deja un comentario

La gente de Octivi, a través de su arquitecto Antoni Orfin, nos hablan de cómo han conseguido servir mil millones de peticiones a la semana con una arquitectura relativamente sencilla basada en HAProxy, PHP, Redis y MySQL para un servicio de e-commerce.

El artículo está muy bien explicado, detallando la situación inicial, el por qué de cada decisión y el papel de cada componente. Muy interesante el hecho de que Redis sea quien realmente provee de datos al aplicativo, mientras MySQL es tan solo una capa de persistencia.

The Easy Way Of Building A Growing Startup Architecture Using HAProxy, PHP, Redis And MySQL To Handle 1 Billion Requests A Week

 

Categorías:Arquitectura, Sistemas, Web

The architecture of StackOverflow

Martes, 22 julio 2014, 0:08 Deja un comentario
Categorías:Arquitectura

Configuration Management: Puppet

Viernes, 20 junio 2014, 10:24 Deja un comentario

DataCenter

De entre las tareas más tediosas de la puesta en servicio se encuentran la actualización y configuración de los diferentes entornos. Estas tareas requieren  la preparación de equipos con mucha experiencia y conocimiento tanto de los entornos técnicos como de lo entornos organizativos en que nos movemos. Pero una vez materializado ese conocimiento son tareas repetitivas y largas, y por lo tanto, muy propensas a error. Además de documentar y actualizar muy bien la situación de los entornos, es imprescindible automatizar estas tareas. No ya solo para evitar errores, sino también para escalar e igualar cuando el conjunto de entornos sea muy numeroso.

Para esta finalidad existen muchas soluciones. De las más conocidas en el mundo Open Source:

Todas son soluciones y basadas en código abierto (licencias Apache y GPL según cada caso), pero con una versión o servicio más completo de pago (modelo Freemium). Las más extendidas son las dos primeras, en especial Puppet que es la que tiene una comunidad más extensa, y por lo tanto una cobertura con módulos de sistemas y entornos mayor.

Puppet tiene un lenguaje de configuración propio basado en Ruby (aunque también permite el uso de Ruby directamente). La primera particularidad es que Puppet recibe una configuración de entorno final o definitiva y se encarga de decidir qué pasos tomar para llegar a ella; nosotros no definimos los pasos, sino el objetivo. Esto nos permite aplicar la misma configuración a entornos con configuraciones distintas, Puppet se encargará de igualar los diferentes entornos. Estas configuraciones son lo que Puppet llama Manifiest y se materializan en ficheros .pp.

Los ficheros .pp contienen descripciones de recursos. Cada recurso es una estructura con tipo, título y atributos que describe el estado final de un cierto elemento de configuración del entorno donde se aplica el manifiesto. Los recursos tienen una tipología propia y una estructura en clases para abarcar el conjunto de elementos o aspectos configurables en un entorno (servicios, ficheros, parametrizaciones del sistema…). Estas tipologías pueden extenderse con módulos específicos para cada elemento de software configurable (la lista se encuentra aquí). Los atributos son los valores que describen la configuración de dicho recurso. El título es el atributo que identifica al recurso de entre los de su clase (en el ejemplo: el path del fichero o el nombre del servicio). El siguientes es un ejemplo de manifiesto para la configuración de un Apache:

class apache {
  package {
    apache:
    ensure => installed
  }

  file {
    "httpd.conf":
    mode => 644,
    owner => root,
    group => root,
    path => "/etc/apache2/httpd.conf",
    source => "puppet://modules/files/httpd.conf",
  }

  service {
    apache2:
      ensure => true,
      enable => true,
      subscribe => [File["httpd.conf"], Package[apache]],
  }
}

En el manifiesto anterior el estado final de configuración es aquél en el que se ha instalado el paquete de Apache, se ha copiado el fichero de configuración del repositorio de Puppet (se puede utilizar cualquier otra localización) y se ha habilitado el servicio. Nótese que el atributo, en este caso metaatributo, subscribe indica que el recurso servicio va a ser informado de los cambios que se produzcan en los otros dos recursos; de esta manera se puede establecer orden o dependencia entre ellos. Los manifiestos pueden gestionar variables, variables de entorno, así como hacer uso de estructuras de control o de dependencia u orden.

Puppet puede instalarse con una estructura de agente y maestro, donde el maestro o maestros gestionan la configuración de los agentes, que están en los entornos configurables y que toman (pull) los cambios de los maestros. O bien, como un maestro independiente, que gestiona de manera autónoma el entorno en que se encuentra. Las consultas y aplicaciones de configuraciones se hacen mediante el comando puppet del símbolo del sistema, aunque la versión Enterprise tiene una UI que facilita estas operaciones.

En las referencias incluyo el enlace a la documentación de Puppet (bastante completa en mi opinión), por si quieres conocer más acerca de él.

Referencias

Comparativa en Wikipedia

Review en InfoWorld

Introducción a Puppet en Puppet Labs

La foto ha sido tomada de aquí, bajo licencia CC

 

Licencia Creative Commons
Esta entrada de Juan Francisco Adame Lorite está publicada bajo una licencia Creative Commons Atribución-CompartirIgual 3.0 Unported.

Categorías:Sistemas

Tiempo y zonas horarias en aplicaciones

Domingo, 1 junio 2014, 1:39 1 comentario

Imagen

Siempre me he encontrado con la duda de qué estándar de tiempo utilizar en mis aplicaciones o si debo hacer uso de manera explícita del uso horario o no. Así que he decidido analizar el asunto.

Empecemos distinguiendo los estándar de tiempo de las zonas horarias:

  • Los estándar de tiempos son los convenios establecidos para la medición de duración de tiempos o el establecimiento puntual de eventos en el tiempo. Es muy importante saber para qué vamos a utilizar o para qué necesitamos la medición de tiempos en cuestión. No es lo mismo que queramos definir momentos concretos en el tiempo, bien pasado o futuro; o medir duración de tiempo entre momentos específicos. Para cada caso nos convendrá un estándar u otro.
  • Los husos o zonas horarias o tiempos civiles son los tiempos definidos de manera legal en un territorio. Toman como referencia un estándar de tiempo (hoy en día UTC) y difieren de este un tiempo definido según el meridiano de la región. Pueden adoptar o no, cambios por horario de verano.

A continuación veamos los tipos de estándar de tiempo:

  • Estándares de tiempo basados en la rotación de la Tierra. Son aquellos en los que la posición astronómica de la Tierra y su medición indican cada momento puntual de tiempo o la duración del mismo.
    • GMT (Greenwich Mean Team). Fue el primero en establecerse. Se sincroniza con la hora medida en el Real Observatorio de Greenwich (a las afueras de Londres) que coincide con el meridiano 0 o de Greenwich. Se calcula mediante la duración media del día medida entre dos pasos del Sol por el meridiano. Fue sustituido por UTC desde 1972. Por cierto, las Islas Británicas solo coinciden con GMT durante el invierno.
    • UT (Universal Time). Definido con posterioridad a GMT, pero con su misma filosofía. Su medición, sin embargo, se basa en el posicionamiento diurno de las estrellas y no del Sol, lo que lo hace más exacto. Existen varios UT: UT0, UT1, UT1R y UT2. UT0 es la versión que más se ciñe a la definición de UT. UT1, es el más usado y en el que se basa UTC, incluye sobre UT0 las variaciones debidas al movimiento del eje de rotación de la Tierra. UT1R incluye las variaciones debidas a las mareas. UT2 es un suavizado periódico de UT1.
  • Estándares de tiempo construidos. Son aquellos que se sincronizan mediante eventos de duración definida:
    • TAI (International Atomic Time). Estándar de tiempo desde 1972 para la definición del segundo. Se creó para evitar las derivas de tiempos y las irregularidades propias de la rotación y traslación de la Tierra. El tiempo TAI se calcula mediante la sincronización de cerca de relojes atómicos en todo el mundo. Difiere de UTC en un número definido de segundos (los segundos intercalares añadidos o restados hasta la fecha desde su establecimiento).
    • UTC (Coordinated Universal Time). Es el estándar de tiempo más utilizado. Está basado en el TAI pero con una diferencia de segundos definida y conocida. Estos segundos son los segundos intercalares añadidos o restados (hasta la fecha siempre se han añadido) para evitar que la hora UTC no difiera de la hora UT1 de Greenwich más de 0,9 segundos.
    • GPST (GPS Time). Utilizado por el sistema de posicionamiento GPS. Está retrasado sobre TAI de manera constante 19 segundos. El mensaje de posicionamiento GPS incluye el número de segundos intercalares con UTC, para que las unidades GPS puedan calcular la hora UTC.
  • Estándares de tiempo basados en el movimiento de planetas. Son aquellos en los que la posición y el movimiento de los plantas indican cada momento puntual de tiempo o la duración del mismo. El más utilizado era el Tiempo de Efemérides, calculado a partir de la traslación de la Tierra alrededor del Sol, ya en desuso debido a que no tenía en cuenta las tesis relativistas del tiempo. Hoy en día existen otros que sí tienen en cuenta estos factores, como el Terrestrial Time que sustituyó al Tiempo de Efemérides.

Conclusiones

De manera general, podemos decir que el estándar de tiempo más apropiado a utilizar es UTC para el almacenamiento de información temporal. Formateando o capturando la información para adaptarla a la zona horaria del usuario en el momento de su procesado. Todos las zonas horarias están referenciadas a este estándar y la conversión hacia o desde estas es inmediata.

Sin embargo cuando queramos calcular duraciones de tiempo a futuro con precisión, no podremos utilizar UTC, ni cualquier otro sistema basado o sincronizado en cálculos astronómicos; puesto que no podemos conocer las variaciones de tiempo o segundos intercalares que se aplicarán a futuro a un estándar de tiempo. En ese caso es preferible utilizar TAI. Esta observación no aplica para el procesado de fechas: el evento en cuestión seguirá ocurriendo en dicho momento con independencia de las variaciones.

El almacenamiento y cálculo de tiempos y fechas con zonas horarias aplicadas no puede considerarse una buena práctica. En primer lugar, las zonas horarias con cambio de horario en verano no son causales (un evento posterior a otro puede aparecer como anterior si entre medias se produce el cambio de horario de otoño, el retraso de una hora), lo que dificulta el cálculo de duraciones de tiempo.En segundo lugar, dificulta la internacionalización de soluciones o la compartición de diferentes ámbitos regionales en una solución (aplicación usada en países con diferentes zonas horarias o usuarios que viajan entre zonas horarias); sobre todo si no se ha tenido en cuenta en el diseño inicial de la solución. El argumento de la mejora de rendimiento no suele ser válido para defender esta posibilidad tampoco, puesto que los cálculos para el cambio de zonas horarias de tiempos son inmediatos (por lo general, suma de un número entero de horas).

Hasta Windows 2000 los sistemas Windows almacenaban la hora local referenciada a la zona horaria establecida en el equipo o usuario. Desde Windows 2000 y en los sistemas UNIX-like, se almacena UTC (aunque se formatee a la zona horaria del usuario cuando se muestra).

Referencias

La foto está sacada de aquí.

Time Standard en Wikipedia 
GMT vs UTC

Licencia Creative Commons
Esta entrada de Juan Francisco Adame Lorite está publicada bajo una licencia Creative Commons Atribución-CompartirIgual 3.0 Unported.

Categorías:Desarrollo

Consejos y buenas prácticas del logging de aplicaciones

Martes, 2 octubre 2012, 23:52 1 comentario

Hemos ido asumiendo con el tiempo que aspectos como la seguridad y las pruebas deben de tenerse en cuenta durante todo el ciclo de vida del proyecto, desde sus orígenes hasta su finalización. Que no son requisitos no funcionales deseables o secundarios, si no que son prioritarios como parte de la estructura fundamental del aplicativo (hace no tanto eran funcionalidades o mecanismos que se diseñaban e implementaban a la finalización del proyecto como un añadido, si se hacían). Sin embargo la traza de aplicación, aún habiendo hecho méritos, no ha recibido en muchos casos la misma consideración ;-P.

En esta entrada no creo que os descubra nada nuevo a los que tengáis experiencia en el desarrollo. La idea es hacer un ejercicio de síntesis y revisión para resumir lo que, en mi opinión, son las mejores o las buenas prácticas del log de aplicación. Por supuesto, las aportaciones, observaciones, matizaciones y críticas son bienvenidas; os invito a que os unáis a este ejercicio.

El log o traza de aplicación es el procesado y almacenamiento de información relativa a la ejecución de una aplicación. Contiene datos de entidades, cambios de estado y componentes software involucradas en dicha ejecución. Su principal funcionalidad es facilitar el seguimiento o análisis de la ejecución de la aplicación:

  • Analizar el comportamiento de la aplicación durante la fase de desarrollo y depuración (pruebas de caja blanca)
  • Analizar los bugs o errores de ejecución detectados, sus causas y consecuencias
  • Servir de registro de auditoría cuando la información contenida y el modo en que se ha procesado cumpla los criterios requeridos
  • Medir el rendimiento o carga de los sistemas o aplicaciones
  • Revertir el estado del aplicativo siguiendo en orden inverso el log

Aunque depende de las circunstancias en las que nos encontremos, el segundo de los usos suele ser el más relevante. Un buen log desarrollado correctamente en el código y mantenido o configurado en explotación es una garantía de respuesta rápida para análisis de errores; que incluso podrían hacerse sin necesidad de parar el aplicativo, reconfigurarlo o aplicarle ningún cambio.

La traza de aplicación suele formarse por un conjunto de eventos que se almacenan secuencialmente, por lo general en el orden en que suceden, de manera persistente o recuperable. Se pueden almacenar en ficheros, en BBDD, en componentes distribuidos a tal efecto… Se pueden habilitar mecanismos de rotación o historificación de estos logs, se pueden utilizar por monitores para lanzar alertas, se pueden integrar y fusionar para hacer análisis más exhaustivos… Lo relevante es que la información registrada y la forma en que se gestiona sea útil.

Existen numerosas soluciones y propuestas software, tanto libres como propietarias, más o menos estandarizadas, más sencillas o más completas, de mil tipos y formas. Lo importante es buscar aquella se ajusta a nuestras necesidades y entornos, para olvidarnos de la implementación del mecanismo por completo; y ceñirnos a los dos aspectos más importantes:

  • El contenido de cada registro o evento, principal preocupación del desarrollador
  • El modo en que se procesa, persiste y gestiona, principal preocupación de la explotación del sistema o aplicativo

El coste de implementación del logging se encuentra en el ir, mientras se desarrolla, dejando traza en los diferentes puntos del código. Esta actividad debe hacerse durante el desarrollo, siguiendo patrones, criterios y procedimientos preestablecidos. De esta manera los desarrolladores tendrán criterios comunes y la traza será coherente entre las diferentes partes del código.

La traza y la documentación del código pueden tener puntos en común en cuanto a su filosofía, objetivos y modo de aplicación, pero existe una diferencia importante entre ambos: mientras la documentación del código se adentra en el por qué se hace algo de esa manera, la traza debe cubrir el qué se hace. Por poner un ejemplo, en un bucle la documentación del código debería indicar por qué los límites o condiciones de salida son tales y la traza registrar durante la ejecución en qué punto se cumplió dicha condición de salida.

La información registrada en la traza debe ser relevante y completa. Se pueden considerar los siguientes aspectos a la hora de decidir qué información incluir:

  • Qué:
    • Qué evento o acción ha ocurrido.
    • Qué entidades han estado involucradas.
    • Si hay un cambio de estado, ¿cuál era el anterior? ¿cuál es el nuevo estado?.
  • Dónde:
    • En qué punto del código ha ocurrido: componente, clase, fichero de código, método o bloque de ejecución, línea de código… Cuanto más detallada sea esta información mejor para localizar el lugar del posible error o por donde ha pasado la ejecución, por un lado; pero más puede afectar el logging al rendimiento (se podría requerir información de debug) y a la concrección de la información de la traza, por otro.
  • Cuándo:
    • Registrando el momento temporal, bien absoluto o bien relativo al comienzo de ejecución o cualquier otro evento.
    • Generando una traza secuencial o causal, en la que los eventos que ocurren antes en el tiempo o que ocasionan otros, aparezcan antes.
  • En qué contexto:
    • Registrando estados o variables: propios de la ejecución (parámetros), de personalización o específicos de usuario, referentes a la sesión o transacción en ejecución…
    • Indicando hebras, transacciones o peticiones relacionadas cuando estemos en entornos concurrentes.

Para que la información de la traza sea más detallada en momento de análisis y más manejable durante la explotación de la misma, se establecen niveles de filtrado. De tal manera que solo se muestra o almacenan aquellos eventos con un nivel mayor o igual al del nivel de traza establecido. Las librerías de logging permiten filtrar los eventos por otros criterios como son la clase o contexto del evento, también.

Los niveles más comunes son DEBUG, INFO, WARNING y ERROR. La clasificación de los diferentes eventos en cada nivel es parte del ejercicio de análisis, y deben orientarse a que la traza sea legible y útil en los diferentes contextos del aplicativo, desde el desarrollo hasta la explotación.

Este puede ser un ejemplo de semántica de niveles de logging:

  • DEBUG, para información de muy bajo nivel solo útil para el debug de la aplicación, tanto en el desarrollo como en el análisis de incidencias
    • Llamadas a funciones y procedimientos y otros componentes, con parámetros y respuestas
    • Flujos de ejecución
    • Desarrollo de algoritmos y procedimientos que permitan identificar y seguir su ejecución en desarrollo
  • INFO, información de más alto nivel que permita hacer un seguimiento de la ejecución normal
    • Paradas y arranques de servicios y sistemas
    • Parámetros críticos o relevantes de configuración
    • Comienzo y fin de transacciones y operaciones completas
    • Cambios de estado de operaciones
  • WARN, información de situaciones, que aún sin ser de error, si son anómalas o no previstas, aunque el aplicativo tiene alternativas para solventarlas
    • Parámetros no definidos, y cuyo valor se toma por defecto
    • Situaciones anómalas, pero que son resueltas por el aplicativo, dejando la operación en un estado correcto
    • Funcionalidades no primordiales o imprescindibles, que no pueden resolverse, pero que dejan la operación en un estado correcto
  • ERROR, información de situaciones que son de error y que impiden la ejecución correcta de una operación o transacción, pero sin afectar a otras operaciones o transacciones (error aislado o contenido)
    • No se pudo realizar una operación o transacción, pero no afecta a otras
    • Peticiones o consultas erróneas (almacenando los parámetros de entrada)
    • Funcionalidades generales del aplicativo, que aún afectando al funcionamiento general del aplicativo, no se consideran primordiales o imprescindibles
  • FATAL, información de situaciones de error que afectan al funcionamiento general del aplicativo (errores no aislados o contenidos en alcance)
    • Parámetros no definidos o configuraciones erróneas
    • Falta de conexión o comunicación con otros componentes
    • Errores de ejecución que pueden afectar a operaciones o transacciones independientes, o que afectan al funcionamiento general de la aplicación

Tanto el contenido y forma de cada evento de log, como la semántica de los niveles son parte del diseño del aplicativo. Estos criterios deben definirse y ser comunicados al equipo de desarrollo para que se apliquen de manera homogénea y coherente en todo el desarrollo. Si estos criterios son consensuados con el equipo se puede sacar mucho provecho de la experiencia de los desarrolladores.

Otras recomendaciones a tener en cuenta son:

  • Registrar los eventos de manera atómica, que toda la información referente a un evento se almacene en un registro o línea.
  • Orientar el formato de la información mostrada a poder ser usado de manera informatizada o automática con herramientas específicas.
  • En lo referente a excepciones:
    • Mostrar siempre la traza completa de la excepción con su mensaje y todo el stacktrace.
    • Si la excepción es capturada, tratada y luego lanzada de nuevo (bien la misma u otra) no dejar traza de la excepción hasta que se capture y se trate en última instancia. De esta manera cada excepción solo se mostrará una vez. Si no lo hacemos así y registramos cada captura y relanzamiento aparacerá en la traza varias veces y no sabremos si es la misma o son varias. (Antipatrón catch-log-throw).
    • Hacer uso de la propiedad de excepción causante o interna cuando capturemos y relancemos la excepción. En Java es el método getCause(), en .NET la propiedad InnerException.
  • Hacer uso de mecanismos de MDC/NDC (información de contexto) para entornos de múltiples hebras o ámbitos de transacción. Esta información nos puede permitir filtrar o concretar la información de log  en contexto de negocio específicos como pueden ser clientes, usuarios, productos…
  • Implementar el método toString() de todas las clases de negocio o POJOs para facilitar su traceado.
  • Hay que tener en cuenta las diferencias y zonas horarias a la hora de trabajar con logs de varias máquinas o componentes.
  • El logging no puede tener efectos laterales, no puede modificar el estado de ningún parámetro, variable o procedimiento. Solo muestra información. Debe ser ligero, el propio logging no puede requerir de procesamientos largos o costosos.
  • Las llamadas a métodos, componentes y componentes distribuidos externos, deberían registrarse tras la llamada incluyendo los parámetros de entrada y la respuesta, así como clase/componente y método, a nivel de DEBUG. Y a nivel de ERROR y FATAL cuando ocurra un error en la llamada (si fuese una excepción se debe tracear parámetros de entrada y excepción)

Un saludo,

Juan Francisco Adame Lorite

Licencia Creative Commons

Categorías:Desarrollo Etiquetas:

Los pequeños detalles sí importan

Domingo, 23 septiembre 2012, 23:24 Deja un comentario

He descubierto hace poco un blog muy sencillo y minimalista referente a los interfaces de usuario y a ejemplos de detalles de calidad que se pueden encontrar en muchos sitios y software. Se llama Little Big Details.
Cada entrada hace referencia a un ejemplo de característica o funcionalidad de UI, a modo de pildorilla, que muestra una especial calidad o cuidado en su diseño y ejecución y que nos puede servir de ejemplo para cuando nos toque diseñar uno.
Una buena idea y muy práctica.

Categorías:Desarrollo, UI, Web Etiquetas:
A %d blogueros les gusta esto: