Feeds:
Entradas
Comentarios

Archive for the ‘Filosofada’ Category


Hola, seguimos con la polémica del TDD, pero no temais, en este post no voy a hablar de TDD (al menos no mucho). En el anterior post defendí que si se hacía bien el TDD, con su refactor y que si además se complementaba con un pair programming, la necesidad de métricas estáticas de calidad de código desaparecen. Comentarios a dicho post por parte de @jmbeas, Chuidiang e @ydarias indicaban que mi postura era correcta en un equipo experto, pero que si tenías equipos menos maduros, entonces necesitabas estas métricas porque los equipos no iban a hacer TDD, refactor y pair programming, ya sea por inexperiencia o por simplemente no querer. Bien, esto es cierto, de hecho es interesante que al enseñar TDD y refactor a equipos novatos,  usemos análisis de código estático para ver como de forma incremental, y emergente, el código alcanza una buena calidad.

En el fondo, mi problema es que pienso que los equipos que no hacen TDD o pair programming, sencillamente no están haciendo agilismo… Enrique, no te metas en más líos, mejor digamos que sencillamente no están usando una buena metodología. En el resto de este post voy a explicar tan inverosímil afirmación.

El agilismo propone un metaproceso para definir un proceso de desarrollo e ir evolucionándolo en el tiempo, con el objetivo de adaptarnos a los cambios en nuestro entorno y mejorar dicho proceso de forma continua, de manera que maximicemos el valor entregado al cliente. No vale con que el proceso sea bueno hoy, sino que debe ser mejor mañana y no debe quedarse obsoleto. Y por mejor entendemos que entregamos más valor al cliente en menos tiempo y con menos coste. Obviamente, para poder implementar el proceso de desarrollo en la realidad, y poder hacer mejora continua, se nos propone un conjunto de buenas prácticas. Mi pregunta es, ¿cuál es el conjunto mínimo de buenas prácticas para considerarnos ágiles? Dicho de otra forma, ¿cuál es la metodología ágil más ligera posible? Dada la definición de agilismo anteriormente expuesta, la metodología ágil más ligera, es aquella que sólo obliga prácticas que proporcionen lo más rápidamente posible información de los problemas del proyecto, y nos ayuden a resolverlos cuanto antes. Armados con unos ciclos de feedback rápidos, que nos informen sobre problemas en el proyecto, podemos practicar mejora continua, y terminaremos con una metodología que es perfecta para nuestro entorno de trabajo específico, y que va a evolucionar para adaptarse a cualquier imprevisto.

Dicho esto, hay que tener en cuenta que los problemas nos lo podemos encontrar a múltiples niveles: estimación y planificación, desarrollo, build y pases de entornos, explotación, etc. Necesitamos pues, ciclos de feedback y mejora continua en cada uno de esos niveles. Obviamente algunos de dichos niveles escapan a mi control, pero al menos planificación y desarrollo sí están bajo mi control. Al conjunto mínimo de prácticas, que te permiten conseguir mejora continua de forma efectiva, lo llamaré agilismo minimalista.

A nivel de control de proyecto y mejora continua lo mínimo que puedes hacer es:

  • Retrospectiva al final de proyecto. Hay que reunirse para saber las causas del éxito o fracaso del proyecto, y que cosas se pueden mejorar y cómo. Es el nivel básico de mejora, aprender del resultado de un proyecto para el siguiente. Debería participar la dirección de la empresa también y el cliente.
  • Múltiples reuniones de seguimiento del proyecto, a ser posible a intervalos regulares, predecibles y ni demasiado largos ni cortos. Un ejemplo de reunión de seguimiento es la retrospectiva de sprint dentro de SCRUM. Otro ejemplo es la demo de final de sprint, este último más orientado a obtener feedback del cliente. Nos permite enterarnos rápidamente de los problemas que se producen durante el proyecto sin esperar a que acabe. Si hay algún problema podemos tener la oportunidad de solucionarlo. También podemos ajustar nuestras estimaciones con la realidad del proyecto, y aprender a estimar mejor en el futuro. Se puede involucrar a todos los participantes en el proyecto, al fin y al cabo no se hace todos los días. Esto es importante para que la información del estado del proyecto llegue a todo el mundo por igual y no se quede información escondida en nichos. También debe participar un representante del cliente, para ver como va el proyecto, proporcionar información adicional y aclarar dudas.
  • Reunión diaria o daily scrum. Es un nivel de feedback más rápido, diario, que involucra a los miembros de un mismo equipo y al menos un responsable. En proyectos grandes pueden haber varios equipos cada uno con su propia reunión diaria.
  • Reunión de emergencia. Para solventar cualquier problema grave y no previsto, detectado mediante cualquier mecanismo.

A nivel de planificación y estimación:

  • Dividir el trabajo en unidades manejables. Dichas unidades deben ser estimables, representar un incremento en el valor entregado al cliente, concisas y claras, de un tamaño similar entre si, independientes entre si, tener un criterio de aceptación y no demasiado grandes. Esto se suele conocer como historias de usuario. La importancia de las historias de usuario es que te proporcionan una unidad de planificación y estimación que está bien correlacionada con el valor del proyecto desde el punto de vista del cliente. El criterio de aceptación es importante para saber cuando se ha terminado de implementar la historia.
  • Revisa las estimaciones frecuentemente, teniendo en cuenta los resultados obtenidos en las retrospectivas y reuniones anteriormente mencionadas. Si las historias no fueran pequeñas, independientes y de tamaños similares, el ajuste de las estimaciones basándonos en lo que pasó en otras historias sería prácticamente imposible.
  • Refina y revisa la definición de las historias frecuentemente. Tal vez a intervalos regulares (SCRUM sprint planning) o tan pronto como te sea posible (kanban). Para ello hay que hablar con el propio usuario, o en su defecto con un experto de negocio.
  • Implementa primero las historias con más valor. Esto maximiza el valor entregado al cliente. También nos evita tener que implementar historias que pueden cambiar o quedar obsoletas con el transcurso del tiempo.

