Noticias Weblogs Código

Cuaderno de software

Accidental versus essential complexity

July 23, 2014 07:02 AM

¿Cuántas veces te pasa que, al tener implementada la solución a un problema, te das cuenta de que el problema era mucho más fácil de lo que parecía y que ahora podrías implementar una solución mucho más sencilla? ¿Cuánta de la complejidad de una solución es inherente al propio problema y cuánta se produce por la propia solución planteada?

La complejidad intrínseca de un problema es difícil de abordar. No la has creado tú. Tienes que convivir con ella. No puedes actuar sobre ella a corto plazo. Es verdad que con el paso de los años, van surgiendo técnicas que ayudan a disminuirla. Pero sólo con el paso de las décadas se dan mejoras significativas.

La complejidad accidental de un problema es mucho más fácil de abordar. La has creado tú (o tu organización, o tu cliente…). Puedes actuar sobre ella a corto plazo y disminuirla de manera significativa.

La buena noticia es que casi toda la complejidad de lo que hacemos es complejidad accidental. Ocurre en los procesos que nos autoimponemos, en los análisis y diseños que hacemos y en el código que escribimos. Tenemos la certeza de que:

  • normalmente cuando planteamos un problema no llegamos al fondo del POR QUÉ o PARA QUÉ se está abordando ese problema. Lidiamos muchas veces con los efectos del problema en lugar de ir a la causa.
  • siempre que se plantea un proceso ligero, en pocos pasos, éste tiende a complicarse con el tiempo. Cada vez que ocurre una excepción que el proceso no contempla, se añade una nueva fase al proceso sin tener en cuenta que la mayoría de las veces que se ejecute, al no darse esa excepción, esa fase será una pérdida de tiempo.
  • cuando programamos, planteamos una serie de abstracciones en función de nuestro entendimiento del problema. Cuando solucionamos ese problema, adquirimos un entendimiento del mismo que se puede adquirir de ninguna otra manera que no sea, precisamente, hacer el acto de solucionarlo. Ese entendimiento nos hace ver que las abstracciones que planteamos no iban a la esencia y por lo tanto son demasiado complejas.
  • el diseño de interfaces es especialmente clave en estos casos. Las interfaces normalmente reflejan los procesos de negocio. Si el proceso es complejo, la interfaz es compleja. Si la interfaz es compleja, la programación que haya por detrás también.

En desarrollos iterativos e incrementales, si de base planteamos procesos, interfaces y código complejos, la complejidad del problema lo único que hará es crecer de manera exponencial.

Si uno es consciente de esto, puede actuar sobre ello. Por eso es importante establecer mecanismos que busquen la simplicidad y la esencia de las cosas. Una lista no exhaustiva sería:

  • pregúntate POR QUÉ y PARA QUÉ 5 elevando a la 5 veces. Usa técnicas de análisis de causa raíz.
  • plantea siempre la solución más sencilla que se te ocurra. Iterativamente, intenta reducirla al absurdo y usa a expertos para que le tiren piedras. Si no se cae, es la solución a implementar.
  • antes de automatizar procesos, replantea los procesos. Si automatizas la mierda, tendrás mierda automática.
  • refactoriza el código. Refactorizar es entender que generamos complejidad accidental y que una vez vista la solución existe una manera de hacer las cosas más sencillas. También es entender que no simplificarlo ahora, implica generar más complejidad en el futuro.

» Leer más, comentarios, etc...

Variable not found

#if DEBUG en Javascript (bueno, o algo así)

July 22, 2014 11:55 AM

DebugHay veces que desde Javascript nos interesa ejecutar un código u otro en función de si la ejecución se está produciendo en un servidor de desarrollo o en uno de producción. Por ejemplo, en el primer caso suele ser interesante disponer de logs o herramientas de ayuda a la depuración, mientras que en el segundo lo normal es que queramos introducir código más eficiente y sin este tipo de condimentos.

En este post vamos a ver algunas técnicas muy básicas que nos permitirán ejecutar un código u otro en el lado cliente de aplicaciones ASP.NET MVC (o ASP.NET en general) en función del modo de compilación.

1. El lado servidor

En el lado servidor es bastante fácil de conseguir porque disponemos de directivas de compilación que nos permiten detectar si estamos generando los binarios en modo depuración:
// HomeController.cs
public ActionResult Index()
{
#if DEBUG
ViewBag.CompilationInDebugMode = true;
#else
ViewBag.CompilationInDebugMode = false;
#endif
return View();
}

@* Home/Index.cshtml *@
<p>Compilation in debug mode: @ViewBag.CompilationInDebugMode</p>
También podemos conocer en tiempo de ejecución si ASP.NET está compilando en dicho modo (compilation debug="true" en el web.config):
@* In a Razor view: *@
<p>ASP.NET debugging: @Context.IsDebuggingEnabled</p>
Normalmente esta última vía será la que utilicemos, pues es la que suele indicar si estamos ejecutando sobre un servidor de desarrollo o no, básicamente porque el cambio en el web.config del valor del parámetro debug a false es uno de esos pasos que nos han recomendado desde el principio de los tiempos al pasar una aplicación a producción. Y seguro que todos lo hacemos, ¿verdad? ;-)

2. ¿Y el lado cliente?

En el lado cliente, debido a la propia naturaleza dinámica del lenguaje Javascript, no existe una compilación en modo depuración; bueno, de hecho no existe ni siquiera compilación como tal ;-), por lo que tenemos que recurrir a algunos truquillos para conseguir emular este comportamiento.

2.1. Cargar scripts diferentes en función del modo de depuración de ASP.NET

Una posibilidad muy sencilla, y válida en algunos escenarios, es tener un archivo de script diferente para cada modo de compilación. De hecho, es empleada por muchas bibliotecas Javascript, que se distribuyen en forma de archivo .js para la versión de producción, normalmente ya compactada y minimizada, y .debug.js para la versión a utilizar durante el desarrollo.

Por ejemplo, supongamos que tenemos los dos siguientes archivos:
// File: Myscript.debug.js (development script)
function log(msg) {
console.log(msg);
}

// =====================================

// File: Myscript.js (production script)
function log(msg) {
// Nothing to do
}
Para cargar manualmente un archivo u otro en función del modo de compilación bastaría con hacer lo siguiente a la hora de referenciarlo:
<script src="~/scripts/myscript@(Context.IsDebuggingEnabled? ".debug": "").js">
</script>
Sin embargo, si utilizamos bundles es aún más sencillo: de serie, el sistema de bundling ya distinguirá entre las distintas versiones de forma automática basándose en la extensión de los archivos. Por ejemplo, continuando con el caso anterior, al añadir un bundle como el siguiente el mismo componente de optimización seleccionará el archivo myscript.debug.js cuando la depuración de ASP.NET esté activada, y myscript.js en caso contrario:
bundles.Add(new ScriptBundle("~/bundles/myscript").Include(
"~/Scripts/myscript.*"
));
La referencia desde la vista en este caso sería así de simple:
@Scripts.Render("~/bundles/myscript")

2.2. Detección inline del modo de depuración

Pero no siempre el grano es tan grueso como en el apartado anterior. Muy frecuentemente no nos interesará sustituir completamente un script por otro en función del modo de compilación, sino conocer simplemente el modo en el que estamos para tomar una decisión a nivel de código. Esto nos permitiría escribir código como el siguiente:
function doSomething() {
if (DEBUG_MODE) {
log("Entering doSomething()");
}
// ... Code
if (DEBUG_MODE) {
log("Exiting doSomething()");
}
}
Por tanto, nuestro problema se reduce a establecer esa constante DEBUG_MODE a un valor que evalúe a cierto cuando estemos ejecutando sobre una aplicación compilada en modo depuración y a falso en caso contrario. Y aplicando lo visto anteriormente, podríamos conseguirlo de varias formas.

Una, realmente sencilla de implementar, consistiría en introducir un conciso bloque de scripts antes de cargar las bibliotecas o scripts que usen este valor, por ejemplo en el encabezado del layout o página maestra del sitio web, más o menos con lo siguiente:

@* Razor Layout *@
...
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My ASP.NET Application</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
<script>
window.DEBUG_MODE = @(Context.IsDebuggingEnabled? "true": "false");
</script>
</head>
...
Otra posibilidad sería establecer el valor de DEBUG_MODE en un script independiente del que tendríamos dos versiones, con extensiones .debug.js y .js, que respectivamente lo inicializarían a true y false, y usar cualquiera de las dos técnicas descritas anteriormente para cargar uno u otro en función del modo de compilación.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Cuaderno de software

Un entorno productivo en pareja

July 22, 2014 07:16 AM

IMG_1711

 

Ésta foto tiene ya un tiempo. Es de finales de 2013 (ya me cuesta publicar artículos, a ver si cojo un poco de inercia… :-P). En esa época a Guille y a mí nos cayó la primera versión de un potente software no-tan-big data, a hacer con AngularJS/Javascript, Elastic Search y AKKA, alrededor de un dominio que desconocíamos completamente y donde teníamos difícil encontrar a alguien que nos lo explicara, con una deadline inminente.

Si en un escenario aplica lo de typing is not the bottleneck, creo que es en éste. Con lo que decidimos que en lugar de estar levantándonos de nuestro sitio cada cinco minutos a preguntarnos cosas el uno al otro o tomar decisiones técnicas sin contrastar, íbamos a hacer todo el trabajo en pareja y favorecer el ancho de banda de la comunicación entre nosotros muy por encima del ancho de banda de tecleo.

En la foto se puede ver lo que para mí es un entorno ideal para trabajar el software:

  • un ordenador principal con dos teclados y dos ratones y espacio más que de sobra para estar cómodos y centrados frente a las pantallas.
  • un ordenador secundario. Esto es clave. En un escenario de incertidumbre máxima te atascas cada dos minutos y tienes que guguelear a mansalva. Si estás con un sólo ordenador, uno busca, y el otro tiene que andar aguantándose las ganas de buscar cosas distintas. Lo mejor es buscar en paralelo e ir comentando lo que se encuentra.
  • panel físico para gestionar el To Do. Según avanzábamos, íbamos redefiniendo una y otra vez el trabajo a hacer. Destruyendo post-its y creando unos nuevos con nuestra nueva visión de las tareas a realizar. Para nosotros era clave poder hacer esto sin los impedimentos de una herramienta electrónica como Jira, mucho más lenta. Máxime cuando la trazabilidad, en éste punto, no nos servía para nada.
  • pared pintable para discutir ideas y diagramar cosas. Tuvo mucho flow el poder discutir una idea, pintarla, e ir implementándola teniéndola siempre de referencia y pudiéndonos dar la vuelta en cualquier momento a discutir cualquier aspecto.

Es fácil enamorarse de ésta forma de trabajar. Y, al enamorarte, perder un poco la perspectiva. El sistema nos funcionó bien para su principal función: maximizar la información radiada y la comunicación entre nosotros como estrategia para abordar un producto de alta incertidumbre técnica y de dominio. Lo que no nos funcionó:

  • fuimos incapaces de mantener un sólo “to do” en el panel. Como éramos novatos en todo, aprendíamos una manera más decente de hacer las cosas cada media hora, y todo el código anterior nos parecía refactorizable (y lo apuntábamos al “to do”). Al final manteníamos el panel, un archivo todo.txt en el intellij, y las anotaciones //TODO:  en el propio código. Aunque planteábamos sesiones dedicadas sólo a fundirnos esas listas, no lo conseguíamos.
  • hacer pairing el 100% del tiempo trabajando a veces en jornadas de 10 horas es demasiado. Ya sé que esto, leído, puede sonar bastante evidente. Pero cuando estás dándolo todo, cansado, hay cosas de las que no te das cuenta. Por ejemplo, yo adquirí muchos conocimientos de “angularjs pasivo“. Como Guille es más rápido que yo pensando y empezó el proyecto manejando más de AngularJS, yo solía ser un mero espectador cuando encontrábamos una solución a un problema en JS. No soy manco: a veces se me ocurría la solución a mí… pero normalmente (especialmente cuando estábamos cansados) la implementaba él porque le suponía menos esfuerzo. Con lo que yo no terminaba de asimilar las cosas.

