miércoles, 24 de mayo de 2017

Temet Nosce - Conócete a ti mismo

Soy fanático de las películas de la trilogía Matrix y recomiendo esas películas a cualquiera que no las haya visto. Los que son fanáticos como yo, ya deben haber conectado el título de esta nota con el cartel que cuelga sobre la puerta de la cocina del oráculo: “Temet Nosce”, que en latín significa “conócete a ti mismo”. La historia de esta frase se remonta hasta el templo de Apolo, en la antigua Grecia, pero su historia no viene al caso ni hace al centro de este artículo.

Miraba la película cuando conecté este concepto con el artículo “No hagas lo que no te gusta que te hagan” que escribí hace un tiempo. Conocerse a sí mismo, creo que nadie puede negarlo, es un buen consejo en todo contexto. Si hablamos de un líder de equipo, este concepto trasciende el nivel personal. Un líder debe conocer a su equipo tanto como se conoce a sí mismo, ya que debe ser capaz, en la medida de lo posible, de anticipar los efectos que las decisiones que toma causarán en el grupo.

Es sabido que las personas trabajan mejor cuando las tareas que desempeñan son de interés para ellos. Por esto, al momento de distribuir las tareas a realizar por los miembros del equipo es fundamental conocer los intereses de cada miembro. En otras palabras, es necesario conocer al equipo.

Pero, ¿Cómo hacemos para conocer mejor el equipo que lideramos? Creo que hay tres pasos básicos que cualquier líder puede seguir sin hacer ningún curso en particular:
  1. Proponerse seriamente a sí mismo conocer al equipo: conocer al equipo no es algo que simplemente “se da”. El líder de equipo tendrá que reservarse el tiempo que necesite para hacerlo y por esto debe ser tomado con la seriedad del caso. Forma parte del trabajo formal del rol y, de ser necesario, debería tener asignada una tarea en la planificación del proyecto en el que esté trabajando.
  2. Establecer y garantizar un espacio propicio: cuando un líder quiere conocer mejor a un miembro de su equipo no debe hacerlo a las apuradas, de cualquier manera y en cualquier lugar. Una buena forma, por ejemplo, podría ser organizar almuerzos o desayunos con distintos miembros del equipo. La charla podría rondar el trabajo diario, el proyecto, intereses personales y todo tópico que pueda sirva al objetivo.
  3. Escuchar: ¿Acaso hay que explicar en profundidad este paso?

Por último, y para aquellos que recientemente han abrazado un rol de liderazgo, les dejo dos cosas para pensar:
  • No solamente es su trabajo conocer a su equipo, también es su trabajo lograr que cada miembro      del equipo conozca a los demás.
  • Usted también forma parte del equipo, por lo que también debe lograr que sus colaboradores lo conozcan.


Autor:
Ing. Leo Viegas
Desarrollador 

miércoles, 12 de abril de 2017

No hagas lo que no te gusta que te hagan

Siempre me gustó el desarrollo de software. Empecé a programar cuando tenía nueve años y mi tuve mi primer trabajo como desarrollador a los diecinueve. Cuando en 2005 (por ese entonces tenía veintiocho años) me ofrecieron por primera vez un puesto de Jefe de Desarrollo no lo dudé y lo acepté. Recuerdo haber pensado: “Dada mi experiencia como desarrollador, ser Jefe de Desarrollo será muy fácil para mí”. Hoy en día guardo esa frase en la colección de grandes errores cometidos.

Ser jefe significa mucho más, ahora lo sé, que conocer al dedillo las tareas que deben realizarse en un área. Ser jefe implica relacionarse con gente y la gente (¡Oh, sorpresa para mí!) es muy diferente a las computadoras. Casi de inmediato comencé a hacer agua en mi puesto. Yo era el jefe del equipo, pero no era el líder.

Hubiera estado bueno que alguien me explicara en ese momento las diferencias entre en un jefe y un líder y me hubiera aconsejado un poco respecto de las tareas. Ahora, doce años después, quisiera darle una mano a cualquier colega que se esté afrontando una situación parecida a la que yo afronté.