De momento a nivel de gestión y planificación, el agilismo minimalista no es ninguna sorpresa. El punto es que si no implementas todas estas buenas prácticas, no eres ágil. Simplemente no vas a poder reaccionar a los cambios en tu proyecto ni mejorar la forma de trabajo. Casi todas estas prácticas las realizan todas las empresas serias que conozco, excepto la reunión diaria, las historias de usuario y la implementación por orden de valor. Si no practicas la reunión diaria tendrás problemas ocultos y enquistados al menos hasta la próxima reunión de seguimiento, que será dentro de ¿entre 2 y 4 semanas? ¿No es muy ágil cierto? Hasta aquí casi todos estareis de acuerdo, pero todo esto no es suficiente para reaccionar ante problemas y mejorar en un proyecto software. Necesitamos tener en cuenta otro aspecto, la ingeniería.

Desde el punto de vista de mejora continua de la ingeniería y desarrollo:

  • Programación por parejas. Es otro ciclo de feedback, esta vez sobre el diseño y la calidad de código. Lo podemos considerar una revisión de código continua o una QA continua. El feedback proporcionado por un compañero a la persona que está tecleando es rapidísimo, del orden de segundos.
  • TDD. Nos permite saber si nuestro código funciona, o si la modificación que hemos hecho rompe algo. Feedback del orden de minutos.
  • Refactor frecuentemente. Dentro de un ciclo de TDD para garantizar que se hace a menudo, y guiado por la programación por parejas, nos permite mejorar de forma continua la calidad de nuestro código.
  • Integración Continua. Feedback a nivel de horas, que nos permite detectar problemas de integración.

Bien, con esto quiero decir que el agilismo minimalista necesita de mejora continua no sólo a nivel de gestión, sino a nivel de ingeniería, con lo cual necesitas hacer TDD, refactor, pair programming e integración continua, como mínimo, para ser ágil. Si no haces integración continua no detectas los problemas de integración hasta que no haces un pase, ¿es eso ser ágil? Si no haces TDD no detectas si has roto funcionalidad al introducir un cambio hasta que no lo prueba el equipo de QA, ¿es eso ser ágil? Además si no haces TDD no puedes hacer integración continua, sino como mucho, compilación continua. Si no haces pair programming no detectas errores de programación o fallos de diseño hasta dios sabe cuando, ¿es eso ser ágil? El refactor frecuente te permite arreglar tu código, sin necesidad de esperar al super arquitecto/guru. Lo importante es que todas estas prácticas se realimentan y se producen sinergias positivas entre ellas. Cada una cubre cosas que la otra no. Si quitas una sola el edificio se empieza a derrumbar. Por ejemplo si quitas el pair programming puedes introducir fake TDD o simplemente saltarte el refactor. Si quitas el TDD te cargas la integración continua. Las necesitas todas, no son opcionales, son obligatorias.

Ya contamos en la CAS2010 nuestra experiencia de implantación del agilismo. Hubo un momento que teníamos sprints, retrospectivas, etc. Pero no teníamos TDD ni pair programming ni se hacía refactor, y mucho menos integración continua. Eso no funcionaba. Asi que lo siento, a aquellos que piensen que puedes hacer agilismo con un equipo que no controle estas técnicas y no esté dispuesto a hacerlas, os aviso: no va a funcionar, no basta con el sprint planning y el daily scrum, no vas a ser ágil, necesitas prácticas ágiles de ingeniería y gente capaz de llevarlas a cabo.

Afortunadamente parece que hay gente por ahí de acuerdo conmigo, sino leed lo mismo que digo pero explicado de otra manera en este blog de Luis Artola. SCRUM y KANBAN sólo se ocupan del nivel de gestión y planificación de proyecto, pero no de la ingeniería. Para que tu proyecto sea exitoso, necesitas también buenas prácticas de ingeniería y trabajadores capaces de llevarlas a cabo. Muchos piensan que son ágiles porque aplican prácticas ágiles a nivel de planificación y gestión, pero se olvidan de la ingeniería. Esta es una maldición eterna en el mundo de los proyectos software, parece que por poner una capa de gestión se arreglan todos los problemas, pero nadie presta atención a lo más básico, la ingeniería y la capacidad profesional de los “pikachus”.

En resumen, lo mínimo que puedes hacer para considerarte ágil (agilismo minimalista) es: pair programming, TDD+Refactor, integración continua, reuniones diarias, reuniones periódicas de seguimiento (internas y externas para el cliente), retrospectivas de proyecto y planificación y estimación basada en historias de usuario. Si te falta, aunque sea una sola de estas prácticas, no eres ágil.

¿Y las demás buenas prácticas? Bueno, para mi no forman parte del agilismo minimalista, las considero muy dependientes del entorno en el que se mueva cada uno. Aquí es, donde creo yo, que se debe aplicar eso de probar a usar una buena práctica, experimentar y ver si va con tu proyecto o no. Lo importante es que con el agilismo minimalista irás descubriendo cuales son útiles y cuales no. Por ejemplo, a nosotros los sprints no nos han funcionado bien, por eso estamos pensando en pasar a kanban, pero conservando los principios del agilismo minimalista. Quizás esto de agilismo minimalista+kanban sea algo parecido al scrumban que he escuchado por ahí. En cualquier caso el resto de las prácticas debéis experimentar con ellas antes de adoptarlas en serio o no.

Corolario: si ves que necesitas métricas estáticas de calidad de código, es una señal de que no eres ágil, algo falla (pesaito soy). Saludos y ahora me voy a dormir, que ya me vale.

Read Full Post »


Hola a todos, de vuelta de la AgileSpain2010, y con un poco de “resaca” de la conferencia, me toca defender un par de frases que solté en nuestra sesión. Lo que ocurrió realmente fue lo siguiente:

Creando polémica en la CAS2010

Fanático del TDD en la CAS2010

Bien, pues lo solté y me quedé tan fresco, de hecho no me pareció que fuera una frase polémica, pero empecé a ver caras raras y a la salida de la sesión vi por twitter que mi frase había causado cierta extrañeza. Empezaré aclarando mi frase: “Si haces TDD bien no necesitas análisis de calidad de código estático”. Ojo, hablo de hacer TDD bien, no de hacer TDD a medias. Existe un malentendido respecto al objetivo del TDD, si bien uno de ellos es lograr un conjunto de pruebas automatizadas con alta cobertura de código, éste no es el único, de hecho es sólo la mitad de la historia. El otro objetivo, igualmente importante, es conseguir un código de calidad de forma incremental y/o evolutiva. De hecho algunos autores hablan de calidad o diseño emergente, pero yo prefiero no fliparme tanto de momento. Si tenéis el libro de Carlos Blé sobre TDD, veréis que se llama “Diseño Ágil con TDD”, no programación con TDD o pruebas con TDD o QA con TDD, sino diseño. Éste es el entendimiento general que todos los autores tienen sobre este tema: TDD lleva a pruebas automáticas de alta cobertura y a alta calidad de código, si lo haces bien, claro.

