Cuando la crisis golpea una consultora IT

14 comentarios
Estaba cantado. La crisis ha llegado a nuestro sector. Va a quedarse y va a hacer daño. Ha tardado lo justo en que las empresas del resto de sectores ya en crisis se han recortado su presupuesto en proyectos IT, tanto nuevos como de mantenimiento. El resultado (previsible) es que el modelo de negocio de las grandes consultoras IT no se sostiene y no se sabe adaptar a la nueva situación.

Son ya muchos años explotando un modelo de servicios desde un punto de vista basado unicamente en los números a corto plazo. Sin importar mucho más. Tecnología, conocimiento, proyectos, trabajadores o clientes. Son mentes financieras las que mueven los hilos al más alto nivel. Para ellas, el negocio IT no es muy diferente de cualquier otro, como podría ser el de las máquinas expendedoras.

Modelo de negocio Máquina expendedora

Existe una necesidad: el código. Y las consultoras hacen negocio con máquinas expendedoras de código. El código es un bien caro y se genera directamente dentro de las máquinas. Sin necesidad de comprar ninguna materia prima.

Que un cliente necesita código, pues negocias cúantas máquinas de cada tipo le pones, por cúanto tiempo y, ala, a facturar.

Las máquinas no tienen coste inicial, sólo de mantenimiento (salario) pero tampoco es que sea mucho. Como cualquier coste, se intenta reducir al máximo. No tiene mucha más preocupación. La prueba está en que existe una generalizada inversión de salario, donde no siempre a una máquina que aporta mayor valor le corresponde un mayor coste (salario).

Las instalaciones tampoco es que sean excesivamente costosas (luz, agua, ordenadores, internet,...) o amplias, ni necesiten estar estrategicamente situadas. Además, con suerte, al cliente le da por tener las máquinas en sus propias instalaciones, por lo que es un gasto menos.

En realidad no hace falta un modelo de negocio muy sofisticado. Ya es un negocio por si solo!

Últimamente estaban teniendo el doble problema de que se iban demasiadas máquinas y que además habia escasez de nuevas máquinas. La solución (muy finaciera) ha sido (1) adquirir máquinas que no son expendedoras de código pero pueden pasar por ellas (o al menos se lo parece), (2) importar o usar máquinas de otros paises y (3) subcontratar a otras consultoras más pequeñas y baratas. En realidad nada grave que requiera cambiar el modelo.

El golpe

El modelo era un éxito. Los records de facturación se han ido sucediendo año tras año. Hasta ahora. Al caer la economía de los grandes clientes de código, las consultoras se encuentran con excedentes de máquinas, los beneficios han caido y los objetivos se alejan.

Las primeras medidas

Una vez más, se toman medidas desde un punto de vista financiero. Dejar de contar con ese excedente de máquinas no es caro, mientras no sean muchas. Además las máquinas, de siempre, ni se quejan en exceso ni hacen mucho ruido. Así que no llevamos ni medio año de efecto crisis y ya asoman los primeros EREs. Y los que no se han dado ya es por no perder cotización en bolsa.

Otras medidas a tomar, igual de rápidas que de injustas, son:
  • Congelación de sueldos. El año pasado batiste record de facturación y se te paga congelándote el sueldo...
  • Invitaciones al personal en available para tomarse unas vacaciones sin sueldo aunque cotizando.
  • Reducción de la jornada laboral.
  • Reducir el número de días de vacaciones. Claro, todo el mundo sabe que aumentando en número de horas trabajadas, aumentas la productividad. Ésto último era puro sarcasmo.
En realidad no dejan de ser meros parches para maquillar las cuentas. No son soluciones.

Alternativas