Lecciones aprendidas. Cómo y cuando hacemos pairing ahora:

  • hacemos pairing siempre que lo que nos impide avanzar es el conocimiento (ya sea técnico o de dominio).
  • hacemos pairing siempre que se va a tomar una decisión de diseño importante en la que se va a basar el proyecto (cómo vamos a organizar los componentes, si se va a usar tal o cuál patrón, estrategia de testeo, etc…)
  • siempre que hacemos pairing tenemos una lista preparada con el orden del día. Y sí: normalmente hacemos pairing de jornadas completas, preparando el entorno previamente para ser productivos y tener toda la información disponible y la posibilidad de tener discusiones productivas.
  • es importante no hacer pairing cuando quieres asimilar algo que has aprendido.

Como consecuencia de todo esto, en el último proyecto que hemos hecho juntos habremos hecho un día a la semana de trabajo en pareja. Al menos durante la fase principal del proyecto, acumulando en ese día las decisiones de diseño y la lista de cosas que queríamos aprender del otro. Inventándome la cifra, diré que habremos hecho un 10-15% de pairing.

¿100% o 15% de pairing? Para mí aquí ( como siempre) la clave es el contexto. Y el punto de vista desde el que analizar ese contexto, es el del conocimiento. Si analizo el proyecto en el que hicimos 100% de trabajo en pareja y soy sincero conmigo mismo, no creo que hubiéramos acabado antes ni mejor haciéndolo por separado. No creo que hubiera salido más barato ( argumento clásico en contra del trabajo en pareja). Sin embargo, si analizo el proyecto donde hemos hecho 10-15% de pairing, si que creo que más tiempo hubiera supuesto perder el tiempo. Y también sé que trabajar más por mi cuenta en AngularJS me ha hecho mucho más ágil e independiente.

 

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 167

July 21, 2014 02:26 PM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

Tres años de Koalite

July 21, 2014 07:39 AM

Este mes se cumple el tercer aniversario de este pequeño proyecto personal. Son tres años en los que he escrito un montón de tonterías (222, concretamente), he conocido a mucha gente, he discutido con casi todos los que se han dejado y, sobre todo, he aprendido mucho.

¿Por qué?

Al hilo de un reciente encuentro virtual entre varios autores de blogs, Rubén Fernández escribía en su blog “¿y por qué tengo un blog?“.

Hay muchos motivos para escribir un blog. Hay quien busca crear una marca personal, hacer marketing de contenidos para otros, o incluso intentar ganar algún premio, pero la mejor motivación que puedes encontrar es disfrutar escribiéndolo. Si no disfrutase escribiendo, estoy seguro de que no sería capaz de sacar un hueco todas las semanas para escribir un nuevo post.

Es verdad que mantener un blog ayuda a aprender cosas nuevas, a conocer gente interesante, a potenciar una “marca personal” (para los que les preocupe) y a unas cuantas cosas más. Esos factores pueden ser lo que te hagan a dar el paso para empezar a escribir un blog, pero cuando llevas un tiempo haciéndolo, es fundamental que disfrutes con ello.

Eso de escribir para disfrutar implica también escribir para uno mismo, y por eso cada vez es más fácil encontrar en este blog posts que no interesan a nadie (bueno, o a casi nadie), pero, sinceramente, disfruto más escribiendo eso que un tutorial sobre la tecnología de moda que tenga 500 veces más visitas (y hablo con conocimiento de causa ;-))

…y ¡vacaciones!

No se me ocurre mejor forma de celebrar este tercer aniversario que tomarme unas semanitas de descanso. Este año se me ha hecho muy largo, tanto personal como profesionalmente, y ya me va tocando desconectar (un poco, que si no me aburro), relajarme, y resolver algunos asunto más mundanos que llevo posponiendo tiempo, así que, por lo menos hasta mediados de agosto, no creo que publique mucho.

De todas formas, como se suele decir, la cabra tira al monte y seguro que durante este tiempo seguiremos discutiendo de vez en cuando en twitter.

¡Felices vaciones a todos!

lago_enol

Imagen de Dominik Morbitzer en flickr

No hay posts relacionados.

» Leer más, comentarios, etc...

Cuaderno de software

Panel Time Control

July 21, 2014 06:37 AM

Éste es un pequeño apunte para recordar un panel que nos ha funcionado muy bien a Guille y a mí en un desarrollo que hemos hecho a presupuesto cerrado.

la foto

 

El panel es una especie de Story Map traspuesto. Cada columna es una entrega (diseñamos una estrategia con dos demos y una entrega final). Cada fila es un vertical de la aplicación (un “context boundary” que se diría en DDD).  La primera celda de cada fila contiene un glosario de términos del vertical y una lista de dudas o incertidumbre a ir aclarando.

No dimos color a los post-it´s. Distinguimos la funcionalidad del spike o tarea técnica poniendo de título al post-it “spike”. Viéndolo en retrospectiva hubiera estado bien darles un color de post-it distinto. Lo que si distinguimos fue el feedback/detalle descubierto en las demos(post-it amarillo), de la funcionalidad original (verde). Para visualizar si una funcionalidad estaba terminada, simplemente la tachábamos.

Para visualizar las horas que habíamos empleado (clave en éste proyecto a precio cerrado) usamos el post-it fucsia. Cada semana actualizábamos las horas, moviendo el post-it hacia la derecha proporcionalmente. De tal manera que era fácil ver si estábamos en el último tercio de horas y todavía en el segundo tercio de funcionalidades (vamos tarde), etc…

En definitiva, nos ha funcionado muy bien para controlar el release plan y controlar el tema económico. También para gestionar la incertidumbre del proyecto y el detalle de las funcionalidades.

Creo que éste panel no funcionaría en cualquier contexto. El panel obedece a un escenario donde no hay bugs (porque todo el trabajo diseñado es anterior a que se ponga en producción de verdad el software) y donde al estar dos desarrolladores es fácil saber quién está haciendo qué.

 

» Leer más, comentarios, etc...

Blog Bitix

Anotación Cached de Apache Tapestry

July 19, 2014 07:00 PM

Apache Tapestry

Como he comentado en un artículo anterior sobre los modelos push y pull empleados en los motores de plantillas donde comentaba algunas diferencias entre ellos, en el modelo pull es la vista la que pide los datos al controlador y no el controlador el que proporciona los datos a la vista como se hace en el modelo push. Un problema que puede plantear el que la vista pida los datos al controlador es que si la devolución de los datos solicitados son costosos en tiempo del cálculo, carga para el sistema en CPU o memoria, o intensivos en entrada/salida de disco o red y se piden varias veces puede suponer como resultado que el tiempo empleado para generar la página sea elevado o la aplicación consuma recursos innecesarios.

Apache Tapestry que emplea el modelo pull dispone de la anotación Cached que permite cachear el resultado de un método a nivel de componente y página durante la generación de la misma. Su uso sería el siguiente:

<noscript><pre><code>package info.blogstack.pages; import info.blogstack.entities.Post; import info.blogstack.entities.Source; import info.blogstack.misc.Globals; import info.blogstack.misc.Utils; import info.blogstack.services.MainService; import info.blogstack.services.dao.Direction; import info.blogstack.services.dao.Pagination; import info.blogstack.services.dao.Sort; import java.util.ArrayList; import java.util.List; import org.apache.tapestry5.annotations.Cached; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.ioc.annotations.Inject; public class Label { private Object[] context; @Property private Integer page; @Inject private MainService service; @Property private Source source; @Property private info.blogstack.entities.Label label; @Property private Post post; void onActivate(Object[] context) { this.context = context; } Object[] onPassivate() { return context; } void setupRender() { String hash = Utils.getHash(new Object[] { context[0] } ); label = service.getLabelDAO().findByHash(hash); page = (context.length &gt;= 3) ? (Integer) context[2] : null; page = (page == null) ? 0 : page; } /** * Método que devuelve las articulos publicadas o actualizadas más recientemente de una etiqueta. */ public List&lt;Post&gt; getPosts() { List&lt;Sort&gt; sorts = new ArrayList&lt;&gt;(); sorts.add(new Sort(&quot;date&quot;, Direction.DESCENDING)); Pagination pagination = new Pagination(Globals.NUMBER_POSTS_PAGE * page, Globals.NUMBER_POSTS_PAGE * (page + 1), sorts); return service.getPostDAO().findAllByLabel(label, pagination); } @Cached(watch = &quot;label&quot;) public Long getPostsCount() { return service.getPostDAO().countBy(label); } public boolean isFirstPage() { return page == 0; } public boolean isLastPage() { return (page + 1 &gt; Globals.NUMBER_PAGES_LABEL || getPostsCount() &lt;= Globals.NUMBER_POSTS_PAGE * (page + 1)); } public Object[] getPreviusContext() { return (page - 1 &lt;= 0) ? new Object[] { label.getName() } : new Object[] { label.getName(), &quot;page&quot;, page - 1 }; } public Object[] getNextContext() { return (page + 1 &gt; Globals.NUMBER_PAGES_LABEL) ? new Object[] { label.getName() } : new Object[] { label.getName(), &quot;page&quot;, page + 1 }; } }</code></pre></noscript>

En este ejemplo cada vez que se llama a los métodos getPosts, getPostsCount se accede a una base de datos (o sistema externo) que lanza una consulta, supongamos, costosa de calcular o que simplemente es innecesaria hacerla varias veces. Usando la anotación Cached podemos hacer la aplicación más eficiente evitando las segundas llamadas a los métodos. Si el componente Label del ejemplo se usa dentro de un bucle de un componente loop y como parámetros se le van pasando varios labels las llamadas a los métodos getPosts y getPostCount se realizarán solo para cada valor diferente.

Algunas veces puede interesarnos que el cacheo dependa de un dato, es decir, que para cada valor de un dato la anotación Cached devuelva diferentes resultados. Y esto es lo que se hace en el ejemplo con el parámetro watch de la anotación, por cada valor de la propiedad label el resultado probablemente sea diferente pero nos interesa que el método solo se ejecute una vez por cada diferente valor, dado que los artículos y el número de ellos únicamente variarán en función de esta propiedad. Esto también puede ser usado para que solo se evalúe los métodos una vez por iteración de un bucle estableciendo la expresión watch al índice del bucle.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;t:remove&gt; Llamadas ádicionales para obtener los artículos, devolverán los mismos datos. &lt;/t:remove&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;/html&gt;</code></pre></noscript>

Aún así, la anotación Cached funciona a nivel de petición, cada vez que que se haga una petición a la aplicación y se llame al método anotado por primera vez y por cada valor de la expresión watch se ejecutará el método. Si tenemos muchas peticiones o un determinado componente tarda mucho en generar su contenido, por ejemplo, porque depende de un sistema externo lento (base de datos, http, …) quizá lo que debamos hacer es un componente que almacene durante un tiempo el contenido que genera y sea devuelto en múltiples peticiones, de modo que evitemos emplear un tiempo costoso en cada petición. Para ello, podríamos desarrollar un componente que usase una librería de cahce como por ejemplo EHCache.

Referencia:
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

Cuaderno de software

Cynefin: ¿complejo o complicado?

July 17, 2014 06:38 AM

Siguiendo un poco con la importancia de distinguir entre simple, complejo, complicado, etc… Es muy interesante el marco de decisión( o de análisis) Cynefin.

Cynefin_framework_Feb_2011