Llegado a este punto conviene explicaros mi percepción de los niveles de adopción del TDD:

  • Fake TDD. En este nivel de adopción el TDD no se practica, sino que se simula practicar. En el fake TDD los tests no representan realmente la funcionalidad de las historias de usuario o de la interfaz del componente que queremos probar. No se hace un esfuerzo serio por entender la funcionalidad del componente bajo pruebas y se escriben tests con poco contenido, contenido incorrecto o simplemente tests de relleno sin contenido. Ésto puede ser por desconocimiento de la técnica, con lo que habremos de dar más formación y hacer talleres. También puede ser por presión, que tiende a romper la disciplina del programador, y por miedo a no estar en fechas, se ignora el TDD. La tercera razón para hacer fake TDD la veremos en el siguiente post.
  • Naive TDD. Simplemente consiste en no realizar la fase de refactorización durante el ciclo de TDD. Las causas suelen ser de nuevo inexperiencia o prisas. Normalmente si no se entiende TDD como una metodología de diseño nos encontraremos en este caso. Otro tipo de Naive TDD se produce cuando se escribe primero código de implementación y después el test.
  • TDD. Adopción completa, haces test con funcionalidad correcta pero no consideras el ciclo terminado hasta que no has refactorizado. Para que un ciclo de TDD se considere completo y puedas hacer commit, debe existir un test, con los contenidos adecuados, que al ejecutarse pase, y el código que implementa la funcionalidad bajo prueba sea limpio. Si el código no es limpio debemos refactorizar.

¿Qué es pues código limpio? Pues es código que tiene unos niveles de calidad razonables, pero, ¿qué es la calidad del código? Difícil pregunta. Para ello las distintas organizaciones y empresas definen un modelo de calidad, que consiste en un conjunto de métricas de código que se van a tomar y que resultados mínimos son exigibles para esas métricas. Las métricas se clasifican en dos tipos: estáticas y dinámicas.

La métricas dinámicas miden propiedades de tiempo de ejecución del sistema. Ejemplos típicos son corrección, rendimiento y escalabilidad, seguridad y usabilidad:

  • La corrección la conseguimos con el propio TDD (hasta donde es posible dado que los requisitos son cambiantes y difusos).
  • El rendimiento y escalabilidad se consiguen con pruebas de stress, algo que está aparte del TDD, hasta donde yo sé.
  • La seguridad del mismo modo está en un mundo aparte, y que yo sepa no se pueden hacer pruebas automatizadas satisfactorias para esto, salvo quizás los ataques más típicos.
  • La usabilidad tiene que ver con la facilidad de manejo de la aplicación y su atractivo para el usuario, definitivamente esto no se puede probar de forma automática, para ello podemos usar pruebas de aceptación tradicionales.

Por otro lado las métricas estáticas miden propiedades del código que se pueden detectar en tiempo de compilación. Veamos:

  • Nomenclatura. Si tu código se ajusta o no a determinada nomenclatura. Bueno, que deciros, no considero esto importante para un desarrollo ágil, es mucho más importante la legibilidad y la documentación. Puedo entender que en un lenguaje con tipado dinámico esto pueda ayudar, al fin y al cabo no hay compilador que te diga si una variable es un string o un integer. Sin embargo si vas a usar un lenguaje de tipado dinámico (ruby, javascript, smalltalk, etc) es mejor que no pienses en java, sino que saques partido a las características de dicho lenguaje, que normalmente no está pensado para que una variable sea siempre un string, es simplemente otra filosofía de diseño.
  • Nivel de documentación. Bueno, esto sí es interesante, me habéis pillado :-) Sin embargo no considero que esto se pueda medir de forma totalmente automática. La dificultad radica en que es muy difícil automatizar la decisión de si un método o clase debe estar documentada. Desde el punto de vista del agilismo debemos documentar sólo aquello que merezca la pena, no todo, y por supuesto tampoco vale no documentar nada. Detectar si documentar un artefacto de código “vale la pena” o “o aporta valor” de forma automática es difícil, ¿no creéis?
  • Legibilidad. Lo más importante, tu código debe ser legible por tus compañeros, si no, no podrán mantenerlo. Esto tampoco se puede detectar automáticamente.
  • Estilo y formato de código. Esto realmente es un aspecto de la legibilidad.
  • Tamaño del sistema. No se muy bien para que se quiere medir esto, y además, ¿en qué lo medimos? ¿Lineas de código?¿Puntos función? Sin comentarios. Tal vez lo que queremos medir realmente es la cohesión.
  • Alta cohesión y bajo acoplamiento (principio de una única responsabilidad). Realmente estas son muy interesantes de medir y a mi juicio sí que son necesarias. Los artefactos de código que tengan muchas interdependencias entre ellos deben agruparse en artefactos de nivel superior. Por ejemplo, un montón de métodos que son muy interdependientes podrían agruparse en la misma clase. Y viceversa, si un artefacto esta compuesto de muchos subartefactos que apenas interaccionan entre si, podemos dividir ese artefacto en varios más pequeño. El ejemplo típico es la clase “monstruo” con todos los métodos del sistema dentro de ella, ay, cuantas de éstas habré visto a lo largo de mi carrera. Estas propiedad del sistema sí que se pueden medir automáticamente, al menos en los lenguajes de tipado fuerte. Si se pueden medir automáticamente en los lenguajes de tipado dinámico tengo mis dudas, pero me callo por no ser experto.
  • Duplicación de código. Esto se puede medir fácilmente con una herramienta. La existencia de código duplicado es un signo de mal diseño y baja calidad, excepto en el caso de extrema necesidad de legibilidad.
  • Malas prácticas. ¿Acaso el analizador de código estático es inteligente? No, es tonto, es un robot que se limita a pasar patrones sobre el código. Si no tiene inteligencia, ¿cómo va a saber si una práctica es mala? ¿Acaso entiende el código? Sólo va a detectar las malas prácticas más sencillas. No me lo creo a pesar de lo que me digan los vendedores de herramientas sofisticadas (y caras).