Se echa en falta un poco de imaginación y de conocimiento del sector. ¿Por qué no hacer?
  • Formación. Se puede impartir y recibir cursos en vivo u online. Personal más preparado sí es igual a mayor productividad.
  • Investigación. Se puede investigar y evaluar nuevas tecnologías, herramientas y metodologías que permitan un verdadero aumento de la productividad.
  • Desarrollo de productos. Para posteriormente intentar su venta. Vender productos no genera un beneficio tan inmediato como vender servicios porque primero hay que desarrollar el producto, pero da dinero y prestigio.
  • Desarrollo de aplicaciones internas. Ya se sabe que en casa de herrero, cuchillo de palo, así que seguro que las aplicaciones internas son más que mejorables. Mejores aplicaciones intenas también mejora la productividad.
  • Incrementar la dotación en los proyectos activos. Ésto supone perder dinero o ganar menos en estos proyectos, pero seguro que incrementaremos la satisfacción del cliente y nuestras opciones para obtener nuevos proyectos con él.
  • Dar soporte a compañeros de otros equipos. Pocas veces eres el primero en resolver un problema, en vez de preguntar a Google o probar por probar, por qué no preguntar a un compañero de otro equipo.
  • ¿Colaborar en proyectos open-source de los que tu empresa se lleva lucrando años? Ja, que me da la risa...

Futuro

La vida son ciclos. Al final la crisis se irá. Algunos habrán caido, otros se habrán mantenido a flote y los más preparados habrán crecido. Pero, ¿habremos aprendido algo?

Hacer accesible un método Java en tiempo de ejecución

7 comentarios
No es habitual. Puede que incluso sea síntoma de un mal diseño OO. Pero en ocasiones nos puede venir muy bien ser capaces de hacer accesible un método en tiempo de ejecución. Por ejemplo, para hacer pruebas de un método privado.

El truco está en usar Reflection. Reflection es un API Java que forma parte del JDK (paquete java.lang.reflect) y que permite acceder y manipular la meta-información de las clases y objetos de tu código Java en tiempo de ejecución. No es algo que se use todos los días (a no ser que programes ides, debuggers o frameworks), pero conviene saber que existe y conocer sus capacidades, por si acaso...

Pero vayamos al grano. El código es muy sencillo. A partir de una instancia, (1) obtenemos su objeto Class, (2) obtenemos el objeto Method que representa el método en cuestión, (3) modificamos su accesibilidad y (4) finalmente lo ejecutamos pasándole los argumentos necesarios y guardamos su salida si tiene. Un ejemplo de un método sayHello con un parámetro String y un return String sería éste:


Method metodo = instancia.getClass().getDeclaredMethod("sayHello", new Class[]{String.class});
metodo.setAccessible(true);
String salida = (String)metodo.invoke(instancia, new Object[]{"argumento1"}));


¿Y si fuera un constructor? Practicamente igual, pero usando esta vez el objeto Constructor y teniendo en cuanta que ahora no tenemos una instancia. Un ejemplo con un constructor sin parámetros sería éste:


Constructor constructor = NombreClase.class.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
NombreClase instancia = (NombreClase)constructor.newInstance();


Y esto es todo. Rápido y fácil. Ahora no lo uséis mal...

10 formas de mejorar tu código

15 comentarios
[ACTUALIZACIÓN 030509: Añadido ejemplo de Antiobjeto.]

El otro día vi la presentación 10 Ways to Improve your Code que Neal Ford hizo en la QCon SF 2008 sobre cómo escribir mejor código y que ha publicado el portal InfoQ.

Es una presentación genial y muy práctica. Enseguida te das cuenta de que estás ante un gran programador que además sabe explicarse muy bien. Así que decidí tomar apuntes de estas 10 formas de escribir mejor código, que aprovecho para plasmar aquí y no olvidarlas.

1 - Métodos compuestos

Divide tus métodos en métodos más pequeños que realicen una única tarea claramente identificada. Así se obtiene un código más intuitivo y fácil de probar formado por métodos pequeños, cohesivos y reutilizables. Además es un código autodocumentado porque los nombres de los métodos se convierten en documentación.

2 - Diseño y Desarrollo orientado a pruebas (TDD)

Piensa en cómo se usará una clase y diseñarás mejor su interfaz y su relación con otras clases.
Una clase bien diseñada es fácil de probar.

3 - Análisis estático