¿Cómo tomar decisiones o analizar un problema?

  • Si existe una relación clara entre causa y efecto, el escenario es simple. Puedes percibir la situación, categorizarla y, en función de la categoría, responder. Existen unas recetillas que te dicen en función de la categorización cuál es la mejor respuesta (best practices).
  • Si existe una relación entre causa y efecto, pero no está clara, el escenario es complicado. Requiere experiencia y análisis antes de actuar. Pueden existir unas buenas prácticas pero no una sola manera de hacer las cosas bien.
  • Si existe una relación entre causa y efecto, pero sólo eres capaz de establecerla a posteriori y, además, las consecuencias de lo que está ocurriendo no son predecibles, el escenario en el que te encuentras es complejo. La situación requiere probar varias cosas, coger feedback y actuar. La manera buena de hacer las cosas va emergiendo en función del feedback. No es conocida a priori.
  • Si no existe ninguna relación entre causa y efecto, ni siquiera analizando las cosas a posteriori, estás en una situación caótica. Probar para obtener feedback no sirve porque al repetir no necesariamente va a ocurrir lo mismo. La única posibilidad es dar un paso al frente, actuar y ver qué pasa. Aquí eres siempre un novato y no puedes extraer buenas prácticas en función de la experiencia.

Creo que en general nos movemos entre lo complejo y lo complicado. Y que es difícil saber cuando pararte a analizar bien las cosas y cuando no analizarlas tanto e intentar hacer algo para tener feedback rápido. Me gusta la manera en la que Cynefin enmarca todo esto.

Para un análisis muy bueno sobre Cynefin y Agilismo el post de Liz Keogh.. y ¡ojo a la respuesta de Jon Reffies!

 

» Leer más, comentarios, etc...

xailer.info

Controles de usuario

July 16, 2014 11:24 AM

Estimados usuarios de Xailer,

La futura versión de Xailer incorporará una nueva funcionalidad que creemos puede ser de gran utilidad para todos vosotros y de la cual estamos muy satisfechos por el resultado obtenido, que es la creación automática de controles de usuario.

Actualmente Xailer ya permite la creación de controles de usuario que se pueden utilizar posteriormente en el IDE y en sus aplicaciones finales. De hecho el proyecto \Samples\Controls\Samples.xpj es un claro ejemplo del mismo. Dicho ejemplo crea todos los controles que puede encontrar en la pestaña ‘Samples‘ de la barra de controles. Para su utilización tan sólo deberá incluir en sus proyectos la librería \Xailer\lib\Samples. No obstante, muy pocos usuarios se han adentrado en la creación de controles propios, seguramente por creer que se trata de un proceso complicado y tedioso.

Para facilitaros las cosas un poquito más la nueva versión de Xailer incorporará un completo sistema de creación de controles automático en base a cualquier control que tengáis de cualquier formulario. La idea es que todas las propiedades que hayáis indicado en dicho control, junto con los recursos que utilice, se conviertan en un control adicional que se pueda utilizar en cualquier proyecto actual o futuro.

El funcionamiento es muy sencillo: Seleccionamos primeramente el control personalizado que deseamos crear como control propio, pulsamos el botón contextual del mismo y seleccionamos la opción de ‘Creación de control de usuario…‘. A continuación se nos mostrará el siguiente diálogo:

UserControlDialog

Tan sólo deberá indicar el nombre de la nueva clase, el tooltip que se le mostrará cuando lo pre-seleccione en el IDE, la imagen que se mostrará en la barra de controles y el nombre de la pestaña donde se mostrará el nuevo control. Incluso puede dejar todos los valores por defecto si lo desea.

El IDE creará, si no existiese, un nuevo proyecto de nombre ‘MyControls‘ en su carpeta de ‘Archivos de usuario\Componentes‘ (ver opciones generales) e irá introduciendo todos los controles de usuario que vaya creando. Dicho proyecto será el responsable de crear la DLL MyControls.dll para el IDE y de las librerias para su uso bien por MinGW o Borland C++. Observe como en el diálogo de creación de control usuario existe una casilla para abrir este nuevo proyecto de forma inmediata.

Para poder empezar a utilizar sus nuevos controles deberá cargar en el IDE la librería MyControls. DLL utilizando la opción de menú Componentes->Gestor de componentes.

UserControlGestorComponentes

El IDE de Xailer controla de forma automática los recursos que se requieran. Cuando se crea el control de usuario, sus recursos son copiados al proyecto ‘MyControls‘ y cuando el control es utilizado en cualquier otro proyecto sus recursos son a su vez copiados desde el proyecto ‘MyControls‘ a su proyecto de forma completamente transparente.

Esperamos que con esta nueva funcionalidad ya no tengáis motivos para no crear vuestros propios controles. ;-) Cualquier comentario al respecto será bienvenido.

Un cordial saludo

» Leer más, comentarios, etc...

Arragonán

Semanas 317 y 318

July 15, 2014 04:13 PM

Cosas de ir liado de trabajo, tener un puñado de otros compromisos y una parte de pereza para ponerme a escribir cuando tenía un rato; se me ha vuelto a juntar una semana sin publicar la retro semanal.

En estos 15 días: inauguración de piso, Geek’s Talks, Slap!, cierre del trimestre anterior, consultobirra con los amigos de Certicalia, boda de amigos, gestionar algún tema de viejos proyectos, el UXZaragoza

A destacar el UXZaragoza, claro. Fueron 4 charlas que creo que se complementaron muy bien, y me pareció interesante que en cada una de ellas se hablara desde las experiencias propias.

Adriano y Pablo hablaron de cómo han ido integrando técnicas de UX con las de desarrollo ágil, cosas que les han ido funcionando mejor y peor durante los 4 meses que llevan trabajando en spines: Las cosicas del Lean UX.

Guillermo hizo una charla en la que habló más que nada sobre experimentar (o zancochar :)), probar herramientas, técnicas… que quizás no se vayan a usar a corto plazo pero de las que quizás podamos sacar partido en el futuro: La locura, esa facultad fundamental.

Daniel expuso como trabajos de consultoría que a priori son de diseño o UX de producto digital, terminan teniendo impacto en el mundo físico. Básicamente en cambiar flujos de trabajo que ayudan a mejorar la experiencia de usuario del cliente: Service design de pueblo.

Verónica nos estuvo hablando de tests con usuarios de laboratorio, del modo de observación, de análisis cualitativos y cuantitativos, de analizar los resultados, etc: Falla rápido y barato, falla investigando.

Podéis ver la actividad en twitter con #uxzaragoza.

Y acerca de los trabajos en marcha:

  • Vuelta a dedicar algún tiempo a gestiones para proyectoSinNombre (un día igual lo cerramos y todo…).
  • Empecé ya con Nort, nuevo proyecto al que mayor dedicación le voy a dar durante los próximos meses. Entro a trabajar a modo de contractor con un equipo de varias personas con quienes estaremos desarrollando la aplicación usando Grails. Sobre el cliente y la temática del proyecto no puedo hablar, al menos por el momento.

    Ya hemos hecho el primer sprint, empezamos a implementar historias de usuario, hemos hecho la primera demo, la primera retro… y ver que, evidentemente, tenemos muchos puntos de mejora.

  • He hecho algunas cosas en mhop. Le he dedicado menos disponibilidad, pequeños detalles y algunas reuniones sobre el camino a seguir pensando en el medio plazo.

Buena semana.

» Leer más, comentarios, etc...

Variable not found

Novedades de SignalR 2.1 (y III): progreso de acciones

July 15, 2014 03:18 PM

image[2]La revisión 2.1 de SignalR ha incluido otra característica que puede ser interesante en determinados escenarios: la posibilidad de notificar al cliente del progreso de la ejecución de una acción invocada en el hub.

Aunque en los métodos que normalmente implementamos no tiene demasiada utilidad, sí puede ayudar a mejorar la interacción y experiencia de usuario cuando se trate de ejecutar métodos costosos como el siguiente:
public class GodsOfTheForest: Hub
{
[...]
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything()
{
for (var i = 0; i < 10; i++)
{
await Task.Delay(1000);
// Do something very important here
  }
return 42;
}
}
Hasta ahora, si desde el cliente llamábamos a este método del hub, sólo podíamos tomar el control cuando la llamaba finalizaba, bien obteniendo el resultado si todo había ido bien, o bien capturando el error producido. Por ejemplo, en Javascript lo conseguíamos con un código como el siguiente:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
.done(function (result) {
console.log("The answer is: " + result);
})
.fail(function(err) {
console.log("Oops, something went wrong: " + err.message);
});

Notificación de progreso en SignalR 2.1

Para notificar el progreso de la ejecución en un método del hub, a partir de SignalR 2.1 tenemos que hacer básicamente dos cosas:
  • En primer lugar, añadir al método del hub un parámetro de entrada adicional de tipo IProgress<T>, que obligatoriamente debe ser el último del método. T es el tipo de dato que vayamos a utilizar para notificar el progreso al cliente; por ejemplo, si vamos a notificar un porcentaje, podríamos usar Progress<int>.
  • En segundo lugar, en el punto del código desde donde queremos realizar la notificación, invocar al método Report() del parámetro anterior suministrándole el valor de progreso deseado.
Pero seguro que queda más claro si lo vemos con un poco de código basándonos en el ejemplo anterior:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
IProgress<int> progress)
{
for (var i = 1; i <= 100; i++)
{
await Task.Delay(1000);
progress.Report(i);
}
return 42;
}
De esta forma tan simple estaremos enviando al cliente el progreso de la operación :-) Por supuesto, podríamos enviar al cliente notificaciones de cualquier tipo, como en el siguiente ejemplo, donde usamos IProgress<string> para poder notificar con mensajes de texto:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
IProgress<string> progress)
{
progress.Report("Just starting...");
await Task.Delay(1000);
progress.Report("Working hard...");
await Task.Delay(1000);
progress.Report("Almost done...");
await Task.Delay(1000);
progress.Report("One second to finish...");
await Task.Delay(1000);
progress.Report("Finished");
return 42;
}

Consumo desde clientes del hub

El consumo de los datos de progreso sería bastante simple desde nuestro cliente Javascript, simplemente usar este método progress() para especificar la función callback que recibirá los datos de progreso:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
.progress(function (value) {
log("Progress: " + value + "%");
})
.done(function (result) {
log("The answer is: " + result);
});
Ah, y un detalle muy importante a este respecto: el uso de esta característica requiere la versión 1.7 o superior de jQuery, más que nada porque en el objeto Deferred que retornan las llamadas a las funciones del hub antes no existía el método progress() que estamos usando para recibir las notificaciones de progreso.

Desde clientes .NET es igualmente sencillo. Se han añadido sobrecargas al método Invoke() de forma que podemos indicar el código a ejecutar al recibir el progreso de la operación. El siguiente código muestra una llamada a un método que retorna un entero y cómo gestionar las notificaciones de progreso que nos llegan también en forma de número entero:
int result = await proxy.Invoke<int, int>(
"GetTheAnswerToLifeTheUniverseAndEverything",
(int progress) =>
Debug.WriteLine("Progress: " + progress + "%")
);
El primer parámetro genérico del método Invoke()es el tipo de datos que retornará la llamada al hub, como ha sido siempre hasta ahora, mientras que el segundo es el utilizado para notificar el progreso. Así, si los mensajes de progreso nos llegan en forma de cadena de caracteres, deberíamos usar:
int result = await proxy.Invoke<int, string>(
"GetTheAnswerToLifeTheUniverseAndEverything",
(string progress) => Debug.WriteLine(progress)
);
Bueno, pues con este post podemos dar por cerrada la serie sobre las novedades de SignalR 2.1. Hay algunas más, pero creo que no son tan interesantes como las que hemos ido viendo; si tenéis interés en conocerlas, podéis acudir al repositorio de Github de SignalR.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 166

July 14, 2014 11:45 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

El lado oscuro de los gestores de paquetes

July 14, 2014 05:06 AM

Hace no tanto tiempo (aunque en la escala de internet pueda parecer siglos) gestionar las dependencias de un proyecto era algo relativamente complicado o, al menos, laborioso.

