Mostrando entradas con la etiqueta Informática. Mostrar todas las entradas
Mostrando entradas con la etiqueta Informática. Mostrar todas las entradas

viernes, 8 de julio de 2011

Silverlight y las excepciones no gestionadas

Silverlight nace para cubrir las necesidades de aplicaciones RIA, y como sabemos, las aplicaciones de Internet son exigentes en muchos aspectos. Es debido a dicha concepción, que posee ciertas características muy interesantes para escenarios para los que originalmente no fue creado.

Por ejemplo, la huella en memoria de Silverlight es sensiblemente menor que incuso para el .NET Framework Client Profile. Además el tamaño del instalador es también varias veces menos que dicho (de 28MB a 6MB ). Estos detalles que nos pueden parecer banales, no lo son tanto, si multiplicamos dichas diferencias por muchos millones de descargas o requerimientos de Hardware…

Teniendo en cuenta estas ventajas y que las partes más comunes de WPF se encuentra también presente en Silverlight, este se presenta como un candidato válido para desarrollo de aplicaciones de escritorio. (Ahora que está tan de moda HTML5/JS…)

Este modo de trabajo se conoce como “out of browser” (OOB). Tengamos en cuenta que la misma aplicación puede ejecutarse dentro y fuera del navegador, en principio, en base a que el usuario decida instalarla.

Si deseamos desarrollar aplicaciones de escritorio, podríamos distribuir nuestro .xap como parte del instalador de la aplicación completa, evitando dar la posibilidad de ejecutarla en el navegador (El host va a ser el sllauncher.exe)

Las mayores restricciones en este aspecto están relacionadas con la interacción con el Sistema Operativo y acceso a recursos de la máquina local. Si vuestro conjunto de requisitos de interacción en el puesto cliente no son muy elevadas, es posible que Silverlight pueda ser vuestra tecnología de cliente.

En esta forma de trabajo, debemos evaluar la conveniencia de algunos de los planteamientos del equipo de Microsoft y adaptarlos a nuestras necesidades. La gestión predeterminada de las excepciones no gestionadas que nos propone Visual Studio no está pensada si deseamos crear una aplicación de escritorio.

Si vemos el código que nos propone el Visual Studio al crear el proyecto de Silverlight observamos:

  1:         private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
  2:         {
  3:             // If the app is running outside of the debugger then report the exception using
  4:             // the browser's exception mechanism. On IE this will display it a yellow alert 
  5:             // icon in the status bar and Firefox will display a script error.
  6:             if (!System.Diagnostics.Debugger.IsAttached)
  7:             {
  8:
  9:                 // NOTE: This will allow the application to continue running after an exception has been thrown
 10:                 // but not handled. 
 11:                 // For production applications this error handling should be replaced with something that will 
 12:                 // report the error to the website and stop the application.
 13:                 e.Handled = true;
 14:                 Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
 15:             }
 16:         }
 17:
 18:         private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
 19:         {
 20:             try
 21:             {
 22:                 string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
 23:                 errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
 24:
 25:                 System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");
 26:             }
 27:             catch (Exception)
 28:             {
 29:             }
 30:         }

Como vemos se incluye una función para interactuar con el DOM. Esto no tiene sentido si nuestra aplicación va a correr siempre en modo OOB. Además comprueba si no tenemos el depurador adjunto para realizar esta notificación…

Vamos a comenzar a modificar este comportamiento para que se adapte a nuestras necesidades.


Lo primero vamos a hacer es cambiar el código del método Application_UnhandledException y eliminar la función ReportErrorToDOM.

El resultado quedaría algo así:

  1:         private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
  2:         {
  3:             e.Handled = true;
  4:             new ExceptionDetailView {DataContext = new ExceptionDetailViewModel(e.ExceptionObject)}.Show();
  5:         }


Sencillo, verdad?


Antes de nada marcamos como tratado el parámetro recibido para notificar que ya ha sido tratada y que no escale más por la pila de excepciones. Además mostramos un mensaje de error por pantalla notificando al usuario que se ha producido un error.

Este sería un punto interesante para guardar en el log de la aplicación la información más detallada de la excepción, que seguro el departamento de soporte nos lo agradecerá.

Si deseáis incluir una buena gestión de log en Silverlight os recomiendo usar el Silverlight Integration Pack for Microsoft Enterprise Library 5.0.


Ahora la pregunta es que es ExceptionDetailView?


Pues no es más que una vista que creamos para mostrar el mensaje de error de manera agradable para el usuario. La particularidad de la misma, es que es una ChildWindow clase que se incluye en Silverlight desde la versión 3.


Esta clase nos permite colocar un control de usuario simulando una ventana modal. Es decir, deshabilita y oscurece el fondo. Este es el comportamiento que deseamos para mostrar nuestra excepción de modo que quede algo como esto:


image


En la implementación de la vista empleamos el patrón MVVM, sobre el cual existen toneladas de información. (y eso en raras ocasiones es bueno…)

Resumiendo podemos decir que disponemos de un ViewModel (ExceptionDetailViewModel.cs) y una vista que se encarga de pintar los datos que ese ViewModel expone por pantalla. (ExceptionDetailView.xaml)

La vista tiene asociada en su DataContext el ViewModel y vía Binding implementamos el conexión entre los datos y su representación gráfica.

Además de los datos es responsabilidad del ViewModel exportar la funcionalidad (vía comandos) que la vista puede invocar. En este caso, nuestro ViewModel expone un comando a la vista (ToggleShowingExceptionDetailCommand) que le permite mostrar u ocultar la información detallada de la excepción, para contentar a los más curiosos…

Pues ya solo queda probar, porque nos encanta probar, verdad??


Y en este caso debemos lanzar una excepción (al gusto, yo me he decantado por una IndexOutOfRangeException) y comprobamos que la aplicación muestra el mensaje de error de manera correcta en vez de quedarse en blanco.


Espero que os resulte interesante la idea y os incluyo el código fuente de la aplicación de ejemplo para que podáis probar su funcionamiento. Podéis consultar este y otros prototipos aquí (el nombre del archivo de esta solución es Andoni.Arroyo.UnhandledException )

martes, 18 de agosto de 2009

¿Es lo mismo arquitectura por capas y N-Tier?