Usa herramientas de análisis de código, como Findbugs, para evitar bugs, no violar buenas prácticas y escribir código más fácil de mantener.

4 - Buenos ciudadanos

En POO los ciudadanos son las clases. Una buena clase es aquella que se relaciona bien con las demás. Un ejemplo de mal ciudadano es el Singleton. Los singletons mezclan responsabilidades (su funcionalidad más asegurar 1 única instancia), son difíciles (sino imposibles) de probar y aumentan brutalmente el acoplamiento entre clases al usarse como las antiguas variables globales. En lugar de un singleton, usa un pojo con constructor privado para la funcionalidad y haz que sea una factoría la responsable de crearlo (usando reflection para saltarse el constructor privado) y asegurarse que sólo haya 1 instancia. Más sobre el peligro de los singletons aquí.

5 - YAGNI: you arent gonna need it

No programes lo que no vas a necesitar. Construye la funcionalidad más simple que necesites en cada momento.
No desarrolles de forma especulativa, pensando en posibles necesidades futuras.
Me encanta esta imagen:


6 - Cuestiona el porqué

No hagas las cosas porque sí. Piensa antes y comprueba.
A menudo, dentro de una empresa o equipo, se toman como leyes invariables algunas recomendaciones o soluciones concretas y circunstanciales. Incluso cuando su creador ha desaparecido. Seguro que todos tenemos algúna historia con esto...
Además a veces lo intuitivo no es cierto. Neal cita un estudio donde se observó que la programación por parejas produce código 15% menos rápido pero con 15% menos de defectos.

7 - SLAP

Usa el mismo nivel de abstracción para las líneas de un método.
Así conseguirás métodos compuestos de forma natural.

8 - Programación poliglota

Domina distintos lenguajes para resolver cada problema con el lenguaje más apropiado.

9 - Cada matiz

Aprende y aprovecha los matices especiales del lenguaje. Pej. en Java con reflection puedes hacer maravillas, como cambiar el modificador de acceso de métodos en tiempo de ejecución o construir objetos de clases definidas dinamicamente.

10 - Antiobjetos

Solemos modelar los objetos a imagen y semejanza del mundo real. Sin embargo, en ocasiones, es útil obviar la realidad para crear soluciones menos complejas. Es lo que se conoce como antiobjetos. Por ejemplo, si pensamos en el famoso juego Pac-Man, es lógico imaginar que son los fantasmas los que calculan su camino hacia pacman en base al tablero, su posición, la de pacman y si se comió la fruta. Pero en realidad es el tablero quien mueve los fantasmas!


Y tú, ¿conoces alguna otra forma?

Historia de una integración (iv): Colaboración

1 comentarios
La complejidad de un proyecto también radica en las personas involucradas, y no sólo en la tecnología. A más personas, mayor complejidad.

Personajes

Esta historia tiene muchos personajes. Además de los usuarios, el cliente, mi equipo y mi jefe, están los distintos equipos de cada módulo SAP con su consultor a la cabeza. Y por supuesto el analista interlocutor del cliente, el llamado Product owner de Scrum, como nexo de todos.

Él es el verdadero héroe de esta historia. Trabajador, comprometido, eficaz, comprensivo, dialogante mediador y afable. Rompe con todos las ideas preconcebidas que se pueden tener del cliente funcionario.

La clave para vencer la complejidad humana está en la colaboración. Parece fácil, al fin y al cabo todos teniamos el mismo objetivo: sacar el proyecto adelante.

Pero no sólo fue difícil, en algunos casos ha sido absolutamente agotador. Las personas mediocres tienden a adoptar una postura defensiva y esquiva, están más preocupados por salvar su culo que por el proyecto. Convierten cualquier asunto en una crisis y no se dan cuenta de que así sólo ponen más de manifiesto su ineptitud.

Afortunadamente también había buenos profesionales con los que ha sido una satisfacción trabajar. En realidad me voy a ahorrar los detalles. Supongo que lo entenderéis.

Comunicación