En aquellos lejanos y oscuros tiempos, para incluir en tu proyecto, por ejemplo, NHibernate, debías buscar la página de descarga correspondiente, elegir la versión correcta para tu versión de .NET, buscar posibles dependencias (log4net, Castle.DynamicProxy, etc.), cruzar los dedos para que estuvieran en el zip que te habías descargado de la página de NHibernate y, si no estaban, empezar a buscarlos en sus respectivas páginas de descarga, asegurándote de bajar las versiones exactas que necesitabas para NHibernate y rezando para que no hubiera colisiones entre unas versiones y otras, especialmente si había algún assembly firmado (recuerdo con especial “cariño” log4net y sus versiones 1.2.9 y 1.2.10). Finalmente copiabas todo con mucho cuidado en una carpeta .\lib al lado de los fuentes de tu proyecto y lo añadías a tu sistema de control de código fuente (subversion si tenías suerte, Visual Source Safe si no). Vamos, todo muy rápido, divertido y agradable.

Por suerte hoy en día prácticamente todas las plataformmas de desarrollo cuentan con gestores de paquetes para realizar esta gestión de dependencias y, lo que es más importante, se han convertido en la manera “oficial” y habitual de instalarlas. En realidad todo esto de los gestores de paquetes no es algo nuevo, existen desde hace mucho tiempo, pero en algunas plataformas como .NET su popularidad es relativamente reciente (que Microsoft publicase NuGet fue clave en una comunidad que había ignorado antes intentos como OpenWrap o Horn).

Utilizando un gestor de paquetes, todo el proceso de descarga e instalación de una dependencia que describía antes se reduce a algo como:

npm install underscore

Así de simple. Con sólo un comando el gestor de paquetes se conectará a un servidor que contiene un índice con los paquetes publicados y se descargará el paquete seleccionado, ejecutará los scripts de instalación adecuados y hará lo mismo recursivamente con las dependencias del paquete indicado.

Además, estos paquetes que has instalado quedan registrados en un fichero de configuración asociado al proyecto, por lo que existe un punto centralizado en el que revisar qué dependencias tienes instaladas y con qué versiones.

Visto así, es innegable que los gestores de paquetes suponen un gran avance para el desarrollo de software facilitando la gestión de dependencias y seguramente sean uno de los motivos por los cuales hoy en día se utilizan cada vez más componentes externos (muchos de ellos open source) en todo tipo de proyectos.

Cuidado con las dependencias que asumes

Desgraciadamente esta facilidad de uso también tiene puede tener consecuencias negativas y es importante tenerlas en cuenta.

Al ser tan sencillo añadir nuevas dependencias a un proyecto, es habitual encontrar proyectos con decenas de dependencias externas, cada una de ellas con sus propias dependencias. Esto no es algo malo en si mismo, a fin de cuentas, ¿para qué reinventar la rueda?, pero no hay que olvidar que cada dependencia que asumes en un proyecto es una dependencia que tienes que mantener.

Es tan fácil añadir un nuevo paquete que a veces, en lugar de implementar una función de 10 líneas de código, se acaba asumiendo una dependencia sobre una librería de miles de líneas de código para usar sólo esa función. El utilizar una librería externa, probada y ampliamente usada proporciona una sensación de seguridad que no siempre es real y, cuando se produce un problema, bien porque hay un bug en esa librería o porque hay problemas de compatibilidad entre dos librerías que estamos utilizando, depurarlo puede ser bastante complicado.

Cuando aparece un problema, no sirve de nada decir “el problema no está en mi código sino en la versión 1.9.10 de jQuery, yo no tengo la culpa”. De cara a tus usuarios y tus clientes, lo que no funciona es tu aplicacion, esa por la que ellos han pagado. Al final tienes que responder no sólo por tu código sino por el de todas las dependencias que estás utilizando.

Esto no quiere decir que no haya que utilizar librerías externas. Igual que no tiene mucho sentido ponerte a escribir tu propio servidor web y es más razonable usar IIS, Apache o lo que más te guste, no merece la pena invertir tiempo en escribir tu propio ORM cuando hay alternativas perfectamente válidas.

Lo que sí es necesario es ser consciente de que cada dependencia que asumes tiene un precio. Cuando programamos, si vemos una clase que recibe 12 parámetros en el constructor lo consideramos como algo problemático (o al menos algo que requiere cierta justificación), porque sabemos que esa clase tiene demasiadas dependencias, lo que aumenta su acoplamiento con el resto del sistema y complica su futuro mantenimento. Pues con las dependencias de los proyectos pasa algo parecido. Si un proyecto depende de 12 paquetes distintos, estamos introduciendo 12 puntos de problemas potenciales, por lo que más nos vale ser conscientes de ellos y tener un buen motivo para hacerlo.

Recuperación automática de paquetes

Todos los gestores de paquetes que conozco cuentan con alguna opción para volver a descargarse los paquetes desde un repositorio central. Si recordáis el flujo de trabajo que describía al principio del post, antes era frecuente almacenar los paquetes en alguna carpeta de proyecto e incluirlos en el control de código fuente junto con nuestro propio código. Hoy en día, esto no es lo habitual e incluso se considera una mala práctica, puesto que podemos incluir únicamente un fichero de configuración y descargar los paquetes bajo demanda de sus servidores de origen cuando los necesitemos.

La principal ventaja de esto es que el tamaño de nuestro repositorio de código será mucho menor, especialmente si los paquetes se distribuyen de forma binaria (como es habitual en .NET o Java), que es más difícil de comprimir y de almacenar usando deltas cuando hay cambios de versión.

En un mundo en el que el almacenamiento en disco es tan barato puede parecer que esto no es importante, pero cuando pensamos en el tráfico de red al clonar un repositorio, especialmente con sistemas de control de versiones distribuidos como Git o Mercurial en los que se clona el repositorio completo (y no sólo una versión), y en accesos a través de conexiones a internet en lugar de redes locales, sí que es un factor a tener en cuenta.

Como siempre, esto no es gratis y utilizar la recuperación automática de paquetes en lugar de almacenar las dependencias en el control de código fuente también tiene sus inconvenientes. Ahora, para poder compilar nuestro proyecto, dependemos de que el repositorio de paquetes esté disponible. Por suerte no es muy habitual que estos repositorios centrales se caigan, pero tampoco es imposible.

Esto se puede resolver creando un repositorio de paquetes propio, dentro de tu red local, con una copia de los paquetes que usas en tus proyectos. Es una buena opción, especialmente en organizaciones grandes donde compensa aprovechar las ventajas de no incluir las dependencias en el control de código fuente y se necesitan unas ciertas garantías de disponibilidad, pero montar estos servidores requiere algo de tiempo y es una pieza más del sistema a mantener.

Personalmente, cuando estoy desarrollando un proyecto profesionalmente, siempre incluyo todas las dependencias en el control de código fuente. En mi escenario concreto, con todos los desarrolladores trabajando en una misma red local, no me compensa el riesgo de tener que lidiar con un servidor externo caído o un paquete eliminado para ahorrarme 10 segundos al clonar un repositorio (operación que, por otra parte, tampoco es muy frecuente en nuestro caso).

Conclusiones

Los gestores de paquetes son un gran invento para facilitarnos las cosas al desarrollar aplicaciones porque nos permiten gestionar las dependencias de una manera fácil y cómoda, pero eso no debe hacernos olvidar que cada paquete que instalamos es una dependencia de nuestra aplicación por la que deberemos responder en el futuro.

La existencia de servidores centralizados desde los que descargar las dependencias simplifica el acceso a las mismas y puedo ayudarnos a reducir el almacenamiento en disco y el tráfico de red, aunque a cambio estaremos introduciendo una dependencia entre nuestros procesos y la disponibilidad de esos servidores, por lo que será necesario valorar si realmente merece la pena.

No hay posts relacionados.

» Leer más, comentarios, etc...

Blog Bitix

Plantillas en Apache Tapestry

July 11, 2014 06:54 PM

Apache Tapestry

Una página web está formada por un conjunto de páginas enlazadas entre ellas. Cada página está formado por un html diferente pero normalmente todas las páginas de una misma web comparten el mismo aspecto variando solo una sección donde está el contenido propio de la página. La cabecera de la página, el pie de la página o los menús de navegación suelen estar presentes en todas las páginas de la web y suelen ser los mismos.

En este artículo voy a explicar como crear un componente que nos de a todas las páginas un aspecto común de una aplicación usando apache Tapestry como framework web de tal forma que esa parte común no esté duplicada en la aplicación y pueda ser reutilizada fácilmente. En el caso de Blog Stack las páginas se componen de las siguientes partes.

El esquema de la plantilla será una cabecera, una barra de navegación con enlaces a diferentes secciones de la web, un menú lateral con contenido variable según la página, el contenido que variará según la página y un pie de página. Como todo componente de Apache Tapestry está formado de una clase Java y una plantilla. El componente puede tener diferentes parámetros, y en el caso del de la plantilla muchos para poder variar el contenido por defecto de las diferentes secciones de la página, estos son aside1, aside2, aside3, aside4.

<noscript><pre><code>package info.blogstack.components; import info.blogstack.entities.Adsense; import org.apache.tapestry5.BindingConstants; import org.apache.tapestry5.Block; import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.annotations.Import; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.ioc.annotations.Inject; import org.joda.time.DateTime; @Import(stack = &quot;blogstack&quot;, module = &quot;app/analytics&quot;) public class Layout { @Parameter(defaultPrefix = BindingConstants.LITERAL) @Property(read = false) private String title; @Parameter(defaultPrefix = BindingConstants.LITERAL) @Property(read = false) private String subtitle; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside1; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside2; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside3; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside4; @Parameter @Property private Adsense adsense; @Property private String page; @Inject ComponentResources resources; void setupRender() { page = resources.getPageName(); } public int getYear() { return DateTime.now().getYear(); } public String getTitle() { if (title == null) { return String.format(&quot;%s&quot;, getSubtitle()); } else { return String.format(&quot;%s | %s&quot;, title, getSubtitle()); } } public String getSubtitle() { return (subtitle == null) ? &quot;Blog Stack&quot; : subtitle; } public String getContentClass() { return (isAside()) ? &quot;col-xs-12 col-sm-12 col-md-8 content&quot; : &quot;col-xs-12 col-sm-12 col-md-12 content&quot;; } public boolean isAside() { return (aside1 != null || aside2 != null || aside3 != null || aside4 != null); } }</code></pre></noscript>

El archivo tml asociado al componente plantilla será el que genere el contenido html que se enviará al navegador del usuario. En esta plantilla se incluye una cabecera con el logo de la aplicación y una frase que lo describe, posteriormente está una barra de navegación con varios enlaces, con <t:body/> se incluye el contenido propio de la página que usa el componente plantilla y usando el componente <t:delegate/> se incluye el contenido de los diferentes bloques aside si se han personalizado en el uso de la plantilla, con el componente <t:if test=“aside”> se comprueba si hay algún aside usándose el método isAside de la clase Layout asociada al componente plantilla y del tml. Finalmente, está el pie que será común a todas las páginas que usen este componente.