Hay cursos y capacitaciones para mejorar “las habilidades blandas”, como suelen llamarse. Pensá en hacerlos. Vale la pena. Pero si no contás con tiempo como hacer esas capacitaciones ahora, voy a dejar a continuación una enseñanza que me dio mi abuela cuando yo tenía tres años: “No hagas lo que no te gusta que te hagan”. Mi abuela no había terminado la primaria, pero es una de las personas más sabias que jamás conocí y apliqué muchas de sus enseñanzas en mi carrera. Vamos a lo concreto:

  • ¿Te gusta que cuando estás hablando con tu jefe él se ponga a mirar el celular? Entonces, vos no lo hagas con tus colaboradores.
  • ¿Te gusta que cuando le pedís a tu jefe una reunión por algún tema te diga que está muy ocupado y te dé cinco minutos de su valioso tiempo en un ascensor? Entonces, vos no lo hagas con tus colaboradores.
  • ¿Te gusta que cuando estás hablando en una reunión de trabajo tu jefe te interrumpa y no te deje terminar la idea? Entonces, vos no lo hagas con tus colaboradores.
  • ¿Te gusta que tu jefe te diga “hacé lo que te digo porque acá mando yo”? Entonces, vos no lo hagas con tus colaboradores.
  • ¿Te gusta que tu jefe te mienta? Entonces, vos no lo hagas con tus colaboradores.

En las antípodas de esta “regla” hay otra: “Tratá a la gente como te gusta que te traten a vos”.
  • ¿Te gusta que tu jefe te preste toda su atención cuando estás hablando con él? Entonces, vos hacelo también con tus colaboradores.
  • ¿Te gusta que cuando le pedís a tu jefe una reunión él te dé el tiempo y el el ambiente propicio para que puedan hablar? Entonces, hacelo también con tus colaboradores.
  • ¿Te gusta que cuando estás hablando en una reunión de trabajo tu jefe te escuche y te permita terminar, aunque luego pueda estar en desacuerdo con tu idea? Entonces, vos hacelo también con tus colaboradores.
  • ¿Te gusta que tu jefe tome en cuenta tus sugerencias? Entonces, vos hacelo también con tus colaboradores.
  • ¿Te gusta que tu jefe te diga la verdad? Entonces, vos hacelo también con tus colaboradores.

Ante cualquier otro escenario en el que surjan dudas, volver al consejo original de mi abuela: “No hagas lo que no te gusta que te hagan”.

Autor:
Ing. Leo Viegas
Desarrollador 


jueves, 30 de marzo de 2017

La importancia del testing en los proyectos de desarrollo de software

El testing de software es una de las etapas fundamentales de cualquier buen ciclo de desarrollo. La verificación y validación del software se presentan como operaciones indispensables para asegurar la calidad del sistema antes de su puesta en producción y minimizar costos en el mantenimiento.

Un proyecto promedio tiene días destinados a las pruebas, principalmente antes del despliegue. En la mayoría de los casos, cuando el proyecto sufre retrasos en las primeras etapas del ciclo de vida, el tiempo estimado para pruebas se ve afectado y es así como mucho software comercial consigue solo un par de días de testing antes de enviarse a producción.

En la actualidad, las pruebas de software suelen considerarse innecesarias y costosas, pero en realidad son una inversión garantizada y tienen mayor efecto si se aplican desde etapas tempranas del ciclo de vida del software. En este sentido, cabe destacar que testear no implica aumentar costos, sino todo lo contrario. Testear significa reducir costos originados por diversas correcciones que se realizan en el sistema después de haber salido a producción.

Los beneficios de aplicar las técnicas de verificación y validación no solo se ven reflejados en el área de desarrollo del proyecto, sino también en las áreas de negocio del cliente final. Además de cumplir con el compromiso en tiempo, costo y calidad, el testing se ve reflejado en la fase de instalación en producción ya que, al llegar el sistema a esta fase final, este es prácticamente estable y ofrece una mejor respuesta a las necesidades del cliente y/o usuario final, asegurando su satisfacción respecto al producto terminado.

En base a lo anterior, se puede decir que las pruebas deben ser partícipes en todo momento del ciclo de vida de desarrollo del proyecto y contempladas en la planificación del mismo. El testing evita que el proyecto sea puesto en producción con errores que pudieron ser resueltos a tiempo y retrasos a en las actividades de los usuarios finales, como así también una inversión mayor de tiempo por parte del equipo de desarrollo, ya que cuando el proyecto se encuentra finalizado es más complejo determinar donde se encuentra el error.

Una planificación temprana de las pruebas, el control de las mismas en todas las fases de desarrollo del proyecto, y contar con especialistas de testing, aseguran beneficios para el cliente tanto en ahorro de costos como en plazos de entrega del equipo de trabajo, sin dejar de mencionar que se entrega un proyecto de calidad y usabilidad.

Autora:
Cecilia Zamudio
Tester QA

miércoles, 8 de febrero de 2017

La ley del menor esfuerzo, la navaja de Ockham y el principio KISS