En cualquier caso la mejor forma de colaborar de forma productiva es cuidando la comunicación y las relaciones interpersonales. No hay por qué llevarse mal sólo por ser de diferentes empresas.
Respecto a la comunicación me gustaría hacer una serie de observaciones/recomendaciones:

  • Usa el medio de comunicación más adecuado. Cara a cara, teléfono o correo electrónico. El correo electrónico es el más flexible pero también es el más frio y propenso a malentendidos, tanto de significado como de actitud.
  • Evita discusiones por correo, usa el teléfono y luego mandas un correo resumen.
  • Comunícate con la persona adecuada directamente. Evita los intermediarios, simplemente ponles en copia o al tanto.
  • Asegúrate 2 veces antes de señalar el fallo de otro.
  • Incluye siempre toda la información relativa que puedas, como mensajes, capturas de pantalla, fecha y hora, etc.
  • Ser amable o pedir disculpas no es pecado.
  • Sé comprensivo. Ponte siempre en el lugar del otro. Piensa que no eres el único que tiene trabajo y que tú también te equivocas.

Problemática de un proyecto de integración

Los problemas técnicos de colaboración en un proyecto de integración se resumen en disponibilidad de entorno y de juegos de datos de pruebas. El entorno de desarrollo/pruebas no siempre está disponible. Y aún con el entorno disponible, no siempre existe el juego de datos requerido.

Para minimizar esto lo mejor es usar Mocks que simulen el entorno y devuelvan los datos requeridos. De esta forma te independizas del entorno hasta el último momento, las pruebas de integración. Es la evolución del llamado hardcodeo.

Pero para que los Mocks sean realmente eficaces es necesario que los interfaces estén clara y explicitamente definidos. No sólo el nombre, tipo y orden de los campos de entrada y salida. También los formatos (fechas, cantidades, etc.). Esta definición debe hacerse en un lenguaje formal y procesable por una máquina para (1) evitar errores de interpretación y (2) automatizar la construcción de clientes y servicios. En el caso de los servicios web SOAP se usa el estándar WSDL.

Finalmente me gustaría destacar la importancia del documento Pruebas de integración. Debe estar actualizado y contener un completo juego de casos de prueba que cubra toda la casuística de entrada y salida. Incluidos los posibles errores. Además estas pruebas deben automatizarse cuanto antes para poder usarse como pruebas de regresión.


En resumen, las personas somos más difíciles que la tecnología. Por lo que colaborar no es sencillo. Pero es la única forma de sacar un proyecto adelante. Haz más fácil el trabajo de los demás y el tuyo será más fácil. No seas mezquino.

Historia de una integración (iii): El desarrollo

2 comentarios
El entorno de desarrollo estaba implantado, la arquitectura estaba definida, el análisis estaba en marcha. Era el momento de iniciar el desarrollo, antes de que el tiempo (y el cliente) se echara encima.

Pero pronto quedó claro que los servicios web SAP iban a tardar más de lo previsto. De hecho, ni siquiera disponiamos de los ficheros WSDL que describen el interfaz, que empezaron a llegar por cuenta gotas un par de meses después. Por supuesto, más de uno tuvo que ser modificado y remodificado. Esta situación nos obligó a ser extremedamente ágiles, cambiando la prioridad de los módulos, reconstruyendo los clientes webservices y modificando el modelo de la aplicación a menudo.

Metodología

No hemos seguido ninguna metodología concreta al pie de la letra. Aunque sí es cierto que nuestra forma de trabajar está ampliamente inspirada en Scrum y las prácticas ágiles que promueve. Sobre Scrum podeis leer más aquí y aquí.