<noscript><pre><code>&lt;!DOCTYPE html&gt; &lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;head&gt; &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;/&gt; &lt;meta t:type=&quot;any&quot; t:pagina=&quot;${page}&quot; /&gt; &lt;title&gt;${title}&lt;/title&gt; &lt;!-- Resources --&gt; &lt;link href=&quot;//fonts.googleapis.com/css?family=Open+Sans:400,700&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;/&gt; &lt;link href=&quot;//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;/&gt; &lt;link href=&quot;/feed.atom.xml&quot; rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Portada&quot;/&gt; &lt;link href=&quot;${context:images/favicon.png}&quot; rel=&quot;icon&quot; type=&quot;image/png&quot;/&gt; &lt;/head&gt; &lt;body&gt; &lt;header&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-4&quot;&gt; &lt;h1&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; class=&quot;blogstack&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-th&quot;&gt;&lt;/span&gt; Blog &lt;span class=&quot;stack&quot;&gt;Stack&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt; &lt;/div&gt; &lt;div id=&quot;horizontalSkycraper&quot; class=&quot;col-xs-12 col-sm-12 col-md-8&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;h4&gt;Un poco más que un agregador/planeta de bitácoras sobre programación, desarrollo, software libre, gnu/linux, tecnología, ...&lt;/h4&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/header&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;nav role=&quot;navigation&quot;&gt; &lt;ul class=&quot;nav nav-pills menu&quot;&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot;&gt;Inicio&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;archive&quot; context=&quot;[]&quot;&gt;Archivo&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;faq&quot;&gt;Preguntas frecuentes&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/nav&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div t:type=&quot;any&quot; class=&quot;prop:contentClass&quot;&gt;&lt;t:body /&gt;&lt;/div&gt; &lt;t:if test=&quot;aside&quot;&gt; &lt;aside class=&quot;col-xs-12 col-sm-12 col-md-4&quot;&gt; &lt;t:socialnetworks/&gt; &lt;t:if test=&quot;aside1&quot;&gt; &lt;t:delegate to=&quot;aside1&quot;/&gt; &lt;/t:if&gt; &lt;div id=&quot;bigRectangle&quot;&gt;&lt;/div&gt; &lt;t:if test=&quot;aside2&quot;&gt; &lt;t:delegate to=&quot;aside2&quot;/&gt; &lt;/t:if&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-3 col-md-2&quot;&gt; &lt;div id=&quot;wideSkycraper&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;t:if test=&quot;aside3&quot;&gt; &lt;div class=&quot;col-xs-3 col-md-2&quot;&gt; &lt;t:delegate to=&quot;aside3&quot;/&gt; &lt;/div&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;t:if test=&quot;aside4&quot;&gt; &lt;t:delegate to=&quot;aside4&quot;/&gt; &lt;/t:if&gt; &lt;/aside&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;/div&gt; &lt;footer&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;div class=&quot;footer&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot;&gt;Blog Stack&lt;/a&gt; por &lt;a href=&quot;https://twitter.com/picodotdev/&quot;&gt;pico.dev&lt;/a&gt; está publicado bajo la licencia de software libre &lt;a href=&quot;http://www.gnu.org/licenses/agpl-3.0.html&quot;&gt;GNU Affero General Public&lt;/a&gt;.&lt;br/&gt; El contenido agregado conserva la licencia de su bitácora.&lt;br/&gt; «Powered by» &lt;a href=&quot;https://github.com/picodotdev/blogstack&quot;&gt;Blog Stack&lt;/a&gt;, &lt;a href=&quot;http://tapestry.apache.org/&quot;&gt;Apache Tapestry&lt;/a&gt;, &lt;a href=&quot;https://www.openshift.com/&quot;&gt;OpenShift&lt;/a&gt;, &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;, &lt;a href=&quot;http://www.oracle.com/es/technologies/java/overview/index.html&quot;&gt;Java&lt;/a&gt; y más software libre o de código abierto, inspirado en &lt;a href=&quot;http://octopress.org/&quot;&gt;Octopress&lt;/a&gt;.&lt;br/&gt; &lt;span class=&quot;copyleft&quot;&gt;&amp;copy;&lt;/span&gt; pico.dev ${year} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/footer&gt; &lt;div id=&quot;fb-root&quot;&gt;&lt;/div&gt; &lt;t:ads adsense=&quot;adsense&quot;/&gt; &lt;/body&gt; &lt;/html&gt;</code></pre></noscript>

Para terminar nos queda ver como sería usar este componente en una página donde queremos usarlo. En la etiqueta html se usa la plantilla con t:type para indicar que esa etiqueta es un componente de Tapestry y se le pasan los aside1 y aside2 que en esta página tienen contenido propio. El contenido de la etiqueta html se sustituirá por la etiqueta <t:body/> de la plantilla, el contenido inlcluido en los componentes <t:block/> aunque esté dentro de la etiqueta html solo se mostrará cuando se haga uso de un <t:delegate/>, como se hace el componente plantilla. Este es el caso de la página índice de Blog Stack. A pesar de todo el contenido que genera y solo consta de 34 líneas de código, esto muestra lo fácil que es en Tapestry dividir las diferentes partes de una página en componentes que puede ser reutilizados.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; t:aside1=&quot;aside1&quot; t:aside2=&quot;aside2&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;t:data/&gt; &lt;t:loop source=&quot;posts&quot; value=&quot;post&quot;&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;/t:loop&gt; &lt;section class=&quot;index-pagination&quot;&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-4 col-sm-4 col-md-4&quot;&gt; &lt;t:if test=&quot;!lastPage&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; context=&quot;nextContext&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-arrow-left&quot;&gt;&lt;/span&gt; Más antiguo&lt;/a&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;div class=&quot;col-xs-4 col-sm-4 col-md-4 col-xs-offset-4 col-sm-offset-4 col-md-offset-4 text-right&quot;&gt; &lt;t:if test=&quot;!firstPage&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; context=&quot;previusContext&quot;&gt;Más nuevo &lt;span class=&quot;glyphicon glyphicon-arrow-right&quot;&gt;&lt;/span&gt;&lt;/a&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/section&gt; &lt;t:block id=&quot;aside1&quot;&gt; &lt;t:feeds/&gt; &lt;/t:block&gt; &lt;t:block id=&quot;aside2&quot;&gt; &lt;t:lastposts/&gt; &lt;t:lastsourceswithposts/&gt; &lt;/t:block&gt; &lt;/html&gt;</code></pre></noscript>

Usando el mismo componente podemos darle un aspecto común pero variando el contenido de las diferentes secciones. En este caso usamos la misma plantilla donde se muestra la misma cabecera, enlaces de navegación y pie de página pero sin el contenido lateral como en el caso de la página de preguntas frecuentes de Blog Stack, en este caso no usamos los componentes aside.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; t:title=&quot;Preguntas frecuentes&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;article class=&quot;text-justify&quot;&gt; &lt;header&gt; &lt;h1&gt;Preguntas frecuentes&lt;/h1&gt; &lt;/header&gt; &lt;h2&gt;¿Qué es Blog Stack?&lt;/h2&gt; &lt;p&gt;Blog Stack (BS) es una agregador, planeta, o fuente de información de bitácoras sobre programación, desarrollo, desarrollo ágil, software, software libre, hardware, gnu/linux o en general temas relacionados con la tecnología.&lt;/p&gt; &lt;h2&gt;¿Por qué otro agregador?&lt;/h2&gt; &lt;p&gt; Hay varios motivos, la semilla es que quería hacer un proyecto personal con cierta utilidad para otras personas empleando de alguna forma el framework para el desarrollo de aplicaciones web &lt;a href=&quot;http://tapestry.apache.org/&quot;&gt;Apache Tapestry&lt;/a&gt;. &lt;/p&gt; ... &lt;/article&gt; &lt;/html&gt; </code></pre></noscript>

Por supuesto, podemos crear tantos componentes plantilla como necesitemos en una aplicación y usar uno o otro en función del tipo de página.

Referencia:
Código fuente de Blog Stack
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

Cuaderno de software

Adaptación al medio: evolución y mutación

July 11, 2014 06:39 AM

A menudo me gusta pensar en mi equipo como un organismo vivo multicelular que evoluciona y se adapta al entorno cambiante que le rodea. Tiene que ver con que soy un flipao y un pedante… :-P Sin embargo, esa adaptación creo que es el resultado de dos dinámicas que a veces pueden parecen contradictorias:

  • evolución: intentamos hacer retrospectivas basadas en datos (horas gastadas, tecnologías empleadas, bugs, etc.), de ahí intentamos entender en profundidad cuáles son las raíces reales de nuestros problemas, los priorizamos e intentamos tomar acciones para eliminar ese problema. Lo máxima aquí es: “si tomas acciones alrededor de un problema no prioritario o intentas atacar al problema sin saber cuál es su raíz, estás perdiendo tiempo y dinero”.
  • mutación: probamos cosas nuevas por instinto o por jugar. Aquí la máxima es: “conduct frequent experiments” pero asegurándote de que no penalizas económicamente al producto/cliente/empresa con el que estás trabajando. Yo suelo decirlo mucho: en un equipo no puede salir en una retrospectiva “hagamos integración continua” si ninguno de sus miembros a oído hablar jamás de ello. Rachel Davies en el  Agile Coaching afirma: “Actions are not always about fixing a problem: you need to understand a problem before you can fix it. Team may need to start with actions to explore the problem and gather data.” Sin embargo, aquí también se está teniendo un carácter totalmente reactivo: tenemos un marrón, investiguemos ahora qué hay para solucionar este marrón. Creo que eso es necesario, pero no suficiente.

Creo que todo lo que escribo es de sentido común: céntrate en solucionar los problemas reales que tienes y, también, prueba cosas nuevas mientras no cueste mucho dinero a nadie. Sin embargo, lo que suele suceder en los equipos es que se dividen en “evolucionadores” o “mutadores” cada uno con su zona de confort:

  • los “evolucionadores” se excusan para no probar nada nuevo y no aprender, en que lo que se propone no corresponde a la raíz de ningún problema. Tiene a gala que ellos son ingenieros que ponen el dedo en la picota.
  • los “mutadores” quieren jugar, y tienden a dejarse deslumbrar ante el brillo de lo nuevo sin tener en cuenta lo que puede suponer al equipo o al producto ni justificarlo económicamente. Tiene a gala que ellos son tecnólogos capaces de hincarle el diente a cualquier cosa que salga nueva que, como es nueva, es mejor (mega-sesgo).

Creo que aquí hay que trabajar en la identidad individual de las personas y que todo el mundo se vea a si mismo como evolucionador y mutador (sí, estas dos palabras no existen) porque ambas fuerzas son necesarias para adaptar y evolucionar al equipo.

 

» Leer más, comentarios, etc...

Blog Bitix

Arquitectura y hospedaje de Blog Stack

July 10, 2014 07:00 PM

Blog Stack

Normalmente en esta bitácora suelo poner ejemplos para explicar como funciona una determinada herramienta, son ejemplos que podrían aprovecharse para algo productivo pero para mantenerlos simples por si mismos no suelen serlo. En esta entrada voy a comentar cual es la arquitectura y como está alojado Blog Stack, que es un ejemplo de algo práctico y real por si la experiencia de este ejemplo le sirve de ayuda o de inspiración a alguien. En la red he encontrado comentada la experiencia de Planeta Linux y Meneame, otros proyectos web.

Blog Stack (BS) es una agregador, planeta, o fuente de información de bitácoras sobre programación, desarrollo, desarrollo ágil, software, software libre, hardware, gnu/linux o en general temas relacionados con la tecnología, en definitiva es un servicio que está disponible en una dirección de internet, www.blogstack.info.

Una condición importante que buscaba para BS es que de cualquier opción no debería ser muy cara al menos en los inicios hasta que viese cuales eran los ingresos por la publicidad que era capaz de generar, estimaba que no iban a ser muchos hasta que se agregase unas decenas de bitácoras y tuviese algunos lectores suscritos a las fuentes se sindicación. Por coste cualquier opción que fuese en la nube prácticamente quedaba desechada, la opción más barata era la de Amazon EC2, reservando una instancia t1.micro (0,615 GB) por tres años el coste era de unos 80 € anuales y de 140 € anuales para una instancia de tipo m1.small (1,7 GB), otras opciones como OpenShift con la capa gratuita de 3 gears si un small gear (512 MB) era suficiente no tenía coste pero si necesitase un gear medium (1 GB) el coste era superior, de unos 438 € anuales. Finalmente otra opción que evalué era Google Compute con un coste de 115 € anuales para una instancia f1-micro (0,60 GB) y de 305 € para una g1-small (1,7 GB). Si en cualquier opción de estas en la nube el coste debía de asumirlo yo probablemente al cabo de un tiempo, pocos años, acabaría cerrando el servicio, por ello el coste era importante. OpenShift y su capa gratuita tenía muchas posibilidades.