Existen muchísimas más métricas estáticas, algunas de alta complejidad. Yo personalmente no las entiendo, ni veo cual es su sentido. Todavía no conozco a nadie que me las haya podido explicar, así que de momento me quedo con las que os he contado anteriormente. En cualquier caso, ¿qué utilidad tiene medir una cosa sin entender lo que estás midiendo? Si alguien me sabe explicar alguna métrica arcana, que sea esencial para medir la calidad del software, y no haya mencionado, que me lo comente.

En el modelo de calidad que yo uso, y que me gusta pensar que es un modelo de calidad ágil, considero que el código es limpio si cumple en la medida de lo posible los siguientes criterios de calidad: corrección, legibilidad, sin código duplicado, bajo acoplamiento y alta cohesión. Si os fijáis, excepto la legibilidad, el resto de los criterios te los incorpora de forma natural el ciclo de TDD que incluye la refactorización. La legibilidad es algo que no se puede comprobar automáticamente. O sea que si hacemos TDD bien, y encima lo reforzamos con otras buenas prácticas, como la programación por parejas o la revisión de código, no necesitamos para nada complicar vuestro build con análisis de código estático. Una aclaración, yo considero que el código es limpio si todo el código pasa el modelo de calidad anteriormente mencionado. Esto implica no sólo el código de implementación, sino el código de test. La tarea de refactorización incluye también al código de test no sólo el código bajo pruebas. No es válido tener código duplicado en el código de test, y este debe ser legible, y poseer una buena cohesión. De hecho las pruebas unitarias deben ser independientes unas de otras, lo que implica un acoplamiento entre ellas muy bajo. Un error típico es no refactorizar el código de test, sólo el de implementación.

Muchos estareis pensando que todo esto es muy bonito, pero lo que queréis realmente es controlar que las cosas se hacen bien. Así que muchos me diréis, ¿cómo controlo que el código está bien hecho?¿Cómo se que se está haciendo TDD bien y no me están engañando? Muchos de los expertos en agilismo os dirán que el simple hecho de que hagáis esta pregunta significa que no sois ágiles. Si necesitáis que una herramienta os diga si el equipo está programando según las buenas prácticas es que no estáis al lado del equipo, que no practicáis go&see y que estais encerrados en vuestros despachos sin interactuar realmente con la realidad, es decir, con vuestro equipo.

Ciertamente lo comentado en el anterior párrafo puede ser cierto en las circunstancias típicas de un proyecto ágil. En otras circunstancias, como en el caso de trabajar con equipos distribuidos, es lícito que tengáis dudas sobre si vuestro equipo hace el TDD como debiera, ya que al fin y al cabo, no podéis estar en la misma sala con todos los equipos a la vez. Así pues, si tienes equipos distribuidos, ¿cómo sabes que se hace bien el TDD? La respuesta está en el nivel de cobertura y en aplicar un poco de psicología:

  • Si la cobertura es baja, entonces realmente no se está haciendo TDD bueno. Puede ser porque se está haciendo Fake TDD, y por lo tanto los tests al no ser suficientemente completos no ejerciten bien el código. Puede ser también que tengamos Naive TDD, y se escriba código de implementación antes que el test, con lo que puede quedar zonas del código de implementación sin probar. La falta de refactorización del naive TDD también puede llevar a baja cobertura, después lo comento. También puede ocurrir que simplemente no se ha hecho TDD en todos los casos, y se ha escrito código de implementación sin ningún tipo de test.
  • La cobertura es alta (mayor del 70%). En este caso existe una alta probabilidad de que el código tenga una buena calidad y se haya hecho TDD del bueno con refactorización. La razón de esto es sutil. Lógicamente si hacemos TDD bien vamos a tener una cobertura alta. ¿Podemos llegar a tener cobertura alta haciendo Fake TDD? Claramente no, a menos que el programador dedique sus esfuerzos a sabotear el proyecto y haga casos de tests con alta cobertura, que pasen pero que no prueben nada. Ciertamente esto es bastante bizarro. Sobre la gente que se dedica a hacer esto hablaré en el siguiente post ¿Pero y el Naive TDD? Al fin y al cabo sí hacen tests, podríamos tener alta cobertura a pesar de no refactorizar, ¿no? La verdad es que ciertamente es posible pero es difícil. Al no refactorizar duplicas código, y el diseño de tus clases hace que se vayan volviendo cada vez más difícil de testear, ya que no guardan el principio de alta cohesión, bajo acoplamiento, etc. Por un lado duplicar código hace que tengas que duplicar también los fragmentos de test que ejercitas ese código. Esto se va haciendo pesado y se tiende naturalmente a no hacerse con lo cual la cobertura baja. También la duplicación invita a que los tests fallen, ya que un cambio en una historia de usuario o un bug, implica cambiar código en muchas zonas de la aplicación, con lo que harán que el test que prueba ese bug falle. Finalmente, al no refactorizar, las clases van perdiendo calidad, y va siendo cada vez más difícil hacer pruebas unitarias, ya que estas se van acoplando cada vez más con su entorno, y no presentan una buena encapsulación y cohesión. Como vemos el no refactorizar al principio no tiene mucha importancia, pero conforme pasa el tiempo se hace más difícil añadir tests, con lo cual los tests se hacen más laxos y la cobertura baja.

Yo recomiendo encarecidamente mezclar TDD con dos prácticas más: programación por parejas y revisiones de código, especialmente de los tests. Ambas contribuyen a que los desarrolladores cuiden su prestigio y no pierdan la disciplina, con lo que baja la probabilidad de que rompan el ciclo de TDD. La programación por parejas tiene la ventaja adicional de enseñar la técnica a los más novatos. Os puedo contar que en el último proyecto estaba presionado de tiempo y rompí la disciplina del TDD para una funcionalidad que era “trivial”. Como no estaba haciendo programación por parejas pude hacer esta trampa. ¿El resultado? ¡ Zas, en to la boca ! Efectivamente sufrí una concentración de bugs en ese código “trivial”, necesité una jornada de 20 horas de programación ininterrumpida para solventar el desaguisado. Cuando llegué a casa mi mujer casi me mata de un sartenazo pensando que era un vulgar ladrón. Después estuve una semana KO por el sobreesfuerzo. Nunca más.

Si aun veis que queréis un análisis estático de código podéis usarlo como método didáctico. Conforme vais haciendo TDD, ireis viendo como vuestra cobertura aumenta y también los resultados de las métricas de calidad. Esto es recomendable para equipos que empiezan con el TDD y todo esto de la refactorización. Cuando se trata de un equipo que sabe hacer TDD y refactor, realmente las métricas os sobran, así que podéis aligerar vuestro sistema de integración continua y ahorraros un dinero en licencias desactivando tales métricas.

Por supuesto existe una razón no ágil para esto de las métricas: el informe de colores para impresionar a la dirección y a los clientes. Si necesitáis un informe lujurioso para justificar el avance de vuestro proyecto estais en una situación mala. Yo justifico el avance de mis proyectos con demos, enseñando software que funciona y que cada vez tiene más funcionalidad.

En el siguiente post os cuento la otra pequeña polémica que tuvimos.

Read Full Post »


Hago un alto con la serie de posts sobre la estructuración de los equipos para hacer una introducción a un tema candente, las bases de datos no relacionales y el movimiento noSQL.
Algunos pensarán que esto del noSQL es una moda más, pero lo curioso es que se enfrenta a otra moda mucho más poderosa e influyente, el SQL y las base de datos relacionales. En muchos sitios me enerva una práctica muy común que consiste en que al diseñar la aplicación y analizar los requisitos lo primero que se hace es… ¡el modelo de tablas relacional! Muchos consideran que la aplicación está casi hecha en cuanto tenemos un esquema de base de datos y unas sentencias SQL, y acoplan todo su código a esto. Esta forma de diseñar aplicaciones está muy bien cuando trabajas con COBOL pero desperdicia por completo las características de cualquier lenguaje de programación OO moderno. La cosa llega a tal extremo que muchas personas sólo piensan en aplicaciones CRUD y de hecho hay frameworks como Grails o Rails que te proporcionan una aplicación CRUD muy rápidamente si no te importa acoplarte con la BBDD.
Mi filosofía es un poco diferente, usar Acceptance TDD y DDD y DSL (a.k.a. API Sexy) para definir un core de negocio orientado a objetos que sea fácil de probar, fácil de usar y modele el dominio de la aplicación. Es decir, como uso OO, lo que hago es realmente modelar el negocio mediante un modelo de objetos, que refleja la realidad del problema que queremos resolver y contiene la lógica de negocio. Sobre este modelo monto un DSL para poder trabajar con el modelo escribiendo código que parezca lenguaje natural o al menos sea legible por el analista funcional. En esta filosofía de trabajo la interfaz de usuario y la capa de persistencia son periféricas a este core de negocio. De hecho se puede diseñar el sistema para que tenga distintas interfaces de usuario y puedas cambiar el mecanismo de persistencia de forma sencilla.

Desde este punto de vista, si yo diseño mi sistema con un core OO de negocio robusto, y necesito almacenar los objetos de forma persistente, entonces debo usar una capa de persistencia, y no me importa especialmente cual sea mientras cumpla mis requisitos de la forma más sencilla posible. Desde este punto de vista analicemos lo que nos ofrece una base de datos relacional:

  • Persistencia. Ok, es lo que busco.
  • Lógica de negocio, en forma de procedimientos almacenados y triggers. Pues no, esto me sobra, lo tengo en el core de negocio.
  • Transacciones ACID. Esto depende, si estoy haciendo operaciones críticas que deben realizarse de forma instantánea desde el punto de vista de negocio entonces sí. Si estoy escribiendo una aplicación de redes sociales y quiero escribir una actualización de estado, no me importa mucho si falla al grabarse, o tengo lecturas fantasma. Otro ejemplo de operación no ACID es el de una transferencia monetaria bancaria. Ciertamente das de alta la transferencia de dinero de forma atómica, pero la consolidación de los balances entre la cuenta destino y origen puede tardar días, teniendo mientras tanto un estado “inconsistente”.
  • SQL. Un lenguaje de query complejo, que me permite hacer casi cualquier consulta y que para optimizarlo tengo que ser un experto en BBDD. No gracias, las consultas complejas las hago en mi core de negocio, en mi lenguaje de programación favorito, y por que no, con mi DSL. Yo sólo quiero grabar, leer, y hacer unas cuantas consultas muy concretas, no cualquiera. Además, ¿cómo puede una BBDD ser más óptima en la ejecución de la consulta que mi código, hecho específicamente para mi problema? Es el típico caso de que una solución genérica a un problema no puede ser más eficiente que una solución específica para un problema específico. En este caso SQL es un lenguaje de query genérico, y no conoce los detalles específicos y posibles optimizaciones de mi aplicación, sólo puede usar técnicas generales.
  • Esquema. Estructura rígida y tipada de almacenamiento en forma de tablas con columnas ¿Para que quiero eso, si la estructura y el modelo y los tipos y validaciones del sistema está en el core OO? Realmente esto es un dolor, todos habréis sufrido con problemas de conversión de tipos entre columnas de tablas y campos de objetos, o entre objetos y tablas. Tal es el problema que nuestra aplicación termina haciendo uso de algún framework de mapeo objeto relacional, como Hibernate o iBatis o JPA, que añade otro grado de complejidad y configuración innecesario. Es innecesario porque la estructura de la información ya está en el modelo de objetos, no es necesaria replicarla en la capa de persistencia.