Os dejo algunas notas de la estrategia que utilizamos:

  • Desarrollo incremental con una versión mayor por módulo. Empezando por el módulo de Administración.
  • Los requisitos fueron traducidos en nuestro JIRA a tareas de tipo New Request dentro de la versión correspondiente. Los cambios en los requisitos fueron añadidos como tareas de tipo Improvement. Desgraciadamente cuando la versión estaba cerrada no siempre se añadían como tareas hijas...
  • Simulación de los servicios web SAP mediante Mocks de los clientes webservices. Como ya he dicho los servicios no estaban, pero nosotros necesitabamos ya disponer de datos que pintar en las pantallas! Así que simplemente implementamos los interfaces de los servicios con unas clases tontas que devolvian datos más tontos aún. Mocking manual pero efectivo.
  • Testing de la capa Middleware. Toda la lógica de la aplicación está en nuestras clases service que son llamadas desde las pantallas mediante DWR. Los controladores web (Actions de Struts) practicamente sólo realizan navegación y precarga de campos de formularios. Focalizar el testing en la capa Middleware permitió independizarse totalmente de la capa Presentación y el servidor de aplicaciones, aumentando así la velocidad de desarrollo global.
  • Testing de la capa Acceso a base de datos. Para probar los distintos daos de esta capa usamos un script sql para inicializar la bbdd a un estado conocido junto con los tests transaccionales de Spring que automaticamente hacen rollback evitando la modificación del estado y la fácil configuración entre datasources dependientes e independientes del servidor de aplicaciones. Una más del montón de cosas que te facilita Spring.
  • Integración contínua. Básico en cualquier fase de test seria. Además de actuar como control de calidad de nuestro código. Pero de Hudson ya hablé en su día...
  • Estructura del proyecto. Para atajar la dificultad en un proyecto es básico tener una estructura inteligente y ordenada del mismo. Nuestra estructura de código fuente estaba formada por un gran paquete por módulo, más un paquete comun. Luego dentro de cada módulo, los subpaquetes web, dao, ws, modelo y services.
  • Tags, branches y merges. El cambio de prioridades entre módulos que sufrimos más el desarrollo incremental por módulos podrían habernos costado más de un disgusto serio sin una buena estrategia de control de versiones.

Dificultades

Hasta aquí parece que ha sido un paseo, pero en realidad hemos sufrido lo nuestro también. A parte del tema de la colaboración con el resto de equipos, tuvimos que hacer frente a varias dificultades en:

  • Integración mediante webservices. Se veía venir. Sinceramente e IMHO, los servicios web SAP están, como mucho, diseñados regularmente. Abundan los datos duplicados e innecesarios, no existen namespaces ni schemas comunes y aún nadie me ha podido explicar por qué existe un servicio web por cada operación! Además los WSDLs son generados por alguna herramienta SAP de forma automática que no cumple el WS-I. Lo que nos obligó a usar XMLBeans como OXM (Databinding Java-XML), aunque esto no tiene nada de malo, y también a tener que modificar a mano un par de WSDLs. Y luego está el tema de la infraestructura de red del cliente en producción y sus proxys, pero ésto también lo he contado ya.
  • Pruebas de los servicios web. Descubrimos muchos bugs en los servicios web SAP. Yo diría que sus equipos de desarrollo los probaban poco. Las modificaciones, a veces, ocasionaban nuevos bugs donde antes funcionaban. Pronto quedó claro que no tenían una fase seria de test. Al final tuvimos que programar nosotros las baterias de tests para los servicios web SAP.
  • Gestión de requisitos. Sabiamos que los requisitos iban a cambiar. Pero no tanto. El sistema de gestión de requisitos antes comentado no favorece el control de cambios en los requisitos, es demasiado frágil y requiere demasiado trabajo en mantener la descripción de la tarea inicial y las hijas, además de mezclarse aspectos de descripción de funcionalidad con pura implementación. En el futuro probaremos con una Wiki para los requisitos y el JIRA para las tareas exclusivamente con referencias al Wiki.
  • Formación. Inicialmente, y para mi sorpresa, el nivel de dominio del equipo en Spring e Hibernate era más bajo de lo esperado. Esto nos dió problemas especialmente en mapeos avanzados de Hibernate que provocaban que (1) algunas operaciones fallaran y (2) se realizaran más consultas de las necesarias. También destacar la dificultad, al principio, de trabajar con multiples ficheros de configuración Spring debido al número de módulos y las necesarias configuraciones para mocks y testing.
    Luego la cosa mejoro. Aunque a día de hoy Hibernate sigue sin convencer a más de uno pero con Spring todos han caido rendidos.
  • Proceso de sincronización. Todas las noches debe actualizarse una estructura de 7 tablas con PKs y FKs compuestas con casi 100 mil registros obtenidos mediante cientos de llamadas a 2 servicios web. Es una operación compleja y costosa, con o sin Hibernate. Fue necesario rediseñar los servicios web SAP para operaciones de consultas masivas que minimizaran el número de llamadas y optimizar los mapeos Hibernate así como los algoritmos usados. Al final pasamos de más de 1 hora de ejecución (tiempo inaceptable) a apenas 10 minutos.
  • Testing en la capa de presentación. En su momento no conseguimos hacer funcionar los tests funcionales web basados en Selenium en nuestro servidor de integración contínua, así que esta parte de test se hizo de forma manual. Como la aplicación era una aplicación web RIA bastante grande que tenía que funcionar en IE6, sumado a los cambios de requisitos y modificaciones en los servicios web, os podeis imaginar lo que hemos pasado...