Por otro lado estaba bastante contento con la generación estática de mi bitácora con Octopress y el alojamiento en GitHub Pages. Me pereció que una opción con un coste mínimo podría generar de forma estática el contenido y alojarlo en GitHub Pages al igual que hago con Octopress. Finalmente, me quedaba donde hacer la generación estática, otra opción era usar la Raspberry Pi pero esto me obligaba a mantenerla encendida siempre, cualquier problema en la Raspberry Pi o conexión en mi red o si la necesitaba apagar en algún momento podría dejar BS sin actualizarse. Si era posible necesitaba una forma de nube, por suerte OpenShift ofrece un plan gratuito de 3 gears con 512 MiB de memoria y 1 GiB de espacio en disco cada uno y en las primeras pruebas que hice era suficiente. Además, OpenShift ofrecía todas las herramientas que necesitaba git para subir el contenido estático al repositorio de GitHub, iconv, cron, bases de datos relacionales y no sql, poder usar Undertow como servidor y un «cartridge» diy («Do It Yourself»). Tenía varias piezas, finalmente elegí la combinación de OpenShift para la generación estática y GitHub Pages para servir el contenido. El contenido podría servirlo desde OpenShift pero no estaba seguro de que el gear fuese capaz de aguantarlo todo si el tráfico fuese grande. Por el espacio en disco de 1 GB tenía dudas de cuanto podría necesitar pero si fuera mucho podría adquirir 1 GiB adicionar por 1 € al mes.

Generar el contenido de forma estática tiene limitaciones y obliga a hacer las cosas de diferente forma que empleando una aplicación web pero el requisto del coste era más importante que la funcionalidad y la forma de ofrecerla. El coste de BS es de unos ¡10 €… al año! y consiste básicamente en la compra del dominio. El dominió lo compre en DonDominio por que por recomendación era buena opción, en los 10 € está incluido el WHOIS privado que en otras opciones tienen un coste adicional. Teniendo el nombre elegido de Blog Stack para el proyecto tenía que saber que dominios de nivel superior (TLD) estaban disponibles, los “okupas” de los dominios tenían reservado el .com y el .net, como BS era un sitio de información no me importó mucho comprar el .info.

Con todo la arquitectura de despliegue y hospedaje de Blog Stack es la siguiente:

En cuanto a la aplicación y el código Java en si las partes importantes están en un servicio que se encarga de la indexación extrayendo el contenido de las fuentes y guardalo en una base de datos, otro servicio para la generación del contenido del sitio de forma estática (html, css, js, imgs, …) y finalmente el programa Java principal que sirve para lanzarlo desde la linea de comandos, el resto son las clases del modelo de datos y los DAO de persistencia y acceso a dataos. La aplicación sigue el esquema tradicional de 3 capas, la de presentación (páginas y componentes de tapestry), la de lógica de negocio formada por los servicios de indexación, generación y los DAOs (formada por el contenedor de servicios de tapestry y spring) y finalmente la capa de datos formada por una base de datos H2 (donde hibernate interviene). En realidad en BS no hay un servidor de aplicaciones sino que se usa Tapestry en una aplicación «standalone».

La raíz del proyecto fue que quería usar Apache Tapestry de alguna forma en algún proyecto que fuese de utilidad para otras personas, al mismo tiempo que meditaba la idea Planeta Linux dejó de funcionar durante unas semanas y tampoco estaba contento del todo con los otros planetas en los que tenía mi bitácora agregada. Con Planeta Arch Linux porque después de unos meses seguía sin ni siquiera respuesta a la solicitud de agregación y Planeta Código ya que creía que podría ofrecer mejor funcionalidad, por ejemplo, los gist no se importaban bien (utilizan javascript y por seguridad deben estar excluidos). Ya que empezaba el proyecto creía que podría proporcionar alguna idea que permitiese descubrir contenido a los usuarios a través de como hace stackoverflow con las etiquetas de modo que una persona se pueda suscribir (mediante rss/atom) a una etiqueta para recibir el contenido de solo el tema que le interese de cualquier bitácora agregada, descubrir nuevas bitácoras con contenido interesante a veces no es fácil. La opción más parecida y mejor que he encontrado es bitacoras.com, salvo las votaciones, el ranking y que tiene un ámbito más amplio al poder incluir bitácoras de más temáticas y que la agregación de bitácoras no está automatizada, BS en lo importante ofrece lo mismo de forma más simple y está más especializado tanto en la temática de las bitácoras como en la suscripción por etiquetas.

Apache Tapestry junto con el módulo Tapestry Offline y el hecho de que el contenedor de dependencias de Tapestry se puede usar en una aplicación «standalone» me permitía resolver la parte técnica en la mayor parte, el resto era usar unas cuantas librerías para tener la funcionalidad que necesitaba. Una de ellas era jsoup con la que podía filtrar el contenido agregado y evitar problemas de seguridad. Permitir en el contenido scripts e iframes puede ser un problema de seguridad pero permitiendo algunas fuentes confiables podía importar el contenido de forma segurar sin perder funcionalidades. En algunas bitácoras como la mía se suelen utilizar como trozos de código gist, vídeos de youtube o vimeo, presentaciones de Speaker Deck o usar el programa de afliados de amazon. Iba a necesitar una base de datos ya que las entradas van desapareciendo de las fuentes agregadas a medida que se publica contenido. La base de datos que elegí fue H2 principalmente porque puede estar contenida en un archivo que puedo descargar a mi ordenador, de esta manera podría evitar los volcados de PosgreSQL. Si BS resultase que creciese bastante posteriormente podría cambiar H2 por PosgreSQL (la otra opción que consideraba), H2 al principio era más que suficiente. El resto era la colección de herramientas que se suelen utilizar el proyectos Java como Hibernate, Spring, Gradle, Apache Commons, Rome (para obtener las fuentes)…

El despliegue de nuevo código está totalmente automatizado tal y como aconseja el libro The Pragmatic Programmer, con lo que me es muy simple y me consume muy poco tiempo hacer un nuevo despliegue, el tiempo ahorrado lo puedo emplear para desarrollar. El despliegue consiste en unos cuantos comandos ejecutados por una tarea de gradle que se envían a OpenShift. Los comandos de despliegue se encargan de todo mediante ssh, hacer previamente una copia de seguridad de la base de datos (scp/unzip), sincronizar los archivos cambiados del proyecto con rsync, establecer los permisos de ejecución de los archivos bash y actualizar el esquema de la base de datos con liquibase.

No estaba usando Apache Tapestry de la forma habitual que se suele emplear que es para desarrollar aplicaciones web que se despliegan en un servidor de aplicaciones Java. Lo iba a usar como motor de plantillas para generar el contenido estático, aparte de querer usar Tapestry ¿por que esta opción si hay soluciones específicas para esto como Thymeleaf, Freemarker, …? Uno de los motivos es que en en la mayoría de opciones se sigue un modelo «push» en el que se combinan los datos con la vista para producir el resultado. Este modelo «push» cada vez me convence menos a pesar de ser ampliamente usado en muchos motores de plantillas y frameworks web, la razón es que las vistas al final acaban conteniendo lógica si son algo complejas, eso no es bueno. Tapestry por el contrario usa un modelo «pull» en el que la plantilla para generar el contenido puede acceder al controlador por lo que las plantillas no tienen lógica, estando extraída a una clase java con lo que nos beneficiaremos del compilador y el IDE para esa lógica. Además, los componentes de Tapestry son una manera fácil de reutilizar código, por si fuera poco si pasado un tiempo quisiese desplegar Blog Stack como aplicación web podría aprovechar prácticamente todo el código.

Estando convencido del uso del software libre para ser coherente no tenía otra opción que publicar el código fuente bajo alguna licencia de software libre, la opción que elegí fue AGPL. Uno puede tener dudas de “dar” el código fuente pero creía que ganaba si alguien en un caso remoto hiciese un fork de BS, ya que Tapestry siendo una parte tan importante sería lo usado y por tanto habría hecho que otra persona usase Tapestry. Si basándose en la misma idea se hacía un proyecto similar competiríamos y que triunfase la mejor opción. Pero lo más probable que ocurra es que se colabore con el desarrollo de BS, con esta opción también ganaría. De tres opciones posibles en dos ganaría, en la tercera se competiría y las opciones que elegí eran de lo mejor que hay en el mundo Java, BS está bien armado.

Con el desarrollo de Blog Stack he tenido que resolver unos cuantos problemas, en posteriores entradas explicaré como he implementado las soluciones a algunas funcionalidades que son comunes a los proyectos web, como la correspondencia entre las urls y los artículos o etiquetas para no poner identificadores de entidades de la base de datos en las urls y que queden más limpias y amigables, como hacer la transliteración de los títulos de las entradas para la construcción de las urls, como obtener un extracto de la entrada (puede parecer simple pero no lo es tanto), como usar un servidor embebido que sirva el contenido estático o como procesar recursos estáticos como archivos less para generar el css con wro4j.

Referencia:
Presentación de Blog Stack

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

Sieve en C3

July 09, 2014 07:48 AM

Con Sieve en Javascript optimizado, había dado por cerrado el ciclo de rendimientos basados en Sieve en Java, BASIC, Pascal, y C. Sin embargo después del buen sabor de boca con C3 (Clipper Compatible Compiler), me quedaba por comprobar como de bueno sería su rendimiento. La clave de Clipper, era que generaba ejecutables nativos, pero [...]

» Leer más, comentarios, etc...

Variable not found

Novedades de SignalR 2.1 (II): desconexión del cliente

July 08, 2014 11:55 AM

SignalROtra de las novedades que acompañan a la versión 2.1 de SignalR, y que sin duda puede resultar interesante en muchos escenarios, es la capacidad de tener desde los hubs una pista sobre el motivo por el que se desconectó un cliente, cosa que hasta ahora no era posible utilizando las herramientas que nos proporcionaba el framework de forma directa.

En versiones previas de SignalR, cuando en un hub recibimos el evento OnDisconnected() en realidad no sabemos si este cierre ha sido “educado”, es decir, provocado explícitamente por el cliente como consecuencia de una acción, como puede ser que el usuario haya decidido salir de la aplicación, o bien se ha debido a un problema en el software o comunicaciones (imaginad, por ejemplo, que se cae la conexión a internet).

En SignalR 2.1, el método OnDisconnected() dispone ahora de una sobrecarga mediante la cual se nos indica si el lado cliente llamó al método stop() para cerrar la conexión, o bien la conexión simplemente se cortó de forma no esperada:
public class EchoHub : Hub
{
[...]

public override Task OnDisconnected(bool stopCalled)
{
if (stopCalled)
{
return Clients.All.Message(
"The connection " + Context.ConnectionId
+ " closed gracefully the connection");
}
else
{
return Clients.All.Message("Connection closed " + Context.ConnectionId);
}
}
}
El hecho de que stopCalled  nos llegue con un valor true implica que se invocó al método stop() en el lado cliente, lo que significa que ha sido un cierre controlado por la aplicación. Por ejemplo, podríamos haber ejecutado el método expresamente desde el lado cliente o, en caso de clientes web, incluso podría tratarse de un cambio de página o el cierre del navegador, pues ambas acciones provocan la llamada a stop() de forma automática.

Sin embargo, cuando nos llega con un valor false indica que ha transcurrido el tiempo máximo de timeout para la conexión y que, por tanto, podemos asumir que ésta se ha cerrado de forma no controlada. Esto ocurre cuando se corta la conexión, o cuando el proceso cliente muere de forma inesperada (por ejemplo por un error fatal), sin dar oportunidad a que se ejecute el código de cierre.

Por último, al usar esta nueva característica, especial atención a entornos escalados horizontalmente mediante backplanes, pues el cliente podría haber sufrido una caída temporal y, en el proceso de reconexión, haber ido a parar a un servidor SignalR distinto (!).

Publicado en Variable not found.

» Leer más, comentarios, etc...

Cuaderno de software

Palabras (III)

July 08, 2014 06:52 AM

Si nos preguntan: ¿quieres ser eficiente? ¿eficaz? ¿productivo? ¿rentable? Rápidamente responderíamos a todo que sí.  Veamos qué significa (copipegado todo de la Wikipedia):

  • Eficiencia: la capacidad de disponer de alguien o de algo para conseguir un objetivo determinado con el mínimo de recursos posibles viable.
  • Eficacia: es la capacidad de lograr un efecto deseado, esperado o anhelado.
  • Productividad: es la relación entre la cantidad de productos obtenida por un sistema productivo y los recursos utilizados para obtener dicha producción.
  • Rentabilidad: es la capacidad de producir o generar un beneficio (económico, social…) adicional sobre la inversión o esfuerzo realizado.