Mi profesora de historia de cuarto año, cuyo nombre hoy en día es esquivo a mi memoria, me decía: “Viegas: usted siempre elige el camino más fácil”. Me lo decía como si eso fuera algo malo y yo pensaba “¿Quién en su sano juicio elegiría el camino más difícil?”. Esto a lo que en el vulgo hemos llamado “la ley del menor esfuerzo” está muy denostado y creo que es el momento de revalorarlo. Al menos, de ubicarlo en el lugar que se merece, ni más arriba ni más abajo.

Si bien es complejo negar que en muchas áreas del desarrollo humano para alcanzar una meta se requiere realizar un esfuerzo importante (pregunten a los comerciales lo que cuesta concretar una venta), siempre postulé que no deberíamos perder el foco respecto de si no sería posible alcanzar la misma meta esforzándonos un poco menos. Grande fue mi sorpresa cuando me enteré de que ya alguien en la historia había postulado algo parecido.

William de Okham postuló: “en igualdad de condiciones, la explicación más sencilla suele ser la correcta”. Este principio filosófico llamado “la navaja de Ockham” tiene aplicación en distintas áreas como economía, biología, informática y otras.

Siguiendo esta línea, en informática tenemos el principio KISS, de las siglas en inglés “Keep It Short and Simple”, aunque hay versiones de gente non sancta que dice que en realidad significa “Keep It Simple, Stupid!”. Cada quien tomará la versión que más le agrade. Si bien a la gran mayoría de aquellos que hacemos de la informática nuestra labor, nos agrada pensar que el principio KISS es privativo de nuestra profesión, en realidad puede aplicarse a otras áreas de trabajo y la vida personal. Para los que jugamos paintball no es novedad que las estrategias sencillas suelen ser más exitosas que las complejas.

Pero volviendo sobre la informática, no debemos perder de vista que históricamente los costos de mantenimiento siempre han sido infinitamente superiores a los costos de desarrollo de un sistema. Diseñar software teniendo presente el principio KISS resultaría en menores costos durante la etapa de mantenimiento y, por qué no, un poco más de alegría en el corazón de aquel que debe mantener el sistema en cuestión. ¿Alegría? ¿Corazón? ¿Hay lugar para eso en los planes de trabajo de un área de desarrollo? Sostengo, afirmo y defiendo que sí lo hay. Como desarrollador, cuando tengo que mantener código complejo, devenido de diseños complejos y normalmente sin documentación apropiada, sufro. En cambio, cuando el código es simple, devenido de diseños simples, soy feliz.

Todo lo anterior también aplica a otras disciplinas: comercial, gestión, deportes, las compras del fin de semana en el supermercado, etc. 

Teniendo en mente a William de Okham y sin realizar un mayor esfuerzo podemos mantener las cosas simples y vivir y trabajar un poco más contentos.

Autor:
Ing. Leo Viegas
Desarrollador 

miércoles, 25 de enero de 2017

MetisMenu con AngularJS

Hoy analizaremos las oportunidades que nos brinda MetisMenu cuando lo combinamos con AngularJS.

Pero antes…

¿Qué es MetisMenu?
Es un plugin escrito en jQuery diseñado para armar menús animados con gran facilidad.

¿Qué es AngularJS?
Es un framework de código abierto del tipo MVC (Modelo Vista Controlador) de Javascript para el desarrollo Web Front End que permite crear aplicaciones del tipo SPA (Single-Page Applications). Su desarrollo es realizado por Google.

Entonces, ¿cuáles son los beneficios de combinar ambas tecnologías? 

Podríamos pensar en armar un menú cuyos elementos surjan de la iteración brindada por AngularJS de elementos de un array.

Veamos un ejemplo.

 

Ejemplo de metisMenu

Este es el esquema inicial de nuestra página web:

<html>
                <head>
                               <meta charset="UTF-8">
                               <Title>metisMenu</Title>
                </head>  
                <body>
                </body>
</html>

Lo primero será entonces agregar las referencias a las librerías de nuestro plugin. En este ejemplo vamos a utilizar los CDN (Content Delivery Network), que básicamente son sitios permanentemente online que disponibilizan dichas librerías gratuitamente. Otra opción es bajar los archivos desde la página GitHub oficial del plugin y referenciarlos localmente.

<head>
                <meta charset="UTF-8">
                <Title>metisMenu</Title>
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css">
                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js"></script>
</head>

Acto seguido vamos a crear nuestro menú en el body:

<body>
                <ul class="metismenu" id="menu">
                               <li class="active">
                                               <a href="#" aria-expanded="true">Menu 1</a>
                                               <ul aria-expanded="true">
                                                               <li><a href="#">item 1.1</a></li>
                                                               <li><a href="#">item 1.2</a></li>
                                                               <li><a href="#">item 1.3</a></li>
                                                               <li><a href="#">item 1.4</a></li>
                                               </ul>
                               </li>
                               <li>
                                               <a href="#" aria-expanded="false">Menu 2</a>
                                               <ul aria-expanded="false">
                                                               <li><a href="#">item 2.1</a>
                                                                              <ul aria-expanded="false">
                                                                                              <li><a href="#">item 2.1.1</a></li>
                                                                                              <li><a href="#">item 2.1.2</a></li>
                                                                                              <li><a href="#">item 2.1.3</a></li>
                                                                                              <li><a href="#">item 2.1.4</a></li>
                                                                              </ul>
                                                               </li>                                                                                                     
                                                               <li><a href="#">item 2.2</a></li>
                                                               <li><a href="#">item 2.3</a></li>
                                                               <li><a href="#">item 2.4</a></li>
                                               </ul>
                               </li>         
                </ul>
</body>

Y por último, justo antes del cierre de la etiqueta body, agregamos una llamada a la función metisMenu() que habilita finalmente la animación de nuestro menu:

<body>
                <script type="text/javascript">
                               $(document).ready(function(){                                              
                                               $("#menu").metisMenu();
                               });
                </script>
</body>

Haga clic aquí para ver este ejemplo en vivo.



Con unos retoques podemos agregarle más color y distintas animaciones a nuestro menú, pero ese no es el objetivo de este artículo. Para más ejemplos se recomienda revisar los enlaces al final.

 

Ejemplo de metisMenu sumando AngularJS

A continuación, agregaremos AngularJS a nuestro código. La idea es la siguiente: vamos a simular un repositorio de datos el cual será leído mediante una llamada Ajax (asincrónica) y con esa información se estará creando nuestro menú.Por esa razón vamos a estar reemplazando el código que contiene nuestro menú estático (Menú 1; ítem 1.1, etc.) por código de AngularJS. Recordemos que en este caso será AngularJS quien se encargue de armar los elementos HTML con la información que va a leer de nuestras funciones Ajax.

Agregamos el link al código de AngularJS en nuestro head:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
Nuestro head debería quedar así:
<head>
                <meta charset="UTF-8">
                <Title>metisMenu</Title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css">
                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js"></script>
                <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>

Lo siguiente será modificar nuestro body para incorporar el código de AngularJS.,Nuestra aplicación se llamará 'miAplicacion' y definiremos un sólo controlador: 'miControlador':

<body ng-app="miAplicacion" ng-controller="miControlador">

Definimos una lista desordenada, tal cual como en el ejemplo anterior de metisMenu:

                <ul class="metismenu" id="menu">

Sin embargo, ahora cada elemento de la lista se va a estar generando mediante la instrucción 'ng-repeat', que va a iterar cada país de una lista y vamos a estar monitoreando el final de dicha iteración. Más adelante vamos a entender el porqué de esta acción.

<li ng-repeat="pais in paises" ng-repeat-end-watch="miFuncion">
<a href="#" >{{ pais.Country }}</a>
                <ul >

Aquí vamos a estar definiendo los elementos internos o submenús de nuestra lista y como queremos que siga siendo dinámica.Para ello, volvemos a utilizar la instrucción ng-repeat de AngularJS:

        <li ng-repeat="nombre in nombres" ng-repeat-end-watch="miFuncion">
                                                               <a href="#" >{{ nombre.Name }}</a>
                                               </li>
                                </ul>
</li>
</ul>



Lo próximo será definir nuestro código javascript, el cual recomiendo que sea puesto en un archivo aparte para mantener ordenado nuestro código. Aquí lo definiremos en el mismo código HTML para fines educativos y de simplicidad. Dicho código va al final del código que va dentro de las etiquetas body.

Comenzamos definiendo un módulo de Angular que va a tener la directiva ngRepeatEndWatch.