Creo que no me dejo nada. Los temas de colaboración e implantación proximamente...

Tipos de programador

8 comentarios
Me gusta simplificar las cosas. Es algo que no puedo evitar, cúanto más simple es algo, más perfecto me parece. Por eso el otro día cuando leía a Jeff Atwood exponiendo los 8 tipos de programador que según él existen, no pude evitar pensar ¡¿8 tipos para algo tan sencillo?! ¿¿8??

Me vino a la cabeza uno de mis antiguos posts donde hablaba de cómo valorar a los miembros de un equipo. Pero esto es diferente, son tipos de programador. Así que intenté borrar el post de Jeff de mi mente, sobretodo la parte de hacerse famoso... y llegué a la conclusión de que sólo existen 3 tipos de programador:
  • El primer tipo sería el programador que es capaz de aprender por sí mismo.
  • El segundo tipo sería el programador que es capaz de aprender con la ayuda de otros.
  • El tercer tipo es aquel que no es capaz de aprender ni por sí mismo ni por los demás.
Los del primer tipo son superiores, los del segundo brillantes. Los del tercero son inútiles.

Es así de sencillo. Actualmente nuestro mundo vive una evolución imparable y un programador debe vivir en un estado de contínuo aprendizaje que le permita reinventarse a sí mismo en caso necesario.

Por aprendizaje no me refiero a memorizar por memorizar. Aprender implica descubrir y comprender nuevos conceptos para ser capaz de relacionarlos con conocimientos previos de forma coherente. El conocimiento se aumenta con conocimiento.

El primer tipo es un programador superior porque no depende de nadie. Es capaz de buscar, encontrar y aprovechar las fuentes de conocimiento que necesita por sí mismo. Su propio código es una de ellas porque tiene un potente espiritu crítico. Pero sobretodo tiene una vocación y un talento natural. Disfruta realmente programando, resolviendo problemas y experimentando.

El segundo tipo es un programador que tiene la inteligencia necesaria para convertirse en un excelente profesional. Todo dependerá de su actitud, porque aunque es capaz de diferenciar entre lo bien y lo mal hecho, debe esforzarse en aprovechar las oportunidades que se le presenten para aprender. Principalmente, gracias a sus compañeros, pero también un libro, un artículo en internet o el código fuente de un proyecto open-source que utiliza.

El tercer tipo es un programador cuyas acciones no tienen un porqué. No razona porque no aprende, sólo memoriza una parte. Va a lo fácil, a sobrevivir. Necesita constante soporte y supervisión. Hoy hace bien una cosa y mañana la misma la hace mal. Su código es fuente de la mayoría de bugs totales. Daña la moral del equipo. Y a la larga, resta más que suma.

En definitiva -insisto- el programador capaz de aprender por sí mismo es superior, el que es capaz de aprender con la ayuda de otros es brillante y el que no es capaz de aprender ni por sí mismo ni con ayuda de otros es inútil.