La diferencia entre eficiencia y eficaz la saco también de la wikipedia: matar una mosca de un cañonazo es eficaz (o efectivo: conseguimos el objetivo) pero poco eficiente (se gastan recursos desmesurados para la meta buscada). Pero acabar con su vida con un matamoscas, aparte de ser eficaz es eficiente.

Sin embargo, lo que más me interesa es el matiz que diferencia productividad y eficiencia. A menudo los confundimos, o los usamos de manera intercambiable. Y no lo son. No es lo mismo tener una buena relación entre lo que producimos y los recursos que gastamos para producirlo, que gastar siempre el “mínimo de recursos posible”.

Yo, a nivel personal, quiero que aquel que me contrata sienta que le vale la pena hacerlo (rentable), quiero demostrar que soy un tío capaz (eficaz) y que con los recursos que me dan aporto una cantidad de valor notable (productivo), pero no tengo especial interés en gastar el mínimo de recursos posible (eficiencia).

Optimizarme yo, implicará probablemente suboptimizar a otros. Visto de otra manera: sospecha de las personas que son cada vez más eficientes, probablemente estén encontrando la manera de hacer cada vez menos cosas. El clásico ejemplo es el del programador que cada vez entrega más funcionalidad por hora trabajada, porque cada vez testea menos, analiza menos y deja más flecos abiertos. Alguien que lo vea desde fuera, lo verá como un tío cada vez más eficiente, sin embargo, las consecuencias para el software será una fase de pruebas manuales mayor (hecho probablemente por otras personas/departamento), más bugs en producción, o soluciones que no se ajustan a lo que pedía el cliente (failure demand).

Guerra a la eficiencia personal. Guerra a los sistemas(sobre todo los de timetracking) que favorecen, valoran y premian esa eficiencia. Creo que uno debe ser eficaz, y mantenerse rentable y productivo, pero nunca buscar el mínimo de dedicación para conseguir un objetivo porque el camino principal para ello es reducir la calidad de lo que haces.

 

» Leer más, comentarios, etc...

Blog Bitix

Modelo «push» contra modelo «pull» en frameworks web

July 07, 2014 06:00 PM

Apache Tapestry

En la mayoría de frameworks de desarrollo de aplicaciones o páginas web para producir el contenido HTML que se envía al cliente se emplea un modelo en el que el controlador proporciona los datos que combinados con una plantilla producen el HTML. Este modelo también es el empleado habitualmente en muchos motores de plantillas (thymeleaf, mustache, …). Sin embargo, hay dos modelos que se pueden seguir para producir un texto como resultado dada una plantilla y datos:

  • Push: este es el modelo comentado. El controlador recupera de antemano todos los datos que necesita la vista, el controlador también determina la vista o plantilla que se usar. Combinando los datos y la plantilla se produce el resultado.

Los pasos que se siguen en este modelo son:

  1. La petición llega al servidor
  2. El dispatcher redirige la petición al controlador
  3. El controlador solicita los datos a la base de datos
  4. El controlador obtiene los datos de la base de datos
  5. El controlador redirige a la vista y le envía los datos que necesita
  6. La vista genera el contenido y se envía al cliente
  • Pull: en este modelo el controlador no conoce los datos que usará la vista y es esta la que los solicita según necesita. La vista tira del controlador, el controlador solo debe ofrecer el soporte par que la vista pueda recuperar los datos que necesite.

Los pasos que se siguen en este modelo varían ligeramente del modelo push pero de forma importante, son:

  1. La petición llega al servidor
  2. El dispatcher redirige la petición al controlador
  3. El controlador redirige a la vista
  4. La vista pide los datos que necesita al controlador y el controlador los pide a la base de datos
  5. La vista obtiene los datos que ha pedido del controlador
  6. La vista genera el contenido y se envía al cliente

El modelo push es empleado en muchos de los frameworks web más usados, algunos ejemplos son Symfony, Django, Grails o ASP.NET MVC. En la categoría de frameworks que usan un modelo pull está Apache Tapestry.

Al modelo push le encuentro algunos problemas. Un problema es que el controlador debe conocer que datos necesita la vista y si la vista tiene cierta lógica esta la tendremos duplicada tanto en en controlador como en la vista. Supongamos que en una aplicación tenemos un usuario y dirección con una relación de 1 a 1 entre ambos y que debemos mostrar en una página el usuario y su dirección solo si solo si es un usuario VIP. En el controlador tendremos que recuperar el usuario, comprobar si es VIP y si lo es recuperar su dirección. El problema está que en la vista deberemos hacer también una comprobación si el cliente es VIP o al menos si a la vista se le ha proporcionado una dirección, como resultado la comprobación la tendremos duplicada tanto en el controlador como en la vista, como sabemos la duplicación de código y lógica habitualmente no es buena idea ya que a la larga dificulta el mantenimiento de la aplicación.

En Grails (pero podría ser cualquier otro framework o motor de plantillas push) podríamos visualizar el usuario y su dirección si es VIP de la siguiente forma:

<noscript><pre><code>// Grails // Controlador (groovy) def showUsuario() { def usuario = Usuario.get(params.long('id')) def direccion = null if (usuario.isVIP()) { direccion = usuario.direccion } render(view:'show', model: [usuario:usuario, direccion:direccion]) } // Vista (gsp) Nombre: ${usuario.nombre} &lt;g:if test=&quot;${usuario.vip}&quot;&gt; Dirección: ${direccion.toString()} &lt;/g:if&gt;</code></pre></noscript>

Si usamos hibernate la recuperación de la dirección podemos hacerla navegando la relación pero he querido recuperarla en el controlador expresamente para el ejemplo, si no pudiésemos usar hibernate para recuperar el dato relacionado probablemente lo que haríamos es recuperar el dato en el controlador como en el ejemplo.

Otro problema del modelo push es que si la vista es usada en múltiples controladores, y precisamente la separación entre vistas y controladores uno de sus motivos es para esto, todos estos controladores van a compartir el código para recuperar los datos que necesite la vista, dependiendo del número de datos y de veces que empleemos una vista en múltiples controladores quizá debamos hacer una clase asociada a la vista que recupere los datos para evitar tener código duplicado (y exactamente esto es lo que se hace en Tapestry).

En el modelo pull el controlador no debe conocer que datos necesita la vista y si hay lógica para mostrar ciertos datos está lógica solo la tendremos en la vista. Aunque el controlador no deba conocer que datos en concreto necesite la vista si debe ofrecer el soporte para que la vista los recupere cuando necesite. Como se puede ver el código en el siguiente ejemplo la comprobación de si el usuario es VIP solo está en la vista. En Tapestry cada vista tiene asociado una clase Java que es la encargada de ofrecer el soporte para que la vista pueda recuperar los datos, el conjunto de controlador más vista es lo que en Tapestry se conoce como componente, si el componente se usa varias veces en el mismo proyecto no necesitamos duplicar código.

<noscript><pre><code>// Tapestry // Controlador (java) public Usuario getUsuario() { return usuarioDAO.get(id); } public Direccion getDirecion() { return getUsuario().getDireccion(); } // Vista (tml) Nombre: ${usuario.nombre} &lt;t:if test=&quot;usuario.vip&quot;&gt; Direccion: ${direccion.toString()} &lt;t:if&gt;</code></pre></noscript>

¿Podemos emplear un modelo pull en un framework que normalmente se suele usar un modelo push? Sí, basta que en el modelo de la vista pasemos un objeto que le permita recuperar los datos que necesite. En Grails empleando un modelo pull el código podría quedarnos de la siguiente forma:

<noscript><pre><code>// Grails // Controlador (groovy) def showUsuario() { render(view:'show', model: [controller:this]) } def getUsuario() { return Usuario.get(params.long('id')) } def getDireccion() { return usuario.direccion } // Vista (gsp) Nombre: ${usuario.nombre} &lt;g:if test=&quot;${usuario.vip}&quot;&gt; Dirección: ${direccion.toString()} &lt;/g:if&gt;</code></pre></noscript>

Como se ve el if de comprobación en el controlador desaparece, a pesar de todo si la vista fuese usada por varios controladores deberíamos crear algo para evitar tener duplicado el código que permite recuperar los datos a la vista. Aunque esto es perfectamente posible no es la forma habitual de usar los modelos push.

Este ejemplo es muy sencillo y empleando cualquiera de los dos modelos es viable, pero cuando el número de datos a recuperar en las vistas y el número de veces que se reutiliza una vista aumenta (y en teoría la separación entro controlador y vista uno de sus motivos es posiblemente para reutilizarlas) el modelo push presenta los problemas que he comentado que el modelo pull no tiene.

Referencia:
Pull vs. Push MVC Architecture
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 165

July 07, 2014 11:45 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

Por favor, comenta el código

July 07, 2014 05:06 AM

Recuerdo que cuando empecé a programar en la carrera (hace ya unos cuantos años), una de las cosas en la que más nos insistían los profesores era en que usásemos comentarios en nuestro código. Muchos comentarios. Prácticamente uno por cada línea. Visto con perspectiva, es posible que sólo trataran de averiguar la intención que había detrás de ese código tan enrevesado que escribíamos para cualquier cosa, o de obligarnos a pensar lo que queríamos hacer antes de empezar a escribir cada línea de código.

El hecho es que esa filosofía de sobreabundancia de comentarios llevaba escribir código de este estilo:

// suma los elementos de un vector
// param: vector con los datos a sumar
// return: suma de los elementos
int sumaVector(int[] vector) {
  // Variable para almacenar la suma de los elementos
  int suma = 0; 
  // Recorremos el vector 
  for (int i = 0; i < vector.length; i++) {
    // Obtenemos el elemento i-ésimo del vector
    int elem = vector[i];
    // Actualizamos la suma
    suma = suma + elem;
  }
  return suma;
}

Seguro que más de uno ha escrito código así más de una vez cuando estaba empezando. Lo malo de este tipo de comentarios es que no añaden gran cosa al código e introducen mucho ruido, consiguiendo el efecto contrario al deseado: no ayudan a entender mejor el código, sino que lo hacen más difícil. Además, existe el riesgo de que los comentarios se “desincronicen” y no digan lo mismo que el código, contribuyendo a la confusión.

Afortunadamente, en cuanto se adquiere algo de experiencia, se deja de usar este estilo de comentarios, pero entonces llega la fase de la “documentación automática”. Muchos lenguajes tienen herramientas para generar documentación a partir de determinados comentarios, y esa documentación luego queda muy bonita en el autocompletado del IDE o en un HTML autogenerado, así que acabamos escribiendo cosas como ésta:

/// <summary>
/// Clase para representar una persona
/// </summary>
public class Person
{
  /// <summary>
  /// Obtiene o establece el nombre de la persona
  /// </summary>
  public string Name { get; set; }
}

Con esto hemos pasado del nivel aprendiz al nivel consultor. Ahora tenemos un montón de ruido en el código con comentarios que sólo dicen obviedades, pero éste está escrito siguiendo el manual de buenas prácticas de la empresa de turno.

Esto se puede “mejorar” introduciendo en cada fichero un aviso de copyright, o llevar a un nivel extremo haciendo que en la cabecera de cada clase o método se introduzcan el autor y el historial de modificaciones (supongo que para no tener que fiarse de control de versiones). Suena exagerado, pero existe código así.

Todo este (ab)uso de los comentarios acaba provocando la reacción lógica: matemos los comentarios.

Además lleva a una situación curiosa y es que los comentarios, que deberían servir para destacar cosas importantes del código, la mayoría de las veces quedan ocultos en regiones de los editores de texto o coloreados de forma que resalten menos sobre el fondo.

Escribe código autoexplicativo

Cuando se empieza la crítica feroz hacia los comentarios se suele incidir en que un comentario es una justificación para un código mal escrito, y que es mejor reescribir el código de manera que no haga falta ese comentario.