Y aquí surge una cuestión, cuando armamos un menú del tipo metisMenu, luego de definir los elementos debemos llamar al método (#idDelElementoContenedor).metisMenu(). Dicha llamada sólo puede realizarse cuando todos los elementos del menú están definidos.El problema con AngularJS es que justamente son llamadas Ajax dinámicas y asincrónicas, entonces tenemos que ubicar la llamada a metisMenu()cuando se terminan de obtener los datos a presentar y se arman los nuevos elementos HTML. Justo allí hay que ubicar la llamada a metisMenu y de esa manera nuestro menú se va a presentar correctamente, es decir, con la funcionalidad de colapsado y des colapsado. Para ello, necesitaremos definir una directiva que esté 'monitoreando' el fin del ng-repeat:

angular.module('miModulo', []).directive('ngRepeatEndWatch', function () {
                return {
                               restrict: 'A',
                               scope: {},
                               link: function (scope, element, attrs) {
                                               if (attrs.ngRepeat) {
                                                               if (scope.$parent.$last) {
                                                                              if (attrs.ngRepeatEndWatch !== '') {
                                                                                              if (typeof scope.$parent.$parent[attrs.ngRepeatEndWatch] === 'function') {
                                                                                                              scope.$parent.$parent[attrs.ngRepeatEndWatch]();
                                                                                              } else {
                                                                                                              scope.$parent.$parent[attrs.ngRepeatEndWatch] = true;
                                                                                              }
                                                                              } else {
                                                                                              scope.$parent.$parent.ngRepeatEnd = true;
                                                                              }
                                                               }
                                               } else {
                                                               throw 'falta ngRepeatEndWatch';
                                               }
                               }
                };
});


Una vez establecida la directiva, definimos nuestra aplicación 'miAplicacion' de AngularJS, quien va a contener 'miModulo' que a su vez es quien monitorea el fin del ng-repeat:
var miAplicacion = angular.module('miAplicacion', ['miModulo']);
Definimos un controller y dentro del mismo estaremos definiendo la variable 'miFuncion' que es el nombre que pusimos en el codigo html: ng-repeat-end-watch="miFuncion"

miAplicacion.controller('miControlador', function ($scope, $http) {
                $scope.miFuncion = function () {
                               $("#menu").metisMenu();
                }

Por último, agregamos dos llamadas a un repositorio de información:

                $http.get("http://www.w3schools.com/angular/customers_sql.aspx")
                .then(function (response) {$scope.paises = response.data.records;});

                $http.get("http://www.w3schools.com/angular/customers_mysql.php")
                .then(function (response) {$scope.nombres = response.data.records;});

                });
</body>

A continuación, se presenta el código completo. Se puede copiar y pegar en cualquier editor de texto y guardarlo como ".HTML":

<html>
                <head>
                               <meta charset="UTF-8">
                               <Title>metisMenu</Title>
                               <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css">
                               <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
                               <script src="https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js"></script>
                               <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
                </head>
                <body ng-app="miAplicacion" ng-controller="miControlador">
                               <ul class="metismenu" id="menu">
                                               <li ng-repeat="pais in paises" ng-repeat-end-watch="miFuncion">
                                                               <a href="#" >{{ pais.Country }}</a>
                                                               <ul >
                                                                              <li ng-repeat="nombre in nombres">
                                                                                              <a href="#" >{{ nombre.Name }}</a>
                                                                              </li>
                                                               </ul>
                                               </li>
                               </ul>
                               <script type="text/javascript">
                                               angular.module('miModulo', []).directive('ngRepeatEndWatch', function () {
                                                               return {
                                                                              restrict: 'A',
                                                                              scope: {},
                                                                              link: function (scope, element, attrs) {
                                                                                              if (attrs.ngRepeat) {
                                                                                                              if (scope.$parent.$last) {
                                                                                                                             if (attrs.ngRepeatEndWatch !== '') {
                                                                                                                                             if (typeof scope.$parent.$parent[attrs.ngRepeatEndWatch] === 'function') {
                                                                                                                                                             scope.$parent.$parent[attrs.ngRepeatEndWatch]();
                                                                                                                                             } else {
                                                                                                                                                             scope.$parent.$parent[attrs.ngRepeatEndWatch] = true;
                                                                                                                                             }
                                                                                                                             } else {
                                                                                                                                             scope.$parent.$parent.ngRepeatEnd = true;
                                                                                                                             }
                                                                                                              }
                                                                                              } else {
                                                                                                              throw 'falta ngRepeatEndWatch';
                                                                                              }
                                                                              }
                                                               };
                                               });

                                               var miAplicacion = angular.module('miAplicacion', ['miModulo']);

                                               miAplicacion.controller('miControlador', function ($scope, $http) {
                                                               $scope.miFuncion = function () {
                                                                              $("#menu").metisMenu();
                                                               }

                                                               $http.get("http://www.w3schools.com/angular/customers_sql.aspx")
                                                               .then(function (response) {$scope.paises = response.data.records;});

                                                               $http.get("http://www.w3schools.com/angular/customers_mysql.php")
                                                               .then(function (response) {$scope.nombres = response.data.records;});

                                               });
                               </script>
                </body>
</html>

 

Enlaces útiles



Autor:



Julián Haeberli

Baufest Technical Expert