Me parece importante, para optimizar la comunicación entre profesionales, que todos tengamos claros los conceptos y los apliquemos para referirnos exactamente a lo mismo. Esto nos evita malas interpretaciones y errores muy críticos sobre todo en entonos de desarrollo rápido de aplicaciones (RAD). 

Existen dos conceptos arquitectónicos que a veces tendemos a confundir. Estos son la “Layered arquitecture” (arquitectura por capas) y la “N-tier arquitecture” (arquitectura de N-niveles). Me voy a referir a ellas sin traducirlas, puesto que ambos conceptos (“layer” y “tier”) pueden verse traducidas (erróneamente en el caso de “tier”) como “capa” lo cual fomenta aún más la propensión a errores.

Veamos primero que es exactamente “Layered architecture”: 

Se centra en una distribución jerárquica de las roles y responsabilidades proporcionando una separación efectiva de las preocupaciones. (cada cual que se apañe con sus problema…). El rol indica el modo y el tipo de interacción con las otras capas, y la responsabilidad indica la funcionalidad a alcanzar.

Por ejemplo, el diseño de una típica aplicación Web comprende una capa de presentación (funcionalidad relacionada con la U.I.), capa de negocio (procesamiento de reglas de negocio) y capa de datos (funcionalidad relacionada con el accesos a datos.)

El estilo de arquitectura por capas tiene algunas características que  la identifica:

  • Describe una descomposición de servicios de tal manera que las interacciones ocurren normalmente entre capas vecinas. (depende de lo laxos que queramos ser…)
  • Las capas de la aplicación pueden residir en el mismo ordenador fisco o pueden estar distribuidas en separados equipos. El concepto de distribución se aplica siempre que se produzca el mecanismo de marshaling para realizar una comunicación (siempre que no se comparta espacio de memoria…)
  • Los componente de cada capa se comunican con otros componente de otras capas a través de un conjunto de interfaces bien definidos. (sino apañados vamos…)
  • En alguna ocasión ha sido descrito como “pirámide invertida de reutilización” donde cada capa agrega sus propias responsabilidades  y abstracciones a la capas que están por debajo.

Que beneficios conseguimos aplicando esta distribución en capas?

Abstracción, encapsulamiento, capas de funcionalidad muy bien definidas, alta cohesión, reusabilidad (las capas inferiores de la pirámide no tienen dependencias de las superiores por lo que es fácil reutilizarlas) y débil acoplamiento (comunicación basada en abstracciones)

La separación, reduce el riesgo y el impacto de los cambios tecnológicos. Por ejemplo al cambiar el mecanismo de persistencia de la aplicación las capas que lo consumen no tienen porque realizar cambios. Además aumenta el rendimiento (a partir de cierta carga de trabajo) al distribuir las capas sobre múltiples capas físicas. También aumenta la escalabilidad y la tolerancia a fallos.

Este escenario es propicio para implementar una buena política de pruebas, permitiendo conmutar entre diferentes implementaciones de los interfaces (mock, servicios reales…)

Por otro lado tenemos la “N-Tier architecture”

El estilo de despliegue de esta arquitectura describe la separación de la funcionalidad en diferentes segmentos, de manera similar a la arquitectura por capas. pero cada segmento es un nivel que se encuentra físicamente en un equipo independiente.

Es un estilo que define el despliegue de las capas de la aplicación, se caracteriza por por una descomposición funcional de la aplicación, componentes de servicios y su despliegue distribuido, proveyendo mejoras de escalabilidad, disponibilidad  y utilización de recursos.

Cada capa es completamente independiente del resto, excepto de aquella/s se encuentran inmediatamente por debajo de ella. La capa n sabe como manejar las peticiones a la capa n+1, como trasladar dicha petición a la capa n-1 (si es que existe) y como tratar el resultado de la misma.

Las arquitecturas con “N-Tiers” poseen por lo menos 3 capas lógicas separadas. Cada capa tiene una funcionalidad especifica, de la cual es responsable y están localizadas en diferentes servidores físicos. Una capa (“layer”) se despliega en un nivel (“tier”) si más de un servicio o aplicación es dependiente de la funcionalidad expuestas por la capa.

Veamos los beneficios que nos aporta esta arquitectura:

  • Mantenibilidad
    Cada nivel es independiente de los demás, con lo que se consigue independencia. Se puede actualizar o modificar un nivel sin afectar a la aplicación en su conjunto. 
  • Escalabilidad
    Es razonablemente sencillo escalar puesto que los niveles están basadas en el despliegue de las capas.
  • Flexibilidad
    Cada nivel puede ser gestionado o escalado independientemente, lo cual aumenta la flexibilidad.
  • Disponibilidad
    Las aplicaciones pueden explotar la arquitectura modular empleando componentes fácilmente escalables.

Resumiendo un poco :

Podemos decir que para que una aplicación sea “N-Tier”, es condición necesaria pero no suficiente que mantenga una arquitectura por capas. Esta característica nos permite cumplir los requisitos de despliegue (3 o más capas separadas) que hacen a una aplicación “N-Tier”.

miércoles, 12 de agosto de 2009

Dulce introducción al Kanban!!

Bueno, parece que SCRUM ha puesto la pica en Flandes y se ha convertido en una metodología ampliamente conocida y respetada. Pues ya está, no? ya tenemos metodología ágil para cualquier tipo de proyecto y para siempre…

Muy confiado tienes que ser para creerte eso…

En mi trabajo hemos desarrollado un producto aplicando SCRUM con unos resultados muy satisfactorios. Dicho producto, se ha comenzado a implantar y ahora entra un equipo de soporte a mantener la primera versión del mismo y comienza una nueva rama de desarrollo de la siguiente versión.

Estaba planteándome como organizar el equipo de soporte y no acababa de encajar SCRUM sobre la naturaleza de su trabajo(la verdad que esta y cualquier otra metodología…). Es muy difícil organizarse para planificar incluso un pequeño Sprint, puesto que no se conocen las tareas de antemano. Por lo tanto, llegué a la conclusión de que necesitamos para este tipo de proyectos (que no son pocos) algo todavía más adaptable que SCRUM…

Tomando un café con el gran (aunque javero) Jorge Prieto me comentó algo sobre Kanbas y comencé a tirar del hilo para ver si me podía cuadrar eso que sonaba a pescado crudo…

Así que vamos a aprovechar este post para explicar que es eso de Kanban compararlo con SCRUM e identificar sus conflictos potenciales. Antes que meternos en harina vamos a dejar las cosas claras, haciendo un pequeño resumen de SCRUM y Kanban

SCRUM (en pocas palabras)

  • Divide su organización en pequeños equipos auto-organizados y multidisciplinares.
  • Divide el trabajo en una lista de pequeños elementos muy concretos.
  • Ordenar la lista por orden de prioridad y estimar el esfuerzo relativo de cada elemento.
  • Dividido en iteraciones cortas de longitud fija (normalmente con entrega o demostración después de cada iteración.)
  • Optimizar el plan de liberación y actualizar las prioridades en colaboración con el cliente, sobre la base de conocimientos adquiridos por la inspección de la entrega después de cada iteración.
  • Optimizar el proceso por tener una retrospectiva después de cada iteración.

Resumiendo el resumen:

Pasamos de “Muchas personas haciendo algo muy grande” a “Pocas personas haciendo cosas pequeñas que se integran con regularidad para ver el conjunto.”

Kanbas (en pocas palabras también)

  • Visualizar el flujo de trabajo
    Dividir el trabajo en trozos escribir cada uno de los elementos en una tarjeta y poner en la pared.
    Usar el nombre o columnas para ilustrar en que estado se encuentra cada elemento dentro del flujo de trabajo.
  • Límitar WIP (Work In Progress)
    Asignar límites explícitos obre cuantos elementos pueden estar en cada uno de los estados del flujo de trabajo.
  • Medir el tiempo
    Contar el tiempo promedio para completar un tema, a veces llamado "tiempo de ciclo" y, como es obvio, optimizar el proceso para que el tiempo tan pequeño y previsible como sea posible.

image

Podemos observar que Kanban es todavía menos prescriptivo que SCRUM.

Prescriptivo significa “reglas para seguir”. 100% prescriptivo implica que no tienes que usar el cerebro, ridículo verdad?…) y en este apartado vemos que SCRUM aporta mas reglas “out-of-the-box” como por ejemplo encajar en un tiempo los Sprints cosa que Kanban no hace.

Para hacernos una mejor composición podemos observar la escala de “prescriptividad” (toma ya!) de forma gráfica:

image

Como siempre podemos decir que la mejor opción es no limitarse a un herramienta en concreto. Probablemente la solución nuestros problemas se encuentre en una combinación de varias. Muchos equipo Kanban realizan una reunión diaria a primera hora (típica liturgia de SCRUM). Algunos equipos de SCRUM que he conocido, escriben su Backlog como Casos de Uso (típico de RUP) o limitan el tamaño de las colas (típico de Kanbas).

Así que como siempre “haz las cosas como mejor te funcionen”, ya os contaré el resultado de aplicar esta forma de trabajo con el equipo de soporte.

Para terminar os cito a Miyamoto Musashi que los japoneses algo saben de todo esto:

“No desarrolles ninguna dependencia de arma o escuela de lucha”

lunes, 10 de agosto de 2009

¿Por que iterar?

Aplicando el patrón C.S.P. (Common Sense Pattern…) en nuestros desarrollos, observamos que debemos cumplir con las necesidades del cliente. Acto seguido, descubrimos, que estas cambian más que los nos gustaría (ouchh!). Por lo tanto, se crea la necesidad de ir ajustando el avance del proyecto a las necesidades reales (y cambiantes) del proyecto.

¿Pero como…?

La respuesta que nos aportan las metodologías agiles es sencilla, iterando. Partiendo el desarrollo de la aplicación en pequeños entregables que el usuario pueda probar e ir enriqueciendo. Suena lógico, pero vamos a hacer un esfuerzo de plasmar negro sobre blanco las principales ventajas que esta forma de trabajar nos ofrece:

  • Gestión de riesgos
    Asumámoslo, el resultado deseado no es concebible por adelantado. Existen riesgos, algunos ya identificados y otros por identificar. Para gestionarlos correctamente debes demostrar o refutar tus requisitos y diseñar las suposiciones implementando incrementalmente elementos que son objetivos del sistema, empezando con los de más alto riesgo.
  • Económicos
    En un ambiente de negocio incierto (casi todos los proyectos lo son !!) es clave optimizar los esfuerzos en las mayores prioridades del proyecto. Debemos manejar cada iteración como un conjunto de fichas de juego a apostar por un conjunto de funcionalidad que se pueda entregar, probar y que funcionen.
  • Enfoque
    Solo podemos mantener una cantidad limitada de elementos en la mente. Trabajando con pequeños lotes de trabajo podemos focalizar nuestros esfuerzos con un conjunto de funcionalidades que comprendemos y somos capaces de encajar y entender.
  • Motivación
    No existe un mecanismo mejor de motivación de acción prolongada (el dinero solo motiva en un espacio reducido de tiempo!!) para un profesional que comprobar como su creación se usa, es efectivo y valorado por los usuarios.
  • Control de la teoría
    Las iteraciones nos permiten controlar mejor las desviaciones, reduciendo el impacto de los errores en las estimaciones y crear un mecanismo de retroalimentación sobre los planes del proyecto.
  • Implicación de los interesados
    Los patrocinadores (clientes, usuarios, dirección…) pueden observar los resultados más rápidamente (cumpliendo así con la visibilidad, requisito fundamental de cualquier desarrollo) y así aportar mayor compromiso, implicación y financiación.
  • Aprendizaje continuo
    Todos los actores del proyecto aprenden en cada iteración. Los miembros del equipo adquieren conocimiento funcional y aportan mejoras desde la prospectiva tecnológica y los funcionales comprenden las implicaciones de automatizar los procesos empresariales. Todos aportan y el proyecto crece rápido y mejora.

Ya tenemos un conjunto de argumentos para convencer a los stakeholders más peleones acerca de las bondades de trabajar en pequeñas iteraciones, ahora el que quiera entender que entienda…

lunes, 3 de agosto de 2009

Aplicaciones multilenguaje ASP.NET MVC

El desarrollo de capa de presentación Web en .NET ha cambiado desde que Microsoft se decidió a realizar una implementación oficial del conocido patrón MVC en la plataforma ASP.NET y la (amplia) comunidad de desarrolladores de ASP.NET lo asumió con todos los cambios que implica.