Este es un argumento perfectamente válido y es algo que debemos cuidar especialmente. En lugar de escribir este código:

// Procesa un pedido aplicando descuentos a clientes preferentes
public void Process(Order order)
{
  // Si es un cliente preferente...
  if (order.Customer.OrderCount > 5 && order.Customer.PurchasedAmount > 1000) 
  {
    // ...aplicamos un descuento del 5%
    order.Total = order.Total * 0.95m;
  }
}

Podemos escribir este otro código y ahorrarnos los comentarios:

public void ApplyDiscounts(Order order)
{
  if (order.Customer.IsPreferred) 
    order.ApplyDiscountPercentage(5m);
}

En estos casos, las refactorizaciones “extraer método”, “introducir variable”, “renombrar método”, “renombrar variable” y compañía, son el camino a seguir para obtener para obtener un código más fácil de entender y mantener.

Los comentarios necesarios

Donde se nos empieza a ir la cosa de las manos es cuando empezamos a pervertir la idea de evitar comentarios redundantes y pasamos a renunciar a escribir cualquier tipo de comentario. Por mucho que queramos, hay cosas que no podemos capturar con el código y en las que un comentario nos puede ayudar a evitar problemas en el futuro.

Por ejemplo, hay veces en las que decidimos usar un algoritmo con una complejidad mayor en caso promedio, pero que para el tipo de datos que esperamos se comporta mejor:

// Usamos ordenación por selección en lugar de mergesort o quicksort
// porque los datos de entrada estarán casi ordenados y el coste será O(N)
var sorted = InsertSort(input);

Si no estuviese ese comentario, es fácil que quien mantenga nuestro código en el futuro (probablemente nosotros mismos) cuando vea que estamos usando un algoritmo “ineficiente” decida “optimizarlo” provocando al final una pérdida de rendimiento. Cuando tomamos una decisión que, a simple vista, resulta poco ortodoxa (pero razonada), dejarla documentada con un comentario es importante.

Esto mismo es aplicable a los casos en que escribimos un algoritmo complicado y queremos dejar constancia de la idea general o de las estructuras de datos que estamos empleando y por qué.

Es cierto que seguimos teniendo el problema de que los comentarios pueden mentir y no ajustarse a la realidad del código. Ese es un riesgo real y requiere disciplina para tratar de mitigarlo, pero no comentar el código suele ser peor.

Lo importante, en todo caso, es comentar el porqué, más que comentar el qué o el cómo. El cómo ya lo estoy viendo, es el código. El qué no debería ser necesario si escribes código autoexplicativo, pero por qué has decidido hacerlo así a sabiendas de que resulta extraño, eso sí que deberías explicarlo.

Existen otro tipo de comentarios que me duele mucho escribir pero que llega un momento en que son necesarios: los comentarios de documentación en lenguajes dinámicos.

Cuando utilizas un lenguaje estático puedes aprovechar el sistema de tipos y los nombres de las variables (o parámetros) para transmitir mucha información sobre el código, lo que ayuda a hacerlo más comprensible y hace que, como veíamos al principio del post, muchos comentarios de documentación sean poco útiles.

En un lenguaje dinámico se pierde parte de esa información y nos quedamos sólo con los nombres de las variables y funciones para indicar el tipo esperado. Esto se puede paliar utilizando tests como documentación, pero hay veces que tampoco eso es excesivamente práctico, ya que para averiguar cómo funciona una función nos obliga a ir a revisar sus tests (que pueden ser muchos) y a navegar por distintas áreas del código, saltando de la definición de la función a sus tests.

El uso de comentarios en estos casos para documentar el interfaz de la función es una gran ayuda (aunque seguimos corriendo el riesgo de que acaben desincronizados con el código, cosa que con los tests no pasa) y en lenguajes como Python o Clojure incluso forman parte de la sintaxis de definición de funciones.

Conclusiones

Los comentarios en el código se han ganado durante mucho tiempo mala fama debido al mal uso que se ha hecho de ellos, y eso ha llevado a una reacción exagerada anti-comentarios que acaba siendo contraproducente.

Nunca debemos olvidar que casi todo el código que escribimos hay que mantenerlo después, y eso implica poder comprenderlo dentro de un tiempo. Para ello lo fundamental es escribir un código que sea lo más autoexplicativo posible, pero hay consideraciones externas al código que pueden ser tan importantes como el propio código, y los comentarios son una buena forma de documentar esas decisiones.

Posts relacionados:

  1. Es peligroso duplicar conceptos, no código

» Leer más, comentarios, etc...

Picando Código

Algunos enlaces cortos: Desarrollo Web y alguna cosa más

July 02, 2014 04:00 PM

Algunas cosas interesantes que he visto y leído en estos días por ahí: WebIDE – Mozilla lanzó un entorno de desarrollo para aplicaciones web completo dentro de Firefox Nightly. Lo estuve probando y está genial. Te permite instalar y testear aplicaciones en dispositivos Firefox OS y simuladores integrado completamente con las Firefox Developer Tools. Se pueden crear proyectos nuevos a partir de ejemplos o editar aplicaciones web ya existentes. La herramienta permite instalar simuladores de distintas versiones de Firefox OS desde un gestor interno. También podemos usar nuestro editor de texto preferido usando el IDE solo para validar nuestras aplicaciones y ejecutarlas en distintos runtimes. Pueden leer más en Mozilla Hacks. WebIDE En Ruby, hace poco vió la luz un nuevo framework web: Lotus. Es un framework MVC con énfasis en diseño orientado a objetos y testeabilidad, con menos DSLs, más objetos y cero monkey-patching. Cada componente del framework es una biblioteca aparte. Router y Controller pueden ser usados con Rack independientemente, el resto de las bibliotecas son View, Model y Utils para las demás necesidades de una aplicación web completa. El código fuente de cada parte de Lotus está disponible en GitHub. Habrá que probarlo para ver qué tal resulta. JavaScript – Estoy leyendo bastante sobre JavaScript, un artículo interesante que me encontré recientemente es: Init.js: A Guide to the Why and How of Full-Stack JavaScript Hay mucha herramienta por aprender en el mundo JS, y este tipo de artículo está bastante bien para ver qué herramientas eligen algunas personas y por qué. Probablemente pruebe Mocha.js y Chai.js eventualmente para TDD (práctica que no he hecho con JS hasta ahora). PHP tiene funciones anónimas a partir de la versión 5.3. Un pedazo de conocimiento que adquirí de casualidad intentando programar Ruby con PHP. Como muestra el ejemplo en la documentación, podemos hacer cosas como:

$greet = function($name) {
  printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');

De todas formas no lo usé porque todavía queda mucho servidor con PHP menor a 5.3 en la vuelta… Tenemos el potencial de resolver los mayores problemas de hoy – Artículo en inglés, comenta cómo teniendo el poder de resolver problemas mayores -energía, alimento, agua, salud, educación-, estamos haciendo aplicaciones como Yo, un app que no hace más que enviar “Yo” a tus amigos y recientemente recibió 1.2 millones de dólares de inversión. ¿Alguien dijo “burbuja”?: We have the potential to solve the biggest problems of today Cristopher McCann.

» Leer más, comentarios, etc...

Picando Código

Volumerb – Ajusta el volúmen de tu sistema desde Ruby

July 01, 2014 04:00 PM

En marzo programé un “control remoto web” para el volumen del sistema como parte de unos proyectos de fin de semana. No sé si podría contar dónde, pero la aplicación se estaba usando como parte de otra aplicación con acceso web :D

Dada la situación, tuvo sentido extraer la lógica de manipulación del volúmen en una gema para que pueda ser integrado de manera más simple con otro sistema Ruby. Así que ahora la funcionalidad está disponible como una gema: volumerb.

volumerb

Funciona en sistemas GNU/Linux y gracias a Daniel Cadenas que me ayudó a testear en su computadora (aparte de ayudarme con un poco de pair programming), en Mac OS X.

Pueden instalar la gema con gem install volumerb o agregarla a su proyecto con su gestor de dependencias preferido. El funcionamiento es bastante básico. Tiene métodos para subir y bajar el volumen (el parámetro de cuánto queremos manipularlo es opcional), un método para definir un valor específico, mutear, y ver el estado actual del volúmen.

Un poco de código de ejemplo muestra cómo funciona:

2.1.2 :001 &gt; require 'volumerb'
 =&gt; true
2.1.2 :002 &gt; Volumerb.vol
 =&gt; {:value=&gt;53, :state=&gt;"on"}
2.1.2 :003 &gt; 10.times { Volumerb.down }
 =&gt; 10
2.1.2 :004 &gt; Volumerb.value
 =&gt; 23
2.1.2 :005 &gt; Volumerb.up 37
 =&gt; {:value=&gt;60, :state=&gt;"on"}
2.1.2 :006 &gt; Volumerb.mute
 =&gt; {:value=&gt;60, :state=&gt;"off"}
2.1.2 :007 &gt; Volumerb.state
 =&gt; "off"
2.1.2 :008 &gt; Volumerb.value = 100
 =&gt; 100
2.1.2 :009 &gt; Volumerb.vol
 =&gt; {:value=&gt;100, :state=&gt;"off"}

El código fuente está disponible en GitHub, todo feedback y aporte es bienvenido :)

» Leer más, comentarios, etc...

Arragonán

Semana 316

July 01, 2014 12:45 AM

Otra vez tarde, pero ahí va el pequeño resumen de mi semana.

Después de un lunes durillo, pude juntarme un rato a tomar unas birras con Isa, que ya apetecía tras un buen porrón de meses, y con TorresBurriel. Por cierto, que mi tocayo está preparando un sarao de UX bastante chulo para el día 11 de Julio en Zaragoza, y le estuvimos echado un cable esta semana desde CachiruloValley que, entre otras cosas, para eso está.

Al terminar el mes también tocó recoger las últimas cosas del piso y perder tiempo en dejarlo bien limpio, igual parece una chorrada, pero al menos a mi este tipo de preocupaciones me han restado bastante productividad un par de días.

Por otro lado el jueves hubo Zaragoza Ruby, este mes no teníamos charla y decidimos dedicarnos a hacer retrospectiva y ver qué hacíamos en el futuro. Este es un grupo pequeño y es normal que se colabore un poco entre todos, pero en estos grupos rara vez no es necesario que una o varias personas tiren un poco de los demás. La cuestión es que Pablo ahora anda liado, y yo he venido ejerciendo de parche en momentos puntuales pero no me puedo hacer cargo de ello mes a mes, por suerte Pedro se ofreció y será la nueva cabeza visible para mover estas reuniones del último jueves de cada mes.

Y para rematar, el fin de semana fue bastante musical, entre conciertos por las fiestas del Gancho, la fiesta de cierre del Explosivo Club (da penilla ver cerrar salas de conciertos) y pasar a ver un par de conciertos del Zaragoza Ciudad (que desde luego el cartel no era lo de antaño, pero oye, que lo pasamos bien).

Sobre proyectos en marcha:

  • En mhop terminé con los cambios para como se gestionan las campañas y productos con precio fijo, no sin pequeños errores que tocó ir subsanando antes de poder ponerlo en producción.
  • Mientras que para minchador estuve reordenando algunas cosas del backlog de producto. Por ello esta semana me puse con cosas relacionadas con la i18n, mejorar algún copy de la aplicación… además de actualizar algunas dependencias que estaban algo obsoletas.
  • Tuvimos una reunión de kickoff con el nuevo proyecto, conocer al equipo, conocer las herramientas, empezar a acordar algunas cosas de organización, ver las historias de usuario iniciales, los wireframes… Y tras ello empaparse algo más sobre este nuevo proyecto (tengo pendiente ponerle nombre en clave).
  • De nuevo tocó perder más tiempo en gestiones para que nos dieran acceso remoto al entorno de producción del proyectoSinNombre. En ocasiones trabajar con organizaciones grandes resulta muy lento, y a eso hay que sumarle que uno también tienes otras cuestiones que atender.

Buena semana

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin