October 2007 - Artículos

Hace un par de dias un amigo me hizo una consulta al paso, "Sabes bien como se formatea un decimal nullable?". La verdad es que me molestó no tener la respuesta y me sorprendió que él tampoco lo supiera y que me consultara a mi en vez de googlear!. 

Claro, por ser nullable no tiene el método ToString(), así que vamos a compartir las dos soluciones encontradas que, aunque muchos pueden conocerlas, muchos no.

La clase genérica Nullable<T> posee dos método:

C# Code
public T GetValueOrDefault()
{
    return this.value;
}

y,

C# Code
public T GetValueOrDefault(T defaultValue)
{
    if (!this.HasValue)
    {
        return defaultValue;
    }
    return this.value;
}

También contamos con el operador ?? como sigue:

C# Code
(decimalNullable ?? 0).ToString("$ 0###.####");

 

El siguiente segmento de código muestra lasformas de hacer lo mismo:

C# Code
namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            decimal? decimalNullable = null;
            string formated;

            formated = decimalNullable.GetValueOrDefault().ToString("$ 0###.####");
            formated = decimalNullable.GetValueOrDefault(0).ToString("$ 0###.####");
            formated = (decimalNullable ?? 0).ToString("$ 0###.####");

            // Esta linea lanza una exception InvalidOperationException
            formated = decimalNullable.Value.ToString("$ 0###.####");
        }
    }
}

Lucas Ontivero

Publicado 26/10/2007 0:31 por Lucas Ontivero | con no comments
Archivado en: ,

En esta serie voy a explicar todo lo referente a la creación de sistemas de plug-ins, pasando por su arquitectura, aspectos de isolation, seguridad, etc.

Introducción

No hace falta que explique aquí que es un plug-in, ¿verdad?. Así que comenzaré planteando un ejemplo y listando algunas características que todo sistema que soporte plugins debe tener.

Un ejemplo

Para esta entrada he creado un servicio de windows el cual vigila un directorio específico a la espera de cualquien cambio en él, como por ejemplo, la creación de un nuevo archivo, la eliminación de uno existente, renombrado de archivos, cambios en sus propiedades, tamaños, fechas de último acceso o escritura, etc.

Este servicio es sumamente útil para tareas de integración de aplicaciones, al mejor estilo Biztalk Server. La idea es que una vez detectado un cambio en un archivo del directorio que hemos especificado, este servicio recorra la lista de plugins instalados y les comunique el evento de modo que cada uno de ellos realizen la acción para la que fueron desarrollados. Estas acciones podrían ser:

  • Mediante EventLogger, mantener un tracking de los cambios en los archivos,
  • Enviar un email con el contenido del archivo (si es de texto claro),
  • Implementar una segunda papelera de reciclaje,
  • Mantener versiones de los archivos,
  • Organizar los archivos que bajamos de interner en subcarpetas según ciertos criterios,
  • Salvar el contenido de un nuevo archivo en una base de datos o desencadenar algún otro proceso,
  • Hacer de demonio de impresión,
  • etc.

El código de ejemplo se encuentra aquí: FileSystemWatcherService.zip

Características

Todo software que soporte plug-ins debe considerar os siguientes puntos importantes:

  • Isolation: cada plugin debe cargarse y correr en un AppDomain separado. El plugin no debe tener acceso al host o debe estar bien planeado, en el ejemplo FileSystemWatcherService.zip la comunicación es unidireccional HOST->PLUGIN.
  • Tolerancia o manejo de fallos: cuando un plugin no funciona o lanza excepciones no esperadas debe descargarse.
  • Seguridad: el plugin debe tener los mínimos privilegios necesarios para correr. Estos privilegios varian según el tipo de aplicación que estamos haciendo, por ejemplo, si en este ejemplo los plugins corrieran bajo un Internet Security Zone, ninguno de las tareas que arriba listo podrían realizarse pero lo cierto es que la mayoria de los plugins no deberían tener más que esos privilegios y en su defecto solo permitirles ejecutarse y nada más.
  • Detección de nuevos plugins o nuevas versiones de los instalados: no es buena idea tener que reiniciar una aplicación o un servicio para instalar un nuevo plugin. En muchos casos, como el de ejemplo nuestro ejemplo, la instalción de nuevos plugins se hace vigilando un directorio "\Plugins" para detectar cambios e los archivos ".dll" que contiene. Cuando detecta algún cambio simplemente descarga los plugins instalados y los carga de nuevo. Esto evita además el tener que realizar cambios en los archivos de configuración o en el registro de windows. Esta es a mi manera de entender, la mejor opción para manejar la carga de plugins. Otros productos trabajan de igual manera como LiveWriter y Notepad++.

 

Arquitectura

Isolation

Como dijimos arriba, es importante que cada plugin corra en un AppDomain propio separado del AppDomain del Host. Para desacoplar dos partes de un sistema, ambas deben conocer y respetar ciertos contratos, es decir que las dos partes deben compartir un conjunto de interfaces. Este patrón se llama Service Interface cuyos componentes se pueden ver en la figura de abajo.

ServiceInteface 
Tomado de MSDN->MSDN Library->Servers and Enterprise Devepment->Enterprise Architecture, Patterns and Practices->Microsoft patterns and practices->Guides->Enterprise Solution Patterns Using Mricrosoft .NET->Service Patterns

Para decirlo de manera facil, tanto el Consumer como el Provider comparten al menos un assembly con las interfaces (contratos). En la figura de arriba también podemos ver un Service Gateway que implementa la cumunicacion desde el Customer hacia el Provider. A diferencia de como se implementa en la página a la que hago referencia, mediante WS, en el ejemplo que proveo lo hago mediante una clase Gateway que hereda de MarshalByRefObject en el assembly compartido FileSystemWatcherService.Interfaces.dll.

Básicamente existe una interface que debe implementar todo plugin, la interface IPlugin la cual posee un método Execute(IMessage message). El código:

C# Code
namespace FileSystemWatcherService.Interfaces
{
    public interface IPlugin
    {
        bool Execute(IWatchMessage message);
    }
}

Mientras que el Gateway se implementa mediante una clase "Gateway" que hereda de MarshalByRefObject para poder cruzar los límites del AppDomain. Esta clase contiene solo un método LoadPlugin que dado el nombre de un assembly, lo carga y mediante reflection busca un tipo que implemente la interface IPlugin y que esté decorado con el atributo PluginAttribute. Si encuentra un tipo que satisfaga estas condiciones, crea una instancia de este y retorna un objeto PluginInfo que contendrá una referencia al plugin e información del mismo que se detalla mediante el PluginAttribute.

C# Code
using System;
using System.Reflection;

namespace FileSystemWatcherService.Interfaces
{
    public sealed class Gateway : MarshalByRefObject
    {
        public PluginInfo LoadPlugin(string assemblyFilename)
        {
            Assembly dynamic = Assembly.LoadFrom(assemblyFilename);
            Type typeofIPlugin = typeof(IPlugin);
            foreach (Type type in dynamic.GetExportedTypes())
            {
                if (typeofIPlugin.IsAssignableFrom(type))
                {
                    PluginAttribute pluginAttr = null;
                    foreach (Attribute attr in type.GetCustomAttributes(false))
                    {
                        pluginAttr = attr as PluginAttribute;
                        if (pluginAttr != null) break;
                    }

                    if (pluginAttr == null) 
                        throw new PluginInformationMissing();
                    
                    ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
                    if (constructor != null)
                    {
                        IPlugin instance = (IPlugin)constructor.Invoke(null);
                        return new PluginInfo(instance, pluginAttr); 
                    }
                }
            }
            return null;
        }
    }
}

El Plugin

por ejemplo, aquí va un plugin sencillo que mantiene un registro de los archivos txt que han cambiado en el registro de eventos de windows.

C# Code
using System;
using System.Diagnostics;
using FileSystemWatcherService.Interfaces;

namespace Logger.Plugins
{
    [Serializable]
    [PluginAttribute("Logger", Mask="*.txt", Description="Log *.txt file changes ")]
    public class Logger : IPlugin
    {
        private readonly EventLog eventLogger;

        public Logger()
        {
            eventLogger = new EventLog();
            eventLogger.Log = "Logger plugin";
            eventLogger.Source = "LoggerPlugin Events";      
        }

        public bool Execute(IWatchMessage message)
        {
            try
            {
                eventLogger.WriteEntry(
                    string.Format("El archivo '{0}' fue tocado", message.FileName), EventLogEntryType.Information, 0, 0);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}

Esto es todo lo relevante en cuanto al Provider y al Service Interface. Ahora veamos un poco la arquitectura necesaria del lado del Consumer.

La aplicación cliente

Esta es la parte más interesante!. Y se compone de las siguientes clases que implementan funcionalidades necesarias. Aunque existen, como siempre, muchas formas de implementar los mismo, yo propongo la siguiente.

Las clases y sus funcionalidades:

Clases Responsabilidades
PluginLoader Un singleton encargado de realizar la primera carga de los assemblies (plugins) y de vigilar los eventos sobre la carpeta "\Plugins" y cuando detecta un cambio descargar los plugins y los cargalos de nuevo. Esta clase mantiene una instancia de PluginsContainer.
PluginsContainer Es una colección de PluginControllers que se encarga de mantener la lista de plugins cargados, registrar y desregistrar/descargar plugins.  Este es el encargado de recorrer la lista de plugin controllers y pedirles que invoquen a los plugins.
PluginController Mantiene una referencia al plugin como así también información sobre el nombre del assembly que lo contiene y el AppDomain en donde se cargó. Esta clase también es la encargada de invocar al método Execute() del plugin con el mensaje adecuado y debería ser capaz (no lo es en este ejemplo) de controlar el tiempo de ejecución del mismo. Controla también que el Plugin no dispare una excepción y si lo hace simplemente descarga el AppDomain en el que se encuentra el Plugin.

El resto de las clases son de soporte. Como ConfigurationSectionHandler  para leer el archivo de configuración, DebugHelpers helpers para depurar principalmente para vigilar que la aplicación no cargue los assemblies en el AppDomain del Host, FileMaskHelper  para chequear si el archivo que cambió concuerda con la máscara que puede manejar un plugin, y Program es el entry point de la aplicación.

En el siguiente código hay que notar que luego de crear el AppDomain se obtiene un proxy trnsparente de la clase Gateway mediante:
CreateInstanceAndUnwrap("FileSystemWatcherService.Interfaces", "FileSystemWatcherService.Interfaces.Gateway");

Para más información (aunque no mucha) vea la documentación de este método. Lo único que hay que resaltar es la siguiente nota encontrada en la documentación:

Note:

If you make an early-bound call to a method M of an object of type T1 that was returned by CreateInstanceAndUnwrap, and that method makes an early-bound call to a method of an object of type T2 in an assembly C other than the current assembly or the assembly containing T1, assembly C is loaded into the current application domain. This loading occurs even if the early-bound call to T1.M() was made in the body of a DynamicMethod, or in other dynamically generated code. If the current domain is the default domain, assembly C cannot be unloaded until the process ends. If the current domain later attempts to load assembly C, the load might fail.

 

C# Code
        private static PluginController LoadAssembly(string assemblyName)
        {
            AppDomain pluginAppDomain = null;
            try
            {
                pluginAppDomain = AppDomain.CreateDomain(assemblyName);
                DebugHelpers.AssembliesInAppDomain("Before create AuxiliaryDomain");
                Gateway gateway = (Gateway)pluginAppDomain.CreateInstanceAndUnwrap("FileSystemWatcherService.Interfaces", "FileSystemWatcherService.Interfaces.Gateway");
                PluginInfo pluginInfo = gateway.LoadPlugin(assemblyName);
                DebugHelpers.AssembliesInAppDomain("After AuxiliaryDomain was created");
                return new PluginController(pluginInfo, pluginAppDomain, assemblyName);
            }
            catch(Exception)
            {
                DebugHelpers.AssembliesInAppDomain("After exception");
                if (pluginAppDomain != null) AppDomain.Unload(pluginAppDomain);
                return null;
            }
        }

Faltan resolver algunos problemas como el de determinar el conjunto de permisos con los cuales deben correr los plugins, controlar el tiempo de ejecución de los plugins para poder descargarlos si por ejemplo entran en un bucle infinito, resolver el problema de que el FileSystemWatcher de vez en cuando lanza varias veces el mismo evento lo que puede hacer que los plugins se descarguen y carguen muchas veces. También hay que refinar el catcheo de las excepciones pero este es solo un ejemplo :)

Continuará....

Lucas Ontivero

Introducción

Continuo aquí la serie "Patterns" que comencé hace algún tiempo. Esta serie se compone de las entradas anteriores:

Antes de comenzar les dejo dejo esta imagen:
TablaDeElementos
Extraido de http://www.vincehuston.org/dp/

Volviendo al tema, ahora nos toca el "Chain Of Responsibilities Pattern". Este es un patrón muy sencillo y muy usado. Chain Of Responsibilities permite desacoplar al objeto que realiza una petición de aquellos quienes pueden tratarla. Es decir, cuando un objeto necesita que se realice alguna acción como, por ejemplo, aprobar una rendición de viaticos, el objeto solicitante (el que requiere que se apruebe la solicitud) no necesita conocer la mecánica de las aprobaciones de viaticos sino que solo necesita invocar a la cadena de responsabilidades y solicitarle que maneje la aprobación solicitada. En la cadena pueden existir múltiples objetos responsables de aprobar o rechazar las rendiciones de viaticos según ciertos criterios e incluso es posible agregar nuevos responsables y/o quitar alguno(s) existente(s).

Veamos el diagrama de clases de este patrón:

CoR
Como puede verse, existe una clase abstracta "Handler" (puede implamantarse también mediante una interface "IHandler" ya que practicamente no contiene códido) la cual mantiene una referencia a otro Handler por medio de la propiedad "Next". Así, es claro que estamos ante una "lista enlazada" de Handlers. Luego, encontramos 'n' implementaciones concretas de Handler (o IHandler) como ConcreteHandler1 y ConcreteHandler2 las que sobreescriben el método "HandlerRequest()" y por lo tanto, en cada eslabón de la cadena encontraremos distintos handlers que manejarán cada uno una petición según se cumpla alguna condición específica.

En el ejemplo que mencionaba más arriba, y acotando el ejemplo a nuestra industria, podremos encontrar que la condición para la aprobación de una rendición de viaticos podría tener relación con el monto total rendido. Y podríamos imaginar el siguiente procedimiento:

  • Si el monto total rendido es menor a $1000, lo aprobará el Project Leader,
  • Si el monto total rendido es mayor o igual a $1000 y menor a $5000, lo aprobará el Program Manager y,
  • Si el monto total rendido es mayor o igual a $5000, lo deberá aprobar el Director del Centro.

En este caso, la cadena de responsabilidades para la aprobación de viaticos se compondrá de 3 responsables: una instancia de ProjectLeader, una de ProgramManager y una de Director.

El objeto solicitante deberá conocer al primer responsable (después veremos como mejorar esta situación) y solicitarle la aprobación como sigue:

public void ToApprove(Accountability acc)
{
    ProjectLeader.HandleRequest(acc);
}
En este caso, imaginemos que se rindieron $1700. Entonces, el primer handler, que es una instancia de ProjectLeader, evauará la condición "if (request.Sum < 1000)" para ver si es él quien debe manejar esta petición pero como el monto lo supera ($1700) le pasará la responsabilidad al segundo en la cadena, al Program Manager. El ProgramManager si es el responsable y por lo tanto se encargará de aprobar la solicitud y allí terminará la cadena. Es decir, el Director no intervendrá.
Abajo muestro un pequeño fragmento de código de estos tres Handlers:
 
public class ProjectLeader : Handler
{
    public override void HandleRequest(Accountability request)
    {
        if (request.Sum < 1000)
            System.Console.WriteLine("Aprobado por el Project Leader");
        else
            base.HandleRequest(request);
    }
}


public class ProgramManager : Handler
{
    public override void HandleRequest(Accountability request)
    {
        if (request.Sum >= 1000 && request.Sum < 5000)
            System.Console.WriteLine("Aprobado por el Program Manager");
        else
            base.HandleRequest(request);
    }
}


public class Director : Handler
{
    public override void HandleRequest(Accountability request)
    {
        if (request.Sum >= 5000)
            System.Console.WriteLine("Aprobado por el Director");
        else
            base.HandleRequest(request);
    }
}
En el ejemplo que acabo de dar es muy importante observar que los resposables deben estar ordenados en la cadena de menor a mayor porque si el Director fuese el primero, entonces todas las aprobaciones serian resueltas por él.  También hay que notar que aquí solo existe un responsable por cada tipo (según el monto) de rendiciones y que apenas uno la maneja, corta la cadena. Pero esto no tiene por que ser así, incluso una petición bien podría ir pasando por todos los elementos de la cadena de responsabilidades y que cada responsable se encargase de hacer algo con ella. Para los amantes del ObjectBuilder, su clase BuilderStrategyChain es una cadena de responsabilidades en la cual se alojan implementaciones concretas de IBuilderStaregy (ej: MethodExecutionStrategy y PropertySetterStrategy ) las que se encargan de realizar distintas acciones sobre los objetos como inyectarles valores a sus propiedades y ejecutarles ciertos métodos.
 
Otro aspecto relevante, aparte del orden de los handlers, es que el tratamiento de una solicitud no está garantizada. Una solicitud puede finalizar el recorrido de la cadena sin que ningún Handler la trate. Salvo en casos como en donde existe como último elemento de la cadena un Handler "Trata todo" el que provea de un tratamiento genérico por defecto para todas aquellas peticiones no manejadas por ninguno de los anteriores. Por ejemplo, podría existir un Handler del tipo LogguerHandler que escribiera una entrada en el event log diciendo que tal petición no fue manejada. 
 

Sistema de plug-in

La cadena de responsabilidades nos permite crear sistemas que soporten plugins de la siguiente manera: imaginemos que tenemos una aplicación que internamente mantiene información en un XmlDocument  que deseamos guardarlo en el disco bajo cierto nombre y extensión, como por ejemplo, archivo1.xml o informacion.doc. Es claro que según la extensión del archivo deberemos invocar a una clase que se encargue de las transformaciones necesaria, pero nosotros queremos dejar abierta la posibilidad de extender esto a otros tipos de archivos como hojas de calculo de MS Excel, PDF, JPG, TXT, etc. Queremos que cualquier desarrollador pueda desarrollar y extender esta característica de nuestro sistema mediante la registración de un plugin. La estrategia para resolver esto es mantener una CoR en la que cada Handler se encargue de manejar un tipo de archivo específico. La solicitud tendrá el XmlDocument, el nombre y la extensión del archivo y al ir recorriendo la cadena, cada handle preguntará si la extensión del archivo es la que él puede tratar, y de ser así, hará las transformaciones necesarias y lo guardará con el formato propio que él maneja. Como último eslabón puede registrarse un Handler que notifique al usuario de que ese tipo de archivo no puede ser manejado por el sistema.  

Mejora

El que el solicitante deba conocer al primer Handler es algo poco elegante, por eso la propuesta consiste en crear una clase ChainOfResponsibilities que se encargue de mantener una referencia al primer Handler como así también, de las tareas de dar el Kick off de las peticiones para que comience a recorrer la cadena como de agregar/quitar Handlers de/a la cadena.

En el ejemplo de abajo puede verse esta clase con las propiedades FirstHandler y los métodos Add y StartRequest. La propiedad LastHandler es privada y mantiene una referencia al último elemento de la cadena para hacer más rápido el agregado de nuevos Handlers ya que de esta manera nos evitamos el tener que recorrer la cadena para llegar al final.

Cuando se implementa como pipeline, puede que una petición haya sido tratada por uno más handlers hasta ue uno falla. En este caso muchas veces puede (y muchas no) implementarse un mecanizmo de Rollback implementando un método Reverse  en cada handler de manera que recorriendo la cadena en sentido contrario pueda volverse la petición a su estado original. Esto no es posible mediante una lista enlazada por lo que existe una alternativa.

Alternativas a la lista enlazada

Si bien este patrón se ideó como una lista enlazada, es posible implementarlo por medio de una lista de Handlers como List<IHandler> solo que en este caso, los handlers no conocerán a su sucesor sio que deberán solicitarle a la clase contenedora que pase la petición al próximo manejador.

Ejemplo

Para ejemplificar un poco más este patrón he creado un pequeño proyecto en el cual se implementa como una pipeline (el resultado de uno pasa al siguiente) para un sistema de Mobile Banking en el cual un cliente de un banco que cuenta con este servicio quiere enviarle dinero por medio de su celular a otra persona de quien conoce también su número de celular (movil).

En este caso, cuando llega una solicitud de envio de dinero, el sistema la pasa por una pipeline la que realiza las siguientes comprobaciones/acciones:

  • Verifica que exista un cliente registrado en el banco con ese número de celular. Si esto falla es porque alguien que no es cliente está haciendo peticiones al sistema.
  • Verifica que el cliente no haya sobrepasado su límite anual de transacciones. Anualmente cada cliente no puede hacer transferencias por más de un monto límite determinado para él.
  • Verifica que el cliente no haya transferido o intente transferir más dinero del que se le estableció como límite mensual y,
  • si todo lo anterior es correcto, realiza la transacción. Esta podría ser muy compleja.

El diagrama de clases para este ejemplo es el siguiente: 

CoRImpl

 

La pipeline 

pipeline

Notese que pueden agregarse y/o quitarse operaciones a esta pipeline.

Código de ejemplo ChainOfResponsibilitiesPatternExample.zip.
El código no es de un sistema real y es extremadamente sencillo. Lo hice solo para mostrar un poco de código y nada más que para eso.

Referencias y otros ejemplo

 

Continuará....

Lucas Ontivero

carrot_on_stick2 Como generarias desconfianza e incertidumbre en los empleados? como romperías la sinergia entre ellos? como incrementarías las rotación del personal? Como puedes agotarlos? Como generarias una psicosis generalizada en una empresa?

Escucho opiniones pero propongo una: administrar por objetivos.

La idea en si no es mala, pero muchas implementaciones si lo son. Se trata de un concepto que parece lógico, establecer una meta organizacional y trazar el camino mediante la declaración y persecución de objetivos a largo, mediano y corto plazo. Se espera que el logro sistemático de estos objetivos lleven a la empresa a cumplir su meta de sobrevivir. Claro, con esto no basta, es necesario que todos colabroren y se "alinien" para el cumplimiento de estos y por lo tanto surge la pregunta: ¿Como?. Facil, aquí en Argentina le llamamos "sistema de premios" y quizás muchos de Uds. en España lo conozcan como la porsión variable del salario, hace mucho me lo explicaron con una metáfora estúpida "Poner la zanahoria delante del caballo".

Tengo una teoria, cuando un empleado ingresa a una empresa desea siempre hacer lo mejor, sobresalir, ayudar con sus conocimientos y experiencias previas, aprender rápido y trabajar lo mejor posible. Lo veo a diario, siempre es así, apenas les dices "mirá esto" o "ayudame un poquito" enseguida pegan un salto de su asiento y vienen atentos a todo. Esto es motivación! Desgraciadamente al poco tiempo alguien les explica que no pierdan su tiempo con esta o aquella custión porque .... existe un sistema que los mide y que su salario depende de ello. Listo, nunca más los volvemos a ver como entonces. Pero eso se soluciona con un curso motivacional! o no?

Sucede que cuando se introduce la idea de que es posible manipular el salario de los empleados, estos reaccionan acorde a las nuevas reglas que gobiernan el sistema. He leido mucho sobre esto y he conversado miles de veces sobre lo mismo y no puedo resumir las conclusiones así que doy solo las mias:

  • Salvese quien pueda! Esto es lo primero. Ayudame? que es eso? si no llego a mis objetivos! esto nos lleva a la segunda conclusión,
  • Sinergia cero. "Nuestro equipo tiene que llegar a:..... No podemos perder el tiempo con ese problema" (no importa su gravedad). Empleado: "Pero vamos a perder el cliente!" - Supervisor: "No es nuestro, es de ellos (del equipo de al lado) así que serán ellos los que pierdan su bono a fin de mes!" quizás esto nos conduzca a la siguiente,
  • Insertidumbre y gestión por miedo. Familiar: "Cuanto cobras?" Empleado: "No se! si logro convencer a mi supervisor quizás saque $xxxx".  Y esto también provoca el
  • Malestar en el trabajo y el pensamiento a corto plazo.

Trabajé en dos empresas en donde esto se llevaba muy lejos y en una de ellas se notaba la psicosis que producía en muestros compañeros catalanes.

Existe en esto un error conceptual de lo que significa trabajar para que a la empresa le vaya mejor, la frase elegida es "alinear los objetivos individuales a los organizacionales" cuando en verdad debería ser "trabajar en pos/colabrorar TODOS JUNTOS para que la organización logre sus objetivos". No es lo mismo y aunque la diferencia parezca tonta el error está en tratar de idear un método sistemático para decirles a cada uno qué es lo que debe hacer y la trampa está en creer que porque "ellos mismos" se han fijado sus objetivos, estos han tenido la total libertad de escoger lo que consideran mejor para la empresa. Falso.

Mi experiencia en este tema es larga. En mi primer trabajo como desarrollador era muy feliz, hasta hoy nunca me volví a sentir mejor en un trabajo hasta el dia, por la insistencia y un poco de descontento por lo bajo de nuestros salario, nuestro supervisor nos vino con "una fabulosa idea" que después de una insignificante burocracia de pensar, planear, revisar, aprobar, seguir, cumplir y revisar el cumplimiento de los objetivos nos darían un premio! que compensaria más o menos nuestros salarios. Umm, peor es nada! en otras areas ya lo hacian y algunos pocos se habian vuelto un poco idiotas con el "para eso no tengo tiempo" hasta el punto que a todo respondian lo mismo; "tu señora estaba enferma no? como está?" - "Bien. Después te cuento. En este momento no tengo tiempo". Cuando se acercaba el fin del mes ya empezaba a planear como me iba a defender del descuento, las discuciones eran fuertes y me arruinaban toda una semana y sobre todo ese clima que me gustaba. Listo! al que no le gusta que se vaya y me fuí.

Me fuí al lugar que me juraron que eso allí no pasaba. Después de unos pocos meses llegó el scorecard! Un sistema de calificación por demás complejo que propiciaba las peores prácticas. Los supervisores estaban entre la espada y la pared, ellos debian hacer que eso cerrase. No solo era complejo sino que todas las conversaciones por razones de trabajo con nuestros compañeros de cataluña eran en lenguaje scorecard. Es que el sueldo depende de eso!. A ellos les causaba una psicosis real y un sálvese quien pueda total. Creo que nunca volveré a ver otro caso similar.

Tuve otro trabajo después, muy bueno y muy interesante. Trabajaba muchas veces un par de horas de más. A decir verdad, 3 o 4 dias a la semana trabajaba entre 1 y 3 horas de más. Pero el ambiente era genial, muchos tipos con mucha imaginación, buena onda, otros con mucha experiencia y muy colaborativos todos. De repente nos llegó el puntaje por mail (no lo recuerdo bien) "chicos, como somos los mejores del mundo (en todos lados les deben decir lo mismo) necesitamos mejorar constantemente. Nuestra puntuación actual es de 3 pts. y necesitamos llegar a 25 pts.". Ojo, aquí también se trataba de $ aunque de una manera un tanto indirecta.

Esta entrada la creé porque en mi nuevo lugar trabajo me han pedido que cargue mis objetivos (alineados claro está) para el siguiente año. Y la buena noticia es que aquí también hay una revisión, esta vez anual, para ver cuanto voy a cobrar el año que viene :). Pero aqu'i esos objetivos parecen no importarles a nadie. Pero, y entonces para que los ponen?

Existe otra teoria, la de Maslow y sus famosos hombre X (funcionan a látigo en mano) y los hombre Y (los que yo sostengo). Estos hombres buscan cosas distintas porque son distintos, pero como administrar es homogeneizar para poder multiplicar, entonces como se homogeneiza esto? (X+Y)/2 = AVG(MAN)? NO! es ridículo! Se debe propiciar la colaboración, la investigación, la sinergia y ayudar a cada quien para que ayude con sus pequeños esfuerzos diarios a mejorar todo lo que sea posible, alejarlos de los sistemas que los vuelven egoistas, cortoplacistas y miedoso, tratar de retenerlos y no de cansarlos o perderlos, no hay que motivarlos sino tratar de no desmotivarlos.

Veo una solución, "El liderazgo". No basta con cambiarle el nombre de capataz a lider, ni darle al capataz un curso de liderazgo. Sería como "ya no se llevarán más con el capataz! ahora odiarán a su lider!". El liderazgo real que hace falta es aquel centrado en una persona que pueda de por si (respaldado por la organización de cualquir manera sólida) movilizar a la gente para trabajar por ese TODOS que es la organización. Alguien sabe como se hace?

Lucas Ontvero

Publicado 5/10/2007 3:22 por Lucas Ontivero | 12 comment(s)
Archivado en:

Esta entrada es parte de la serie "Introducción a los conceptos de Software Factory" que hasta el momento se compone de las entradas anteriores:

En esta entrada voy a trara sobre el punto central al que aplica el concepto de SF, la linea de productos. Antes de esto debo aclarar que existe una clasificación de los SF la cual distingue entre SF horizontales, aquellos relacionados a una tecnología, plataforma, arquitectura, que pueden ser comunes tanto a un CRM, ERP, DMS como a cualquier otro (como son por ejemplo los casos de SCSF, WSSF, MCSF, etc.) y los verticales que son aquellos que se utilizan para la construcción de una familia de productos estrechamente relacionadas a un tipo de negocio como CRMs, ERPs, etc. Hoy contamos, gracias a Microsoft de SF horizontales pero a los SF verticales deberemos construirlos nosotros. A menos que alguien conozca una especie de Customer Relationships/Service Software factory :)