Y esta implementación ha calado rápido y profundo, (ya se está desarrollando la segunda versión) ha llegado a la gente acostumbrada a Frameworks ligeros de desarrollo web que incluían sus propias implementaciones de MVC. Entre otras ventajas, ha ayudado a estandarizar la organización de los archivos del sitio y refinar las responsabilidades (Controladores -> Procesos de interfaz de usuario, workflow de la aplicación... Vistas –> mostrar la información de manera amigable y recoger los datos, validaciones de formato...).

Si deseamos acogernos a la implementación de Microsoft de dicho patrón deberemos ir encajando los problemas la funcionalidad de toda la vida sobre estas nuevas reglas de juego.

Uno de los requisitos clásicos más general es que nuestra aplicación disponga de características de globalización / localización. La idea general es la de siempre, evitar incluir ninguna referencia explicita a una determinada cultura y jugar con las características de globalización que nos ofrece el .NET Framework. Pero como ya sabemos, en Web todo se complica un poquito más. Al correr sobre un protocolo sin estado como HTTP recae sobre nuestra aplicación recordar la cultura especificada por el usuario entre las distintas peticiones.

Entonces tenemos clara la misión:

Mostrar los elementos de la UI acordes a la cultura que el usuario especifique.

Suena fácil, no? Pues lo es, si somos un poco cuidadosos…

(Por cierto, os dejo al final del post el código de la solución para que os lo podáis descargar y probar.)

1 - Estableciendo las bases, recursos a los ficheros de recursos.

Vamos a ir realizando un ejemplo paso a paso para evitar perdernos:

  1. Creamos un nuevo proyecto ASP.NET MVC Web Application.
  2. Incluimos en el proyecto la carpeta de App_GlobalResources.
  3. Incluimos un par de ficheros de recursos de acuerdo a la configuración de recursos por idioma que deseamos incluir.

    La convención de nomenclatura de los mismos dice que la primera parte del archivo es el nombre base. La segunda especifica la cultura (puede también ser solo el lenguaje que la forma). Esta última parte es opcional, entendiéndose como el juego de recursos predeterminado en caso de no exista.

    Es responsabilidad del .NET Framework enlazar automáticamente (en base a los ficheros de recursos especificados) el fichero adecuado con a la cultura especificada en actual hilo de ejecución.

    Por ejemplo, creamos el fichero predeterminado (en Inglés) y otro para español sin tener en cuenta el país. (MLTest.resx y MLTest.es.resx (para cualquier cultura con lenguaje español))
  4. Incluimos en el conjunto de ficheros de recursos todos los recursos que nuestra aplicación va a necesitar.
    En nuestro caso solo uno con nombre "GREETING".
  5. Incluimos desde una vista ( por ejemplo Views/Home/Index.aspx) la forma de consumir el recurso:

    <%
    =Resources.MLTest.GREETING%>

    Fuertemente tipado, Intellisense, detección de errores en tiempo de compilación...(da gusto verlo!!)

Bueno, pues ya tenernos una aplicación que nos muestra los recursos en base a la cultura del Framework instalado (en el servidor Web en este caso) que se encarga de especificar la cultura del hilo de ejecución.

Ya es algo, pero no es lo que queremos, verdad?

(El que dude que revise la misión que hemos puesto arriba)

2- Estableciendo la cultura del hilo en ejecución

Ya sabemos que debemos establecer la cultura del hilo de ejecución para que el Framework cargue el fichero de recursos adecuado, pero ahora la pregunta es cuando?

Recordar que cada petición empieza de nuevo y no recuerda las anteriores ( HTTP es stateless). Indagando un poco en la MSDN podemos ver que la clase Controller (de la que derivan nuestros propios controllers) de la implementación del MVC de Microsoft especifica dos métodos virtuales que nos van a resultar muy interesantes:

  • OnActionExecuting
    Se ejecuta antes de la llamada a cualquier Action Method.
  • OnActionExecuted
    Se ejecuta después de la llamada a cualquier Action Method. (obvio, no?)

Como debemos de establecer la cultura cada vez que nos realicen una llamada a cualquier action method de cualquier controlador, lo ideal es crear un controlador base del cual hereden todos los demás y sobrescribir en el método OnActionExecuting el mecanismo para restablecer la cultura. Nuestros controladores harán así transparente al resto de la aplicación las particularidades de trabajar con estado. También podríamos usar un atributo aplicado sobre la clase base y en la implementación del atributo especificar que implementa el interfaz IActionFilter si lo queremos hacer un poco mas AOP que OO.

En la implementación que os dejo utilizo un atributo con el que decoro la clase base (Al gusto!!)

En el ejemplo:

La clase base de los controladores:

[SetCulture]
public class BaseController : Controller
{
...
}

Y en la implementación de la clase del atributo programo como establecer la cultura seleccionada en el hilo activo:



public class SetCultureAttribute : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext
actionContext)
{
CultureHelper.SetCulture(GetCurrentCulture
(actionContext), actionContext.
HttpContext.Session)
}
...


Para almacenar la cultura especificada entre las llamadas podemos emplear diferentes mecanismos, en este ejemplo el flujo de trabajo será el siguiente:



  1. Revisar si tiene alguna cookie de nuestro sitio que especifique la cultura.

  2. Comprobar la variable de sessión de la cultura.

  3. En caso de no disponer de ninguna de las dos informaciones anteriores, preguntar por las culturas configurados en el navegador y presentar la interfaz de usuario en base a la primera coincidencia que se produzca.


En la implementación que os dejo del proyecto podéis consultar los métodos que he creado para realizar dichas acciones, son muy sencillos y podéis modificarlos al gusto.

Los principales métodos son:



  • GetCurrentCulture de la implementación del atributo, que consulta la información previamente almacenada en base al flujo que hemos comentado.

  • CultureHelper.SetCulture que recibe la cultura a asignar y el objeto intrínseco Session Object y carga cultura en el hilo actual de ejecución.


Aquí nos aparece otro problema.(Que raro, eh?)

Si el usuario es la primera vez que accede a la Web no tiene cookie ni session establecida.

Para intentar que la aplicación sea lo más “inteligente” posible, recogemos del navegador las culturas que tiene especificadas y las cruzamos con una sección especifica del fichero de configuración creada a tal efecto.

Por ejemplo, si el usuario tiene Chino e italiano necesito determinar si mi aplicación dispone un algún fichero de recursos que da soporte a alguno de los mismos. Para ello me creo una section en el fichero de configuración y un handler que lo soporte que especifique una lista de culturas soportadas y su orden de aplicación. (Así vemos también como poder realizar esta tarea.)

Observar que la tarea no es tan sencilla como coger el idioma y asignarlo al hilo y dejar que el Framework resuelva. (si no lo puede resolver que aplique la predeterminada) puesto que si el usuario tiene, entre todos los lenguajes del navegador, uno que tenemos implementado nos lo estaríamos saltando. (Por ejemplo Chino, Italiano, Español, Ruso)


En el fichero de configuración especificamos:

<
configSections>
...
<section name="EnabledLanguages" type="AndoniArroyo.MLTest.
Controllers.Infrastructure.Configuration.EnabledLanguagesSection,
AndoniArroyo.MLTest"
/>

</
configSections>
...

además incuimos la citada sección:


<!--Especifica el conjunto de culturas con el que trabaja la aplicación. La última es la cultura por defecto-->
<EnabledLanguages>
<Languages>
<add title="Spanish" code="es-ES" />
<add title="English" code="en-GB" />
</Languages>
</EnabledLanguages>


Para que la aplicación pueda manejar esta section debemos implementar el handler que la maneje:


namespace AndoniArroyo.MLTest.Controllers.Infrastructure.
Configuration
{
public sealed class EnabledLanguagesSection : ConfigurationSection
{
[ConfigurationProperty("Languages", IsDefaultCollection = true,
IsRequired = true)]
internal LanguageElementCollection EnabledLanguages
{
get { return (LanguageElementCollection)this["Languages"]; }
}
}
}

Estos lenguajes será los que tendrá en cuenta el método que intenta cargar el lenguaje desde la información recogida del navegador.


Pues ya casi lo tenemos.

Nos queda por ver como el usuario puede explicitamente modificar la cultura con la que desea trabajar en la aplicación. Visto todos lo anterior la cosa es fácil, no? Basta con crear un action method de modificación de la cultura y en la implementación del mismo llamar explícitamente al método de establecer cultura con el parámetro recibido. (CultureHelper.SetCulture pasándole la cultura especificada por el usuario y la referencia al objeto de sesión...)


Recapitulando, hemos visto unas cuantas cosas:



  • Hemos observado los mecanismos de globalización que nos da el .NET Framework y como extenderlos para nuestras necesidades en web.

  • Definido una política de establecer la cultura activa de la aplicación intentando que la aplicación se adapte al usuario.

  • El punto donde cubrir las características de sin estado del HTTP para establecer la cultura especificada por el usuario.

  • Hemos creado secciones personalizadas en el fichero de configuración y hemos visto como manejarlas.


Y como extra lo prometido el ejemplo con todas estas ideas en acción.


miércoles, 6 de mayo de 2009

8 maneras de cargarte la metodología de un plumazo

No es fácil implantar una manera común de desarrollar proyectos de Software. Cada maestrillo tiene su librillo y no siempre nos es fácil adaptarnos al mínimo común necesario para trabajar en equipo de manera orquestada.

Por contra, lo que sí que es realmente fácil , es dinamitar el camino andado. Hace falta muy poco para saltarse alguna que otra regla y echar abajo lo ganado con tanto esfuerzo. Existen algunos puntos claves en los que los equipos somos especialmente propensos a ceder, veamos algunos…

(*Hablando en SCRUM)

  1. No preparar suficiente Product Backlog para que el equipo adquiera prespectiva del proyecto.
    Aunque parezca mentira a los desarrolladores nos gusta saber qué leches estamos programando. De hecho las mejores propuestas de mejora de las aplicaciones surgen, al menos en las primeras fases del proyecto, del propio equipo de desarrollo. Para poder aprovechar toda esa energía/sinergia los desarrolladores deben poder hacerse una idea global del proyecto.
  2. No profundizar lo suficiente el el Sprint Planning Meeting por cansancio o falta de concrección en la exposición del Product Owner.
    La reunión previa en cada sprint permite al equipo hacerse una idea lo más concreta posible de las funcionalidades que más valor aportan al proyecto. Nos permite alinearnos con las necesidades del cliente de tal forma que todos aunemos esfuerzos en la misma dirección. También permite mitigar los riesgos e identificar sorpresar antes de asumir un compromiso. Un buen hábito que no siempre se realiza es el de poner una meta al sprint ( por ejemplo “Disponer del sistema de automatización de informes” ) que identifique la situación ideal al final del mismo y ayude al desarrollador a responder preguntas que le “tienten” por el camino. (¿Es esto importante para la meta del sprint?)
  3. Falta sistemáticamente al Daily Scrum o no respetar sin aviso las pocas liturgias que especifica SCRUM.
    Una de las características que hace a SCRUM “medio” bala de plata tan demandada por los desarrolladores, es las pocas imposiciones que propone. De éstas, una de las más importantes, es la de realizar una pequeña reunión diaria en al que cada miembro del equipo explica que ha hecho el día anterior, que impedimentos se ha encontrado y que pretende hacer en el mismo día de la reunión. Este Daily Scrum es el que da jabón al equipo y permite que todo el mundo este al día del avance, evitar duplicar batallas…
  4. Estancar conocimiento asumiendo un mismo desarrollador todas las Sprint Backlog Item de un Product Backlog Item.
    Es importante que nadie sea imprescindible. Que todos los miembros del equipo desarrollen tareas de todos los Product Backlog Items permite evitar que se creen “Reinos de Taifas” y estandariza el código. Además los equipos de desarrollo con un poco de verguenza torera se igualan por el mejor, lo que permite que se produzca aprendizaje por el camino, mejorando la calidad final del proyecto.
  5. Inventarse tareas sobre la marcha o modificar las existentes bajo demanda.
    Cuando un equipo de SCRUM asume un compromiso con el cliente a través de un sprint, dicho compromiso es inmutable por ambas partes. Es por esto que los sprints son tan cortos, para permitir reorientar el proyecto entre sprints.
  6. Intentar esconder las desviaciones o sufrir el “sindrome del investigador“.
    Si existen desviaciones, lo mejor es saberlo y ser consciente de las causas que las han provocado. En la mayoría de ocasiones dichas desviaciones se pueden justificar y son el argumento principal para mejorar los procesos del equipo. Otro de los aspectos que debe afrontar cualquier metodología es lograr una adecuada visibilidad. Esta característica es la que debe permitir permite que los StakeHolders conocozcan la situación del proyecto. Adoptar una visibilidad adecuada permite detectar las desviacioes de manera temprana así como planificar las acciones orientadas a corregirlas. Como equipo debemos ser capaces de justificarlas y transmitir los motivos a los StakeHolders adecuados, así como las acciones y planes creados. Suele ser un buen hábito, especificar a nivel de Product Backlog Item las condiciones de aceptación, en las cuales se especifican las poscondicones necesarias para que una historia pueda darse por terminada. Por supuesto, deben cumplirse todas para que se pueda dar por finalizada.
  7. No retroalimentar Sprint Backlog Items después de realizarlos.
    Es verdad que siempre vamos con prisa, que existen muchas tareas y todas las excusas que quieras poner, pero para poder decir “Hecho” (y hecho es hecho!!) cada desarrollador debe actualizar como parte de la tarea el estado de los Sprint Backlog Items asociados. Mantener al día dicha información nos permite, analizar las desviaciones y observar la tendencia del sprint y el producto. Colateralmente nos ayuda a ganar y mantener la visibilidad del proyecto manteniendo a los diferentes StakeHolder lo más al día posible.
  8. Evitar las Sprint Retrospective o demostrar falta de compromiso.
    En SCRUM la unión y el compromiso del equipo es clave puesto que todos los miembre del mismo interactuan ampliamente con los demás. El Sprint Retrosprective es la liturgia clave para recibir feedback del mismo y aumentar la unión y sentimiento de grupo. Estas reuniones pueden mejorar mucho los procesos afinando el día a día del equipo, identificando los posibles cuellos de botella y planificando como evitarlos.
    Como siempre, la clave es la constancia, no ceder al desaliento y aplicar las técnicas espartanas de confianza en tus compañeros de equipo. Solo así se puede lograr el “milagro” de echar a andar y mantener una forma de trabajo metodológica.

Como siempre, la clave es la constancia, no ceder al desaliento y aplicar las técnicas espartanas de confianza en tus compañeros de equipo. Solo así se puede lograr el “milagro” de echar a andar y mantener una forma de trabajo metodológica.

Publicado originalmente en Synergos

lunes, 27 de abril de 2009

La metodología (Ágil) va a llegaaaaar…

Como ya predijo el filósofo Arrabál, las cosas están cambiando.

Poco a poco, el sector del desarrollo del software se va haciendo un hombrecito. Desde luego este proceso no se ha postpuesto por falta de esfuerzos. La industria, cual madre concienciada, haciendo palanca a través de la Ingeniería del Software ha intentado estandarizar de muchas maneras el “artístico” proceso de crear software. Algunos de estos intentos han sido más forzados que otras (UML, CMMI, Métricas de calidad… ), pero casi todos han demostrado la misma poca efectividad.

Dichos intentos “encauzadores” de mama industria han quedado a medio camino por la misma razón: contaban con que el terreno sobre el que se apoyaban era firme, cuando realmente lo hacían sobre arenas movedizas. En relación a otras ciencias (y entornos productivo-económicos) el desarrollo de software avanza muy rápido.

Alcanzar esta velocidad de crucero implica que hay mucho que mejorar y obliga al sector a reinventarse contínuamente. La tecnología, los modelos de negocio, el alcance de la funcionalidad de las aplicaciones, la automatización de procesos, la integración y orquestación de unidades de negocio… todo está en expansión. Como todos sabemos, no se puede estandarizar lo que no deja de cambiar, por lo que los intentos realizados se han ido quedando anticuados antes de alcanzar su madurez.

Pero en ese reinventarse ha cambiado la prespectiva…

Un buen día nos damos cuenta de que estamos peleando contra la realidad. Nos liberamos y dejemos de atacar y evitar el cambio como un enemigo. Es una actitud provocada por el convencimiento que surge desde dentro de que no hay otra opción. Debemos asumir (cuanto antes mejor) el cambio como algo que está ahi, nos guste o no, no podemos evitarlo. ¡¡Todo cambia!!

En las metodologías “predictivas” se destinan gran parte de los recursos a ser capaz de predecir lo que se va a desarrollar en los próximos años. Por muy concienzudo que sea dicho estudio, el contexto varía, las necesidades del cliente cambian, surgen nuevas oportunidades y riesgos. El que mayor capacidad de cambio demuestre, contará con una ventaja competitiva significativa.
Por lo tanto, permitir que nuestro cliente marque el siguiente paso (o mejor dicho, pasito) a dar nos permite ajustar el camino a recorrer a las necesidades que sobre la marcha surgan, aportando un mayor valor al software y satisfacción al cliente, que es nuestro objetivo… ¿o no?


Publicado originalmente en Synergos

miércoles, 15 de abril de 2009

.NET Framework 4.0 y C# 4.0 (1 de 5)

Podemos incluir las nuevas caracteristicas que se incluyen en C# 4.0 en cuatro grupos principales:

  • Dynamic lookup
    Nos permite escribir métodos, operadores, acceso a propiedades y campos y cualquier invocación a objetos que se salte la comprobación estática de tipos de C# resolviendose en tiempo de ejecución. (¿esto es bueno o malo...? ya hablaremos, pero lo bueno es poder elegir, no? )

  • Parámetros con nombre y opcionales
    A partir de C# 4.0 los parámetros puedrán ser especificados como opcionales, asignandoles un valor por defecto en la declaración de miembro. Por supuesto, cuando el miembro sea invocado, los argumentos opcionales pueden ser omitidos. Los de VB.net que dejen de sacar pecho, porque además cualquier parámetro podrá ser pasado por nombre en lugar de posición. (nada de opcionales al final...)

  • Caracteristicas especificas de Interoperabilidad con COM
    La combinación de las dos mejoras comentadas anteriormente permiten hacer más cómoda la programación contra objetos COM. Además en el framework 4.0 se incluirán otras pequeñas características que mejorarán aún más esta "dura" experiencia.

  • Varianza y Contravarianza
    Quien haya profundizado en la O.O. usando genericos se dará cuanta de lo necesaria que era esta caracteristica. Ahora C# admite "co y contravariance" (por ejemplo Lista de objetos <-> Lista de string). Además los tipos de la B.C.L. son actualizados para que tengan en cuenta esta característica.

Continuaremos profundizando en posteriores posts.

martes, 7 de abril de 2009

.NET Framework 4.0 y C# 4.0 (0 de 5)

Visual Studio 2010 y el .NET Framework 4.0 estarán pronto en beta y con ellos se incluirán nuevas características. Una de ellas, es la nueva versión del lenguaje de programación C# (4.0, que se dice pronto!!). Para ponernos un poco en situación voy a escribir una serie de post en los cuales reflejar las evoluciones más significativas del mismo.

Para hacer las cosas como se deben vamos a hacer un poco de historia:

El equipo C# comienza en 1998 con el objetivo de crear un nuevo lenguaje de programación simple, moderno, orientado a objetos y type-safe (seguridad de tipos) para la plataforma .NET.

Microsoft lanza la plataforma .NET y el lenguaje de programación C# en verano de 2000 con gran éxito puesto que a día de hoy es uno de los más usados.

Uno de los principales éxitos de C# ha sido las buenas fuentes en las que se basa ( C++, Java...) así como el continuo y rápido proceso de evolución que la gente del equipo responsable de Microsoft le aplican.

Con la versión 2.0 del lenguaje se incluye el soporte para genericos, metodos anónimos, iteradores, tipos parciales y tipos nulables.

En la siguiente versión, la 3.0 el lenguaje se centra en habilitar LINQ para lo cual requiere todo lo anterior y una serie de nuevas características:
  • Variables locales de tipo implicito.
  • Métodos de extensión. (se emplea para implementar los proveedores de LINQ de manera debilmente acoplada)
  • Expresiones Lambda. (de esto ya hablaremos en otro post)
  • Inicializadores de objetos y colecciones.
  • Tipos anónimos.
  • Arrays implicitamente tipados.
  • Expresiones de consulta y árboles de expresiones.
Ahora que hemos conseguido un poco de prespectiva comenzaremos a hablar en el siguiente post de las novedades de C# 4.0.

Nota:
El nombre de C# es un guiño a modo siguiente evolución de C++. (C con 4 +)
C =>C++=>C++++

miércoles, 10 de diciembre de 2008

Como incluir un fichero en el resultado de una prueba unitaria en Visual Studio 2008

Existen ocasiones en las que nos puede interesar que una prueba unitaria incluya un determinado recurso. Esto ocurre cuando el código que deseamos testear cuenta con la existencia , por ejemplo, de un determinado fichero de texto, una hoja de cálculo o una Service-based Database (.mdf)

Como sabrán, el resultado del proceso de test genera una nueva carpeta (TestResults) donde incluye el resultado de cada generación. Si dicho resultado cuenta con una referencia relativa (la mejor manera de hacerlo) hacia un recurso pues ya la tenemos liada...

Pero como siempre (o casi) la solución es más facil de lo que parece. Existe un decorador especialmente creado para esta necesidad. Basta con colocar sobre el método que deseamos incluya el recurso el item de despliegue de la siguiente manera:

[TestMethod()]
[DeploymentItem(@".\fichero.xls")]

public int EjemploTest()
{

...

(.\ si está en la raiz claro...)

Por supuesto los más inquietos ya se estarán preguntando como evitar tener que incluir este decorador en todos los métodos (especialmente interesante si se trata de una Service-based Database (mdf)).

Pues para esto también existe una facil solución. Basta con incluir el recurso en el fichero LocalTestRun.testrunconfig dentro del elemento Deployment con la siguiente sintaxis:


\[Nombre del proyecto]\[Nombre del recurso]

y ya podemos quitar el decorador.

Como siempre, espero que les sea de utilidad.

Un saludo.

Extra ball:
Si desean incluir un fichero Service-based Database (mdf)  en una prueba unitaria y referenciarlo con un connection string del estilo: "..AttachDbFilename=|DataDirectory|.." asegurense que tienen instalado el SP1 de VisualStudio 2008.  Si este, la referencia se realiza directamente contra en .Net Framewok y no encontrará el recurso.


lunes, 29 de septiembre de 2008

Microsoft + JQuery ! (con el MVC la Santa Trinidad)

jQuery va a formar parte de la plataforma oficial de desarrollo de Microsoft.

Por cierto, jQuery es una librería de Javascript que permite simplificar la gestión de manipulación de documentos HTML a través del D.O.M. gestión y manejadores de eventos, animaciones...

Esta unión va a permitir que jQuery se distribuya con el Visual Studio. La idea es incluirla sin modificaciones (a ver si es verdad!!) de tal manera que se permita al equipo de desarrollo de jQuery continuar su propio avance de manera independiente.

Además los desarrolladores de Visual Studio podrán disponer de intellisense para jQuery (hay rumores que comentan que estará disponible como una descarga gratuita independiente en unas semanas)

Personalmente he desarrollado algunos proyectos con jQuery y la experiencia ha sido positiva por lo que me parece una conjunción positiva.

Un saludo.

viernes, 27 de junio de 2008

Ejemplo de globalización

He recibido la notificación de que desde la Universidad de Panamá, concretamente la facultad de Informática, electrónica y comunicaciones ha empleado información de este blog para realizar un estudio sobre entornos de programación.

Me alegro que les haya servido de ayuda.

Un saludo "transoceánico".

martes, 24 de junio de 2008

Cuando emplear declaración implicita de tipo de variables en .net

En C# 3.0 es posible descargar en el compilador la tarea de determinar el tipo de una variable. Dicha labor de inferencia se realiza en tiempo de compilación por lo que desde el punto de vista de la aplicación generada (del IL en adelante) se mantiene la verificación estricta de tipos.

La palabra reservada para declarar de este modo las variables es "var".

Ahora la pregunta es cuando usarla y cuando no. Porque exista esta caracteristica no vamos a volver locos, por lo tanto mi recomendación es usarla solo y exclusivamente cuando sea estrictamente necesario.

Esto es, cuando vayamos a crear una variable de un tipo que no existe y no queremos reutilizar más alla del ámbito de vida de la misma.

En cualquier otro caso, como siempre, especificamos en tiempo de diseño el tipo porque mejora la legibilidad y nos cuesta lo mismo.

Además aligeramos el proceso de compilación y evitamos posibles errorres de programación que se apoyen en el funcionamiento de la inferencia del compilador.

Dicho queda, un saludo.

Palabra de consultor...

martes, 10 de junio de 2008

SCRUM o como sobrevivir entre Sprints...

En mi empresa estamos introduciendo la metodología ágil SCRUM así que hemos pasado a vivir entre Sprints e incertidumbre. Existen algunas características que "obligan" al equipo a centrarse en las tareas y a unificar los esfuerzos.

Cada tres semanas nos comprometemos a una entrega con el cliente, priorizada por el, cada día hacemos una reunión a primera hora donde revisamos como van las tareas que nos hemos auto-asignado así como los impedimentos que nos hemos ido encontrando.

En contra de lo qe pueda parecer, el equipo trabaja más unido que nunca, la compenetración y colaboración es máxima y la eficiencia aumenta día a día.

Ya iremos hablando más de SCRUM que da para mucho.

Un saludo

miércoles, 28 de mayo de 2008

Carga diferida de entidades en el Entity Framework

Alguna duda que se repite cuando se trabaja (o mejor dicho todavía se trastea...) con Microsoft Entity Framework es la posibilidad de realizar cargas diferidas de entidades en base a la navegación que sobre el E.D.M. se va realizado.

Imaginemos que tenemos una entidad Curso y otra Alumno. Supongamos que existe una relación de navegación de 1 a * entre ambas. (Relación de asociación a nivel del CSDL). La cuestión es si podemos recuperar los cursos (solo los cursos) y en base a nuestras necesidades avanzar por alumnos automatizando la carga de los mismos a través del mecanismo de carga diferidad "orquestado" de manera automática por el Entity Framework.

La respuesta es NO.

La respuesta oficial del equipo de desarrollo de ADO.net de Microsoft es que ellos bajo ninguna circunstancia permiten que el Entity Framework tome la iniciativa y acceda a la Base de Datos subyacentce sin la autorización expresa del desarrollador. (Lease esto lease paso del tema que es un patín...). De hecho esta funcionalidad ha sido desestimada (al menos de momento) por lo que no está planificada en el roadmap del producto.

Palabra de ADO.net Developer Team...

Saludos.

martes, 27 de mayo de 2008

Entity Framework SP1 (o como no debe usted implementarse su propio O.R.M. a estas alturas...)

Con el reciente lanzamiento de la Beta del Visual Studio 2008 SP1 por parte de Microsoft me he decidido a investigar la nueva versión del Entity Framework que en el se incluye. Tengo que decir que aunque la cosa todavía esta un poco verde promete.

Se caia de maduro que Microsoft necesitaba cubrir un nicho en la que las soluciones de terceros (Hibernate, Neo, Codus...) se le habian colado en su bonito .NET Framework: Los Object Rational Mapping (ORM) y los servicios de consultas contra dichos objetos.

Microsoft ha realizado intentos, timidos pasos hacia lo que hoy es (o mejor dicho mañana será) el Entity Framework. Se probó con el DataSet, que prometia ser la revolución y se quedo por el camino, muy mal acompañado del XSD para definir modelos. Las especificaciones completas de XSD además de ser infumables, se olvidan por completo de hablar de servicios como la persistencia. Así que no es el lenguaje optimo para hablar de entidades que queremos trabajen con el ORM.

De hecho, existe una nueva especificación para definir los modelos conceptuales (CSDL), de almacenamiento (SSDL) y mapeo el E.D.M.(Entity Data Model). Dichas especificaciones cubren ambos mundos de manera muy natural (puesto que para eso ha sido creado, dicho sea de paso)

Disponer de un ORM (entre otras funcionalidades) estandar y mantenido por Microsoft es un tema a tener muy en cuenta. Yo por mi cuenta, me cree hace un par de años una implementación propia de un ORM común para mis proyectos. Pero no solo yo, es que casi todos los colegas con cierta carga de proyectos se han ido haciendo las suyas propias formando una torre de Babel de lo más poco práctica.

Y el problema no solo es hacerlo sino mantenerlo (y más rápido que Microsoft) desviando esfuerzo de la implementación de sus propias aplicaciones. Por eso es tan interesante el Entity Framework.

Por supuesto, existe la guinda del pastel en forma de LINQ to Entities que es una implementación del proveedor de LINQ para trabajar contra objetos de tipo Entidad (Definidos en el E.D.M.) e incluso una implementación de SQL llamada eSQL(subconjunto muy limitado (sin instrucciones DML para la release 1)) para atacar directamente al nuevo cliente EntityClientProvider que se coloca sobre los proveedores de todas la vida, abstrayendo de sus particularidades.

En este aspecto coincidimos todos los que "parimos" cualquier implementación mejor o peor (que de todo hay no se vayan a crerer) de ORM, en cuanto madure un poco, al trastero con las soluciones propias y a trabajar con el Entity Framework.

Como ven hay muchas cosas interesantes pero ya las iremos viendo poco a poco.

Un saludo.

Razones para evitar la programación en cliente en Aplicaciones Web

Existen algunas razones para intentar evitar la programación en el lado del cliente:
  • Aislamiento
    El código del lado del cliente no puede acceder a los recursos del servidor. Por ejemplo, una aplicación que se ejecuta en el cliente no tiene una manera sencilla de leer un fichero o interactuar con una base de datos del servidor (al menos no sin caer en problemas con la seguridad y compatibilidad de navegadores)

  • Seguridad: Los usuarios finales pueden ver el código de cliente. (Y cuando digo pueden es SIEMPRE pueden solo varia el tiempo que tienes que invertir en poder ...) Por lo tanto un usuario malintencionado al entender como funciona la aplicación puede "trampearla" más a menudo.
Un saludo

lunes, 19 de mayo de 2008

En que ando metido...

Hace tiempo que no cuento nada por aqui, así que voy a comentaros en que investigaciones ando metido ...

  • Entity Framework
  • WPF
  • WCF
  • Mock
  • SCRUM
  • PoliceInjection
  • MVC con comandos en WPF
  • JQuery
  • ...
Ya os iré constando cosillas interesantes.

Saludos

sábado, 5 de abril de 2008

Programadores

Los restrictivos trabajos de las grandes compañías son especialmente duros para los programadores, porque la esencia de la programación es construir cosas nuevas. La gente de ventas realiza las mismas presentaciones muchas veces, el soporte responde repetidas veces a las mismas consultas, pero cuando has escrito una pieza de código no quieres volver a escribirla de nuevo.

Por lo tanto, un programador trabajando como lo que significa ser programador están pensando siempre en hacer cosas nuevas.

Un saludo a todos los compañeros programadores!!