Si somos prácticos y aplicamos el principio KISS, vemos que una BBDD relacional puede ser, en muchos casos, algo que nos añade una complejidad innecesaria. Nos añade funcionalidades duplicadas con el core OO o simplemente innecesarias en algunos casos. Lo que ocurre es que el típico escenario de aplicación, el típico caso de uso, está cambiando respecto a lo que era antes. Cada vez tenemos más aplicaciones que tienen alguna o todas de las siguientes características:

  • Deben soportar grandes volúmenes de datos. Pensad en cualquier red social o en aplicaciones de Google, como el google maps.
  • Deben soportar altas cargas de transacciones online, debido a que el número de usuarios en la web puede ser enorme. De hecho cuantos más usuarios tengamos, los señores de negocio estarán más contentos.
  • El rendimiento no debe degradarse debido a los dos puntos anteriores.
  • ¡Que no se caiga! Hoy en día la caída e indisponibilidad de una aplicación tiene consecuencias monetarias muy fuertes.
  • En aplicaciones como las redes sociales, la consistencia no es crítica, con lo que las transacciones ACID no son necesarias.
  • ¡Fácil de usar! No quiero sufrir para simplemente grabar datos y hacer cuatro consultas básicas. No quiero saber nada de configuraciones de Hibernate o explains de Oracle.
  • ¡Fácil de integrar con un core OO de negocio!

Como vemos en muchos tipos de aplicaciones, las bases de datos relacionales te ofrecen capacidades que no te interesan a cambio de complicarte la vida. Por otro lado puedes tener muchos problemas con los requisitos de rendimiento, escalabilidad y disponibilidad. Para solucionar esos problemas vas a necesitar expertos, máquinas y productos normalmente caros. Lo peor que es que dichos expertos pueden coger una posición de poder y empezar a imponer sus reglas. Lo ideal es que las reglas las ponga el cliente, no un grupo de sacerdotes de una oscura tecnología.

Hasta ahora por tradición y por moda (o por imposición) cada vez que se nos planteaba la necesidad de persistir información elegíamos una BBDD relacional de forma automática y sin pensar. Como la integración entre un core OO y la BBDD relacionales es complicada, la gente llegó a varias soluciones (no necesariamente excluyentes):

  • No usar un core OO de negocio. Me hago una aplicación igual que la que hacía en COBOL pero traducida literalmente a JAVA.
  • Uso un Grails o un Rails, me acoplo al esquema de la BBDD y ya está.
  • Uso mi framework de mapeo objeto relacional (Hibernate, iBatis, etc),  e invierto esfuerzo en configurarlo y optimizarlo.

Esto es un razonamiento torticero, como tengo que usar BBDD relacionales entonces me complico la vida o paso de la OO. Es decir, me creo problemas “artificiales”, que no existían originalmente, porque elijo no solucionar el problema original (la persistencia) de forma óptima, sino hacer según manda la tradición y la moda. Lo lógico es analizar que tiene que hacer tu sistema de persistencia y después decidir qué usas. Puedes terminar usando una BBDD relacional si realmente la necesitas, pero en la mayoría de los casos os daréis cuenta que no os hace falta. Este es precisamente el punto de partida del movimiento noSQL.

Resumiendo, el movimiento noSQL, entre otras cosas, te propone:

  • No tienes porque usar una BBDD relacional, tienes alternativas para la persistencia de tu modelo de objetos.
  • No te hace falta un lenguaje de triggers ni de consultas complejas ya que la lógica de negocio está en la aplicación, no en el sistema de persistencia.
  • No necesitas un esquema rígido en tu sistema de persistencia, ya tienes tu modelo de objetos. Puede que incluso trabajes con un lenguaje de programación de tipado dinámico (js, ruby, etc). Simplemente quieres persistir objetos enteros o documentos. La estructura exacta de lo que guardes no debe ser de la incumbencia del sistema de persistencia, sino de tu modelo de negocio.
  • Te vale una API sencilla y un modelo de consulta sencillo. Uno muy popular es el clave valor, o tabla hash persistente. Guardas objetos o documentos y los asocias a una clave para poder recuperarlos.
  • Puedes prescindir de ACID para conseguir alta disponibilidad, escalabilidad y bajos tiempos de respuesta.
  • No odies SQL, simplemente debes saber cuando usarlo y cuando no. Hay casos en los que es la mejor solución, pero no en todos, y creo que cada vez en menos.

En siguientes posts hablaré sobre que sistemas de persistencia noSQL son más usados, su paradigma de desarrollo y como podemos eliminar, en muchos casos, la necesidad de transacciones ACID y el 2PC, con el objetivo de aumentar la disponibilidad y escalabilidad. Iré mezclando posts de noSQL con los de estructuración de equipo para que haya variedad.

Read Full Post »


La idea para este post me la dio un comentario en el post de víctimas de la moda, alguien dijo que un arquitecto era visto como 5 programadores por parte de los gestores de proyecto. Ésto me hizo pensar en escribir una serie de posts abordando como se suelen montar los equipos de desarrollo y el efecto de éstos en el éxito de los proyectos. En este post daré mi opinión sobre los roles en un proyecto típico.

Muchas veces viendo como se organizan los equipos de desarrollo me da la impresión de contemplar un sistema social basado en castas, más propio de eras anteriores que de un entorno de alta tecnología. En mi opinión en muchos entornos empresariales los roles de los miembros de un equipo típico se asemejan más a clases sociales que a otra cosa. Esto es una tendencia muy peligrosa que hace que el equipo no actúe como tal, sino como una jerarquía señor/vasallo, y se transforme en varios grupos que desconfían los unos de los otros y se desprecien mutuamente. ¿Cómo se llegó a esta situación? Es difícil de saber, pero creo que hay varias causas:

  • El mal entendimiento por parte de los gestores en muchos clientes de lo qué es un proyecto software.  Ésto es comprensible ya que la mayoría de los clientes no saben construir software, no es su negocio, ¡ si lo supieran no necesitarían subcontratar ! Esta falta de entendimiento fue explotada por algunas empresas del sector, lo que ha creado desconfianza ¿Recordais cómo era todo antes del crack de las punto com…?
  • El concepto de gestor universal. Un gestor universal es alguien que cree que puede gestionar un proyecto de un área que no entiende usando herramientas genéricas. Tal gestor aplicará las mismas técnicas de gestión a un proyecto de software que a un proyecto de construcción de vivienda que a la organización de un equipo de administrativos. Obviamente esto es una concepción errónea que lleva al desastre. Si creemos en este principio terminamos con jefes de proyecto que no saben informática y que no se enteran de lo que ocurre de verdad en su proyecto.
  • Derivado del punto anterior nace la concepción errónea de que los informáticos son intercambiables, como si fueran piezas mecánicas de una maquinaria. Ciertamente esto puede ser real en otras áreas de gestión, pero no en la informática. El resultado típico es que los gerentes ven a los técnicos como simples obreros que no dan valor añadido y no son muy distinguibles de un administrativo, generando actitudes altivas y poco colaborativas y dialogantes. Además este prejuicio puede llegar a hacer que se considere que un técnico deba cobrar poco comparado con otros roles. Como reacción el técnico pensará mal del jefe de proyecto y lo considerará un opresor. Esta creencia es falsa, entre otras cosas porque un técnico tiene mucha información en su cabeza sobre el proyecto que uno recién llegado no tiene, y la productividad de los técnicos varía en función de su talento de una forma mucho más acusada a como varía ésta entre dos personas que realizan tareas mecánicas y repetitivas.
  • Carrera profesional. Como cada rol pertenece a una casta, es normal pagar más dinero a las castas más altas. Como la casta más baja es la de programador entonces será la que menos cobre. Te puedes encontrar con un jefe de proyecto sin nada de experiencia que cobre mucho más que un desarrollador con mucha experiencia y que produzca por diez desarrolladores normales. Lo que es peor, este desarrollador experto y talentoso tendrá un sueldo similar al de un desarrollador sin iniciativa ni talento. Esto hace que los desarrolladores se desincentiven, y no quieran seguir siendo desarrolladores, sino jefes de proyecto. Al fin y al cabo nadie va a reconocer nuestra capacidad como tales. Esto genera una dinámica que lleva a que escaseen los desarrolladores expertos y arquitectos, disminuyendo la calidad del equipo y aumentando la dificultad de contratar a un nuevo desarrollador que sepa lo que hace.

Todo esto puede generar dinámicas muy dañinas y un sistema de trabajo vicioso:

  • Alta rotación. Los miembros de las castas más bajas se sienten maltratados y deciden marcharse. Ésto genera a su vez que el conocimiento escape del equipo y la productividad baje. Como reacción el gerente para reemplazar esta baja puede decidir contratar. Como éste y RRHH no saben distinguir a un buen técnico de otro que no lo es, le resulta muy golosa la opción de contratar más barato. De esta forma podrá tener más miembros en el equipo con lo cual todo va a ser más productivo, ¿verdad? Y además al ser más barato se lo tendrá “menos creído y será más dócil”. Total, tampoco es muy importante la cualificación, porque no es más que un “obrero”. Para escribir en un teclado no hace falta saber mucho. Por este camino puedes llegar a terminar con un equipo bastante grande, lleno de gente sin capacidad técnica y/o sin iniciativa.
  • Sabotaje. El técnico está tan quemado que simplemente realiza código de baja calidad, no hace pruebas, copy&paste, etc. Al fin y al cabo al jefe de proyecto sólo le interesa llegar en fechas, y tener terminado tal funcionalidad para la reunión de seguimiento tal, que si no le riñe el cliente. Además el jefe de proyecto no es técnico con lo que no puede detectar estas malas practicas. El que se introduzcan defectos en el software no es evidente en el momento, lo que hace que el jefe de proyecto piense que todo va bien hasta que la situación explota.
  • Más madera. Como tenemos problemas, pongamos más gente. Hay que presionar a RRHH para que contraten rápido y barato. RRHH ante esa presión contrata rápido y barato, es decir mal. Tu equipo crece sin mesura y se llena de técnicos sin capacidad.
  • Zombis. Ya que contratamos barato, puedo convencer al cliente de que su proyecto va mal y necesita “más madera”. Yo le vendo que le contrato 10 técnicos de los buenos más, a tarifa buena y contrato 10 cadáveres para que calienten la silla. Los cadáveres son baratos y el volumen del proyecto aumenta, con lo que es más rentable. Realmente esto es un técnica propia de consultoras sin ética ni escrúpulos, que es posible debido a que el cliente no sabe diferenciar entre zombis y desarrolladores.
  • Proyecto cerrado y horas extras como recurso común. La defensa del cliente ante “más madera” y “zombis” es cerrar el proyecto en tiempo y dinero. De esta manera no puede ser engañado por empresarios sin escrúpulos. Esto genera que cualquier sobrecarga de trabajo se termine ejecutando como horas extras y la consiguiente bajada de productividad y “quemamiento” del equipo, lo que aumenta la rotación.
  • Metodología pesada. Debido a “más madera”, tienes un equipo muy grande. No sabemos como hacer que muchas técnicas del agilismo escalen bien a equipos grandes. Además los malos resultados del proyecto hacen que el cliente desconfíe. Como conclusión los gestores se sienten reafirmados en su metodología pesada, ya que el agilismo es “para proyectos de juguete” y “tenemos que controlar a todos estos sinvergüenzas”.

Si pensáis que lo que cuento es una exageración, yo he conocido proyectos en los que los jefes de proyectos no sabían manejar un ordenador pero si hablaban un inglés de nivel. He visto zombis atestar salas de trabajo, sí, lo he visto en alguna gran consultora, aunque afortunadamente nunca he trabajado para ellos y no todas son tan malignas como las pintan.

Lo peor es que esta dinámica se refuerza a si misma con lo que es muy difícil de romper y se transforma en el modelo mental por defecto de las personas involucradas en la informática. Lo aceptan como una verdad evidente y no se plantean que las cosas pueden ser de otra forma y te dicen olvídate del agilismo, es un cuento de hadas“. Cuando alguien llega nuevo a este negocio, los que ya están en él, y metidos en esta dinámica viciosa, te cuentan que las cosas son como son y que no se pueden cambiar. Que se contrata barato y mal porque no te puedes fiar de un desarrollador y el proyecto siempre se sale del presupuesto; que se hacen proyectos cerrados para que el presupuesto no se vaya de madre y no nos engañen; que te olvides del TDD que eso lo que hace es quitarte productividad y nadie te lo va a agradecer; que te olvides del pair programming, que lo único que hace es disminuir la productividad a la mitad, y encima cuando viene el cliente ve la mitad de sillas ocupadas… No se dan cuenta que esta dinámica ineficiente no es una propiedad intrínseca de los proyectos software sino causados por una mala gestión y por muchas concepciones erróneas. Ésto es una de las causas más importante de resistencia al agilismo, el pensar que el desarrollo de software es así y no se puede hacer de otra forma.

Precisamente en el agilismo se intenta eliminar este ciclo vicioso. Para ello usa diversas técnicas:

  • Tener una persona que actúe como puente entre el cliente y el equipo. Sepa defender los intereses del cliente pero de forma razonable e interaccione frecuentemente con el equipo. Debe filtrar y priorizar los requisitos del cliente y ser capaz de pasárselos al equipo. Es como tener un cliente a tu lado continuamente, es el Producto Owner.
  • Enseñar resultados frecuentemente al cliente. Resultados tangibles, demos, pruebas de concepto, prototipos, la aplicación parcialmente construida, etc. El cliente puede no entender de software pero si que va a entender la demo. Al permitir un mejor seguimiento, evita recelos por parte del cliente y aumenta la confianza con lo que la dinámica social del proyecto mejorará.
  • Persona que actúe como jefe de proyecto, actuando como enlace principal con el Product Owner, siguiendo el desarrollo del proyecto día a días, y encargado, de forma principal, de eliminar cualquier impedimento para el buen avance del proyecto. Al contrario que el jefe de proyecto tradicional, debe tener la suficiente capacidad técnica como para entender problemas de índole técnica, además de problemas organizativos. De esta forma será capaz de tomar decisiones cabalmente y detectar malas prácticas. Además debe hacer una labor de enseñanza del método de trabajo que se esté siguiendo. En SCRUM, por ejemplo, a este rol se le denomina Scrum Master.
  • Tener siempre en el equipo a un miembro de gran talento y experiencia que además de trabajar y producir en el día a día, se encargue de evitar pitfalls típicos, de plantear soluciones problemas difíciles y que actúe además como mentor del resto del equipo. Tradicionalmente lo más parecido a esto es un arquitecto. La diferencia está en que es un arquitecto que trabaja continuamente con su equipo, produciendo código y enseñando a los demás. No es alguien que aparece para arreglar un incendio y desaparece. Ni tampoco alguien que desprecia a los desarrolladores, se dedica a pintar cajitas y citar a gurús. Si la carga de trabajo lo permite, este mentor y el Scrum Master pueden ser la misma persona.
  • Motivar a los desarrolladores. Pagarles justamente y evitar horas extras como recurso común, escuchar lo que tengan que decir (daily meetings, retrospectivas, reuniones de planificación, …) Esto evita la rotación y aumenta el entusiasmo del equipo. También evita el cansancio de las horas extras.
  • Si se necesitan horas extras, que sea de forma excepcional, y que todo el mundo las sufra, desarrolladores, jefes de proyecto, etc.
  • Contratar a personal competente y eliminar de tu equipo a personas sin motivación para hacerlo bien y sin capacidad de hacer su trabajo. Esto normalmente implica hacer una buena selección de RRHH, lo que a su vez es incompatible con las prisas.

Un error común al implantar el agilismo es usar como Scrum Master a un jefe de proyecto con la mentalidad tradicional, y contratar como miembros del equipo a personas sin motivación y sin capacidad técnica. Esto hace que al final no se haga agilismo, por mucho que los roles parezcan ágiles. El desarrollador sin motivación y capacidad producirá poco, no escribirá código con calidad suficiente, no tendrá iniciativa, etc. El jefe de proyecto que siga pensando que está por encima del resto de los miembros del equipo y que no entiende el proyecto, generará la misma dinámica viciosa de siempre.

Otro error clásico es hacer que el Product Owner o no exista o sea el mismo jefe de proyecto. Esto es un error porque el primero representa al cliente y el segundo al equipo, si hacemos coincidir los dos roles en el jefe de proyecto corremos el riesgo de no atender a las necesidades y prioridades reales del cliente y caer en prácticas no éticas. Si hacemos coincidir ambos roles en una persona del cliente el proyecto se convierte en barra libre y nadie atiende a las necesidades y problemas reales del equipo.

En el fondo de todo esto está la enseñanza que creo más valiosa del agilismo: lo más crítico no es el proceso sino la gente que componen el equipo y dan vida a ese proceso. No gastes pues tanto en procesos, y preocúpate porque la gente que contrates sea apta para su puesto y sobre todo que sepan jugar en equipo, al fin y al cabo el desarrollo de software es un juego de equipo, no de individualistas.


Read Full Post »

Víctimas de la moda


Bueno pensaba que mi primer post iba a ser técnico, pero no, me han entrado ganas de soltar una “filosofada”.

Desde hace ya un tiempo me he dado cuenta que mucho de los profesionales que nos dedicamos al desarrollo de aplicaciones web hemos caído en la tentación de tomar decisiones técnicas basadas en la moda. Yo suelo tener que vigilarme a mi mismo para no caer en esta trampa.

¿Quién no se ha encontrado pensando que para su nuevo proyecto va a usar las últimas tecnologías que están más de moda? Quizás es que nos aburrimos de usar siempre las mismas tecnologías o simplemente no queremos quedarnos “deprecated” y evolucionar.

Sin embargo esta forma de tomar decisiones nos puede llevar al desastre en un proyecto, si elegimos la tecnología equivocada, el proyecto puede fracasar. No nos servirá de nada decir que es lo último, lo más moderno, que era el proyecto de código abierto más popular o que el google trends decía que se iba a imponer. Al fin y al cabo somos ingenieros y debemos tomar decisiones basadas en razonamientos sólidos, evaluar coste/beneficio y ver que herramientas son las más adecuadas para nuestra misión. No deberíamos dejarnos guiar por nuestros deseos o dejarnos engañar por campañas de marketing viral.

Lo peor ocurre cuando te encuentras con algún arquitecto o programador o incluso cliente que ya ha transcendido su etapa de víctima de la moda y se ha transformado en un fanático. Algunas citas textuales de fanáticos que me he encontrado: “pues el framework X es el mejor” o “eso que haces es una mierda, es mucho mejor la tecnología Z” o la última que escuché, “es que el framework XYZ es el que hace todas las cosas chulas”. Siempre termino preguntándoles ¿Por qué? y ellos responden “¿No lo sabes? Todo el mundo lo usa…”

Usemos las herramientas adecuadas para cada tipo de problema.

No seamos víctimas de la moda, sino profesionales.

Read Full Post »

« Newer Posts

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 42 seguidores