Volviendo al tema, una linea de productos se refiere a un tipo de aplicación que puede agruparse o identificarse como perteneciente a una familia de productos, como por ejemplo sistemas de CRM, software de seguros, software para entidades bancarias, de control aereo y cualquier otro. Es decir, pueden agruparse de distintas maneras, por segmento de negocios, tipo de soluciones, plataforma, etc. Lo importante es poder identificar aquellas características comunes, lo que mediante la reutilización de ciertos assets de alto nivel de granularidad, nos proporcionará la tan buscada economía de alcances.

Tanto los SF horizontales como verticales cumplen con el cometido de mejorar la calidad, productividad y tiempos de entrega, pero son los verticales los que pueden aportar "el gran salto" que una empresa necesita en estos sentidos.

Los pasos

Alcances: El primer aspecto importantes es analizar los alcances del SF a construir, es decir, que tipos de aplicaciones podremos crear con él. Este es sin dudas un tema esencial y dificil por cuanto mientras más tipos de aplicaciones querramos abarcar, mayor esfurzo requerirá en sus otros dos ejes (variabilidad y extensibilidad).

Supongamos que tenemos bien definido el alcance de nuestro SF. Algo así como:
"DMSSF (Document Manager System Software Factory) es un SF para la construcción de aplicaciones de uso de documentos como, expedientes, procedimientos, reclamos, comunicados, Bases de conocimientos, publicaciones en linea, reporte de errores y otros similares".
Bueno, debería ser mejor pero este es solo un blog :). Ahora, todos estos sistemas tienen características similares o al menos una muy evidente, son todo sistemas documentales.

En el libro "Practical Software Factories in .NET From Theory to Practice—A Primer, Reference, and Case Study", que es el que estoy leyendo ahora, dice que el primer paso es redactar y comunicar la visión del SF y da el siguiente ejemplo para la empresa ficticia ISpySoft:
"Develop a Software Factory that enables efficient development and maintenance of
high-quality, modular, extensible applications for private investigator offices and field
agents, and that allows ISpySoft to also offer new services to its customers.
"

<opinion>La verdad es que a mi entender, estas declaraciones no sirven para nada y ponen a mucha gente a perder tiempo valioso en discuciones improductivas. Además suenan todas iguales.</opinion>

Variabilidad y extensibilidad: Una vez analizadas las similitudes (y extraerlas y documentarlas) debemos concentrarnos en las diferencia. Las diferencias podrian ser:

  • para expedientes y reclamos tenemos clientes mientras que para el resto no.
  • para expedientes, procedimientos, reclamos y reporte de errores tenemos un workflow mientras que para el resto no.
  • Algunos documentos necesitan un control de versiones y otros no.
  • Algunos pueden necesitar si o si de digitalización de documentos en papel y otros no.
  • Algunos necesitan autentificación, envios de emails, trabajo desconectado, integración con outlook, logueo en db, etc.

Aquí también deberian ser mejores.

Estas diferencias hacen al concepto de variabilidad y extensibilidad. Muchas veces se piensa que esto es una cuestión de configuración o parametrización de un producto complejo al que mediante opciones de parametrización se le pueda dar la funcionalidad requerida, algo así como configurar un sistema de control de transporte para que se comporte como un control de tráfico aereo o uno de tracking de ganado. Esta es la clásica visión de producto configurable que no se corresponde con la idea de software factory. Esto es parte del paradigma anterior :)

En la entrada [Software Factories] Introducción (Parte 2) explico estos dos conceptos así que no los repetiré aquí. Pero cual es la diferencia entre variabilidad y extensibilidad? (<include> o <extend>) La variabilidad se refiere a aquellas características o alcances que tenemos plenamente identificados y que por lo tanto están ya en el total conocimiento de quienes conocen el dominio. Estas son candidatas a ser fácilmente configurables por nuestras herramientas por ejemplo: usuarios integrados con LDAP u otro sistema nuestro, tenemos integración de clientes con otros sistemas o no, etc.

La extensibilidad, en cambio, hace a todos los aspectos que pueden pertenecer (tal vez no) a la penumbra de los requerimientos del cliente y que por lo tanto deben predecirse para dejar esas puertas abiertas.

Pasar de la visión de producto a la visión de linea de productos

Este es un tema muy dificil, sobre todo porque no encontré nada al respecto así que acá va un intento:

  • Convencer al concejo de ansianos (o al gran jefe) de las bondades del cambio. El concejo no entiende de c# ni Biztalk y dado que esta iniciativa se gestará en los niveles medios o administrativos, es primordial contar con el apoyo de la alta gerencia.
  • Converzar con todos los involucrados sobre la nueva manera de pensar y hacer software. Estos cambios siempre despiertan curiosidad y motivación en la gente por lo que abre las puestas para que se los involucre en las nuevas tareas. Además todo el mundo tendrá preguntas y sugerencias.
  • Reunir al equipo que desarrollará el SF. Este equipo ya está preseleccionado de antemano, todos saben quienes son, los mejores desarrolladores, el product manager, la gente de QA, los implementadores con mayor conocimiento del dominio del negocio y las necesidades habituales de los clientes, etc.
  • Armar el Software Factory Schema (introducción parte dos). Y factorizar el o los productos existentes y matchear los factores (producto de la factorización) con los viewpoints del SFS.
  • Comenzar a tailorizar EL proceso. Ya no tendremos UN proceso de desarrollo DE SOFTWARE sino un proceso de desarrollo de aplicaciones de administración de documentos web enabled.
  • Manos a la obra!
  • Hacer un piloto y cargar el feedback para mejorar el SF.

Indudablemente esto es mucho mas complejo y por cada item existen muchos aspectos que escapan al sentido original de esta entrada como "Análisis y diseño de SF".

Assets existentes

Para finalizar, tanto el resultado de la factorización de los productos (si los hubiera) como los assets existentes deben integrarse y acoplarse mediante, el proceso de desarrollo y el IDE de desarrollo.

Continuará...

Lucas Ontivero

Hace más de 40 semanas que vengo analizando la tendencia del mercado laboral de mi ciudad (Córdoba - Argentina) para tratar de entender que está pasando, que perfiles se buscan, cuales faltan, cuanto se paga, cuantas empresas nuevas se forman o radican, cuantos ingenieros egresan, etc.

Luego de algún tiempo de intentar clasificar todos estos factores juntos lo abandoné porque me superaba pero seguí conservando y siguiendo algunos indicadores propios (entre ellos mi salario - el mas desalentador jajaja).

Aquí va un análisis muy simple de una sola fuente: http://www.computrabajo.com.ar
Este es el resultado de 34 semanas de ofertas de empleo sin categorizar, algo así como ofertas en bruto.

Para la estimación usé la función LINEALST de Excel y los Límites superior e inferior los calculo a partir del método de las 3 sigmas.

Como se puede observar, la demanda es creciente por lo que no muestra señales de formar parte de un sistema estable, a menos claro que se trate de factores estacinales que no se manifiesten en tan poco tiempo.

Semana Ofertas Media L.Sup Media L.Inf Est.Lineal
1 64 66,88 2,88 112,70 21,06 39,26
2 54 66,88 12,88 112,70 21,06 40,52
3 55 66,88 11,88 112,70 21,06 41,77
4 46 66,88 20,88 112,70 21,06 43,03
5 53 66,88 13,88 112,70 21,06 44,29
6 42 66,88 24,88 112,70 21,06 45,55
7 48 66,88 18,88 112,70 21,06 46,81
8 49 66,88 17,88 112,70 21,06 48,06
9 42 66,88 24,88 112,70 21,06 49,32
10 57 66,88 9,88 112,70 21,06 50,58
11 53 66,88 13,88 112,70 21,06 51,84
12 82 66,88 15,12 112,70 21,06 53,10
13 73 66,88 6,12 112,70 21,06 54,35
14 56 66,88 10,88 112,70 21,06 55,61
15 58 66,88 8,88 112,70 21,06 56,87
16 38 66,88 28,88 112,70 21,06 58,13
17 60 66,88 6,88 112,70 21,06 59,39
18 94 66,88 27,12 112,70 21,06 60,65
19 58 66,88 8,88 112,70 21,06 61,90
20 68 66,88 1,12 112,70 21,06 63,16
21 89 66,88 22,12 112,70 21,06 64,42
22 62 66,88 4,88 112,70 21,06 65,68
23 80 66,88 13,12 112,70 21,06 66,94
24 94 66,88 27,12 112,70 21,06 68,19
25 69 66,88 2,12 112,70 21,06 69,45
26 58 66,88 8,88 112,70 21,06 70,71
27 80 66,88 13,12 112,70 21,06 71,97
28 61 66,88 5,88 112,70 21,06 73,23
29 82 66,88 15,12 112,70 21,06 74,48
30 64 66,88 2,88 112,70 21,06 75,74
31 92 66,88 25,12 112,70 21,06 77,00
32 91 66,88 24,12 112,70 21,06 78,26
33 114 66,88 47,12 112,70 21,06 79,52
34 88 66,88 21,12 112,70 21,06 80,77
35 82,03
36 83,29
37 84,55

Aquí va la gráfica:

image

Nota: las tres semanas sin datos son solo para "estirar" la linea de tendencia. Las causas de los últimos puntos de crecida se deben a EDS, Gameloft, Motorola y el gran pico final se debe a que a estos tres se le ha sumado Santex y otros aleatorios.

Lucas Ontivero

Publicado 2/10/2007 2:59 por Lucas Ontivero | 2 comment(s)
Archivado en: