Tinselcity

¡Ay, ay, ay, Agile!

Ay, ay, ay, Agile es una pequeña colección de escenas cotidianas, fragmentos de retratos de programadores. Todo es real. Ojalá tuviera imaginación para imaginar estas cosas y no las viviera en la realidad. No pretenden ser humorísticas. Tampoco pretendo humillar a nadie, ni protestar o quejarme de nada. Son simples retratos.


George recibe una tarea que consiste en añadir uno de esos tutoriales/demostraciones embebidos en la aplicación para resaltar e informar de una nueva funcionalidad. En la tarea no viene ninguna explicación sobre qué debe mostrar o decir el tutorial. Mirando en la tarea original de la funcionalidad no viene tampoco ninguna explicación sobre cómo se usa la funcionalidad o qué debería ser lo más representativo o destacable.

George pregunta a la persona que implementó la funcionalidad pero esta, aunque le explica de qué trata, tampoco le sabe decir qué debería poner en el tutorial. George se lamenta en voz alta diciendo que podrían estar un poco más definidas las tareas.

- Agile significa menos documentación - le recrimina Karen desde la mesa de enfrente.

Sorprendido, George insiste en que le parece normal saber por lo menos qué es lo que tiene que hacer.

- Agile significa documentación menos detallada y más abierta, porque la documentación no vale nada. Lo que tienes que hacer es trabajar en equipo - le corrige Karen.


Una mañana me dice Tom:

- Oye. Mira, yo intento ir mejorando las cosas que veo en los JSPs cuando tengo que modificar algo pero ya es la cuarta vez que Karen me echa la bronca por limpiar algo. Lo siento, pero no voy a seguir haciéndolo porque no quiero que me eche la bronca otra vez.

Le he dicho que no se preocupe, que no pasa nada; que no lo sienta, que ya lo siento yo.


- Bueno, esto son las promesas de jQuery. No sé si las conoces. Es para manejar la asincronía - el Arquitecto siempre asume que nadie sabe nada


- Edward, tienes un desajuste de 14 minutos en las imputaciones del mes de Septiembre. Por favor arréglalo cuanto antes. - el jefe, que algunas semanas antes aseguraba que aquí no se venía a hacer horas y que los tiempos eran simplemente informativos, para vosotros mismos.


En la reunión “explicativa” de antes de empezar el sprint donde nos cuentan lo de Agile:

- Una vez que empecemos el sprint, no se quita ninguna tarea del sprint. Repito, no se quita ninguna tarea del sprint. - dice enfáticamente Karen.

Semana y media después, en medio del sprint:

- Frank, desasígnate esta tarea porque la sacamos del sprint. - oigo a Karen decir.

- ¿Qué? Pero ¿así sin más?

- Sí. Y si ya has imputado algún tiempo a esa tarea, bórralo. - dice Karen dando la conversación por terminada.


En una reunión, dos personas que han sido contratadas explícitamente para mejorar la calidad del proyecto describen el horror que han visto en el código. Es todo tan desorganizado que no se siguen ni siquiera unos mínimos básicos de orden y limpieza, así que están proponiendo implantar un proceso de “buenas prácticas” con, al menos, unas normas simples y fundamentales para dar algo de consistencia y coherencia al código.

El Arquitecto es el responsable directo del proyecto desde su inicio. Su aportación en la reunión es:

- Yo es que como soy Fullstack no tengo problemas con las buenas prácticas. Estas cosas a mi me salen de forma natural ya. Pero es cierto que, claro, el resto del equipo igual le cuesta más.


En un cierto formulario de una sección de la aplicación, se introducen hasta 5 direcciones de correo electrónico de contacto. Estas direcciones se validan en servidor originalmente. Se envía el formulario, se valida apoyándose en la infraestructura de validación de Struts y, si hay errores, se redirige de vuelta al mismo formulario pero con un mensaje de error.

Cuando se desarrolla esto, XHR (“AJAX”) ya existe y se usa en todos sitios desde hace años. Validaciones simples hechas en cliente llevan existiendo muchísimo más tiempo, claro. Aún así, tardan 3 años en decidir que tener que hacer la petición completa, ir y volver del servidor y repintar la página entera por un error en una dirección de correo electrónico es una experiencia mejorable. Se les ocurre, ¿por qué no validar en cliente?

El Arquitecto construye una expresión regular en JavaScript y una función validarEmail. Durante los siguientes 6 años, la aplicación funciona de esta forma:

  1. Cuando el usuario ha introducido las direcciones de correo y pulsa el botón de aceptar, las direcciones de correo pasan por la función validateEmail.
  2. Si una dirección no es válida, se escribe en un campo oculto el número del campo no válido.
  3. El formulario se envía a servidor de todos modos.
  4. En servidor no se validan las direcciones de ninguna forma. Lo que se hace es mirar si el campo oculto está relleno.
  5. Si lo está, entonces se redirige de nuevo al formulario igual que se hacía antes, marcando la dirección errónea gracias al campo oculto.
  6. Sólo hay un campo oculto, así que si hay varias direcciones erróneas, solo se marca como errónea la última. Si el usuario sólo corrige esa, que es lo más natural, se vuelve a repetir todo el proceso, con petición y recarga de toda la página cada vez.
  7. Además, la expresión regular que tienen no está demasiado bien y deja pasar por buenas muchas direcciones claramente erróneas. Después de todo el proceso, aún es posible introducir direcciones malas en el formulario y que se acepten porque en servidor ya no se validan.

(Nota: Sí, al encontrar esto lo he arreglado y lo he dejado bien xD)


En una reunión sobre el funcionamiento del “equipo”, estamos la mitad del equipo. Estamos “los desarrolladores” pero no están “los analistas”. Michael, el jefe, nos aclara con mucha insistencia:

- No son “ellos” y “nosotros”. Somos un equipo. ¡Estamos todos trabajando juntos! Os tenéis que meter esto en la cabeza de una vez. Tenéis que cambiar vuestra forma de pensar.

Exactamente dos minutos después, Michael dice:

- Ya hablaré con ellos cuando sea oportuno, ahora de lo que se trata es de cómo trabajáis vosotros.

Unas horas después, según salgo hacia casa uno de los analistas me ve algo decaído y me dice:

- Mira, tienes que entender un poco la dinámica que hay aquí en este proyecto. Por un lado estamos nosotros, que estamos aquí desde el principio y tenemos una relación más o menos cordial. Luego está el otro grupo, que vinieron hace unos 8 años.


Hay un componente que permite ejecutar diferentes comandos sobre lo que haya seleccionado en un listado. Se configura pasando los varios comandos como funciones de JavaScript y el controlador este proporciona también una serie de opciones adicionales sobre su funcionamiento. En particular, podemos proporcionar una función de callback que puede interceptar el proceso antes de que se ejecute la operación y realizar tareas adicionales (por ejemplo, rellenar un campo oculto con cierto dato, o comprobar que la selección es adecuada para esa operación) y puede decidir que la operación se cancele.

Al principio del código del componente está la explicación. Hay un:

var OPC_CANCEL = true;

Y la explicación dice:

/**
 * ...
 * - preExecOpCB (opcional): callback que se ejecuta  antes de comenzar la ejecución de la operación.
 *   Este callback puede devolver return OPC_CANCEL (true) para cancelar la ejecución del execOp
 * ...
 */ 

En el código, hay alrededor de cien ocasiones donde se usa esto. En todas ellas aparece:

    return OPC_CANCEL (true);

Y obviamente en todas ellas da un error porque OPC_CANCEL no es una función. Esto ha estado así siempre, durante 10 años. Nadie se ha dado cuenta nunca. Es más, hay un punto concreto en el código donde alguien, muy optimista hace:

    return OPC_CANCEL (false);

Que tampoco funciona, claro.

Dos meses después: Karen ve el cambio y le pregunta al Arquitecto si sabe de qué va esto. El Arquitecto no sabe. Dice “¿Había algún error quizá? No sé.” El Arquitecto nunca se preocupa de si su equipo hace las cosas como se deben hacer. Tampoco de explicarles nada.


- No, no, no. Te estás complicando. No hay ningún servicio para modificar. Solo hay dos servicios. El servicio de crear registro nuevo y el de anular que antes borraba pero ahora anula. Entonces si cambia el estado a anulado, tú llamas a anular. Si cambia a reactivado, tú llamas a crear uno nuevo y el de crear uno nuevo si encuentra uno existente anulado, lo reactiva. Pero si el usuario no tiene permiso para ver los anulados, cuando intenta crear uno nuevo, el de insertar lo detecta también pero no lo reactiva, le da un error. Y si mueves de fecha un registro entonces primero llamas a anular el viejo y luego a crear uno nuevo en la otra fecha. Y si hay uno anulado en esa fecha que no puedes ver, entonces te da un error, pero el otro se deja anulado también. No te compliques.

Minutos después, por e-mail, dice:

- Hay que crear el servicio de modificar fecha, que llama a baja y a insertar.

Los registros, por cierto, no tienen clave. Se buscan por fecha y se confía en que no puede haber dos registros en un mismo día. Obviamente existen registros en el mismo día.

Al día siguiente descubro que el servicio de borrar que antes borraba pero ahora anula ni borra ni anula. No se usa desde hace años y hay que hacerlo nuevo. Sin complicación, claro.


- Eso lo dirás tú. Porque yo he estado leyendo muchísimo sobre Agile últimamente y Agile dice que no documentación.


En un comentario de Hackerl News, un tipo lo llama “los Cinco Valores y Doce Principios”, así con mayúsculas. Y luego dice algo como “Esto lo dice el Primer Valor y el Séptimo Principio”. Yo no le contesto pero mi cerebro piensa que si Agile es tu religión, espero que como mínimo me hables con lenguaje bíblico con citas como “Y se acercó Martin Fowler a la serpiente y le dijo: te arrastrarás sobre tu vientre porque no te auto-organizaste bien.” o si no paso de hacerte caso.


Voy a ver a la analista…

Una pregunta muy concreta. El tema es que es estoy haciendo ConfirmarBajaCosa…
Eso ya está hecho.

Ya bueno, pero hay que rehacerlo con el cambio este.

Sí, pero ya existe. No es hacerlo; solo es modificarlo. Ya tienes el esqueleto y todo, solo es cambiar un poco.

Bueno, cambian los parámetros de entrada, cambian las validaciones, cambia que antes era un delete y ahora es un update, cambia la salida porque antes no se devolvía nada y ahora sí.

Pero ya existe, solo es una modificación.

Eh… vale… En fin, la pregunta es que cuando el estado…

Sí, eso está en la documentación.

Sí, sí, pero a eso iba. Es que lo que pone en la documentación…

Sí, es muy sencillo. Si eres gestor, se pone a 2 (propuesta_anulada) y si no a 4 (aceptado_anulado).

Eso. Pero es por confirmar, porque…

Sí, sí, pues así es como tiene que ser.

…pero estaba pensando en el caso en que eres gestor pero está en estado 3 (aceptado)…

Sí, sí, si eres gestor se pone a 2.

Lo digo porque si está en estado 3 (aceptado) pasaría a 2 (propuesta_anulada) y parecería más lógico que fuera 4 (aceptado_anulado).

… Ah, sí, en ese caso sí. Claro. ¿No está puesto así en la documentación?

No, está lo que decías antes.

Bueno, pues ahora lo corrijo, pero vamos sí, es evidente. De 1 pasa a 2 y de 3 pasa a 4.

He ido guardando algunas preguntas para Gunther, el experto en BBDD, que contrataron para ver si ponía orden en el infierno de PL/SQL. después de un par de dudas le pregunto por una práctica muy habitual en el proyecto.

- Esto, ¿hasta que punto es normal?

- Mira, yo no lo he visto nunca. Bueno, quizá alguna vez algo parecido pero así nunca.

Hay unas tablas generales. Son tablas que contienen generalmente configuraciones o valores casi-constantes. Por ejemplo provincias o tipos de comunicación con un servicio externo. Que son casi constantes porque aunque parezca que algunos pueden cambiar no han cambiado en 20 años. Hay 3 tipos de comunicación. Ya está es lo que hay. Estas tablas no tienen nombre, se llaman cosas como M123 o A77 o D154.

Ahora resulta que tienes en otra tabla una columna tipo de comunicación, algo como tipo_comunicacion_el. Y entonces es cuando aparece otra columna: tipo_comunicacion_tb a su lado. Esta columna contiene el nombre de la tabla en la que se encuentra tipo_comunicacion_el. En algunos casos esto significa que en un proceso hacen una llamada en plan “busco una cierta fila, cojo el nombre de la tabla y con eso voy a buscar en la tabla general algún dato extra sobre, en este caso, el tipo de comunicación”. Esto, en realidad, no se hace prácticamente nunca. Porque el contenido de esa columna es fijo. Es decir, tipo_comunicacion_tb es M144 para todos los registros de la tabla original. No es que pueda cambiar de unas filas a otras. Vamos, que en una tabla con 1000000 de filas, se guarda D123 un millón de veces.

¿El resultado? M144 aparece escrito a fuego en unas cuantas decenas de sitios. Nunca se lee realmente el valor de las tablas, se escribe en el código directamente. Cada vez que se escribe un registro nuevo, se acompaña con M144, a fuego, que unas veces viene en el proceso almacenado y otras viene desde la llamada externa desde Java, y así M144 también aparece a fuego unas cuantas decenas de veces más en el código Java.

No todo son desventajas1). Esta generalidad de las “tablas generales” ha permitido que se escriban ciertas utilidades de forma genérica. Esto significa exactamente esto: Que en cliente, en JavaScript pueda haber también algunos miles de códigos como M144 a fuego.

Lo peor de todo es cómo lo tienen tan asumido que se saben muchos códigos de memoria. “¿Familia de artículos? Mmm… La tabla D104.”


La parte de Java está muy bien. La parte de front es un caos. Y la parte de los PLs también. Pero la parte de Java está bastante bien porque es lo que más he hecho yo. El Arquitecto

A la vez…

La parte de front llega hasta los PLs.

Es decir, que la parte que está muy bien… ¿es parte del caos? ¿O qué?

Mientras tanto en otro tiempo y lugar…

Vamos a hacernos unas utilidades para pasar de Array a List y viceversa porque Collections.toArray y Array.asList solo existen desde hace 10 años y no los conocemos. Y además, esos podrían servir con cualquier tipo de contenido pero nosotros vamos a hacer uno que solo nos sirva para Strings porque todo lo que manejamos son Strings2) porque todo lo sacamos así de base de datos. Eso sí, ya que lo hacemos nosotros, nos vamos a asegurar que si el array o lista original están vacíos no devolvemos una lista o array vacíos sino que devolvemos null, porque así evitamos errores, ¿no?

La parte de Java está muy bien.


Cada vez que dicen que una tarea es “de Arquitectura” lo que significa es que es algo que no saben hacer. Nada más.

Hoy:

Te voy a pasar una tarea de Arquitectura, de la parte de front, claro.

La tarea: escribir un PL en el que se devuelva un array. Los PLs también son front.


El Arquitecto nos convoca a una reunión a las 11:00. Y así, a las 11:20, en una sala, nos reunimos para una presentación de “lo que será la nueva Arquitectura que hemos pensado para próximas aplicaciones a desarrollar alrededor del proyecto principal”. Quieren que sea común para todo lo que hagan en el futuro. Yo estoy en la reunión porque quieren mi opinión, dice. Hay un par de desarrolladores más que, aparentemente, ya saben todo esto.

Ojo, tenemos una pizarra…

Dibuja una caja en la que escribe “Rest” y al lado “API”. Dentro de la caja, bajo “Rest”, garabatea algo que se parece a “Spring Boot”.

Esta parte no me preocupa. Ya está decidido y está bien.

Dibuja otra caja. Encima escribe “Front”, dentro “HTML5, JS, CSS”. Al lado IONIC.

Básicamente lo que hemos pensado para la parte de front es usar HTML5 con Ionic para todo.

Los siguientes 30 minutos pasan contando pequeñas anécdotas sobre aplicaciones con las que han tenido alguna relación en el pasado, pero que en principio no tienen por qué ser iguales a las que se desarrollen en el futuro.

La pregunta para mi es si estoy de acuerdo o no. O más bien que esté de acuerdo, que valide su decisión. Pregunto si sabemos qué tipo de requisitos van a tener esas futuras potenciales aplicaciones. No está claro, algo general, “algo normal,” - dicen - “; si hay algún requisito muy demandante o especial, seguramente esta arquitectura común a todo, no aplique pero serían casos muy puntuales”.

Me vuelve a explicar que quiere saber si estoy de acuerdo o si tengo alguna pega. O más bien que estoy de acuerdo y no tengo pegas. En mi cabeza estoy contestando que me trae sin cuidado si eligen Ionic o Bubonic o lo que quiera. De mi boca sale algo más comedido.

Hombre, así sin saber qué tipo de requisitos, qué planes hay, cómo son esas supuestas aplicaciones futuras, es un poco difícil y no creo que haya mucha diferencia entre elegir Ionic o elegir otra cosa.

Intento explicar que lo que hay que saber es qué es lo que van a querer desarrollar con eso, no qué librería elijan, pero noto que desde ese “así sin saber…” de antes, ya no me escucha, solo intenta interrumpirme para hablar él.

Dejo que me interrumpa y me explica de nuevo que serán “cosas normales” durante unos cuántos minutos. Y vuelve a explicarme que la reunión “es un poco para que dé mi opinión y diga qué me parece”. Es decir, que quiere que diga su opinión y diga que me parece bien.

Finalmente digo que me parece bien Ionic pero que no creo que suponga ninguna diferencia elegir eso o elegir otra cosa. Pero que sí, que me parece bien.

El Arquitecto da por terminada la reunión con éxito, anunciando “próximos pasos” que por algún motivo incluyen instalarme a mi VSCode. Lo dejo pasar teniendo claro que eso no ocurrirá.

Según vuelvo hacia mi mesa, oigo que uno de los desarrolladores pregunta al Arquitecto a qué tarea imputa el tiempo de la reunión.


Hoy es día de visita. Vienen directores, responsables de áreas, coroneles, mandamases.

Hoy se disimula. Hoy no hay música de chiringuito de fondo todo el día taladrando y acompañada del tipo que se cree que sabe silbar. Hoy el desayuno se adelanta un poco y se acorta a su duración oficial para que no quede esto vacío cuando vengan las visitas. Han desarrugado la persianilla de la ventana que siempre está arrugada; sigue hecha una pena pero un poco menos arrugada. Los caballetes con papel que nadie usa pero decoran las reuniones, hoy no están en un rincón sino listos y preparados para que nadie los use de todos modos pero parezca que sí, que habitualmente se usan.

Hoy es todo mentira. Hoy todo es teatro.


Me habían avisado y no quise escuchar. Karen es “vengativa y rencorosa”, me dijeron. Lo ignoré porque la mayoría de comentarios y cotilleos así es mejor ignorarlos.

Hace unos días hice un cambio masivo. En realidad, comparado con otros que he hecho, este era relativamente pequeño: 25 páginas. Lamentablemente rompí una sin darme cuenta. Así que ayer Karen, alterada me amenazaba diciendo que no podía hacer esos cambios masivos, que esto iba a tener que cambiar.

Arreglé el problema en unos minutos, pero eso es irrelevante. Lo relevante es que hago estos cambios porque son necesarios, porque me contrataron para hacer estos cambios, porque viven entre la basura y, a veces, que haya pequeños fallos momentáneos (ni siquiera en producción, en los entornos de pruebas, que están para eso) es el precio que hay que pagar por sacar la basura. La mirada de Karen me decía que no estaba de acuerdo. Karen nunca está de acuerdo con nada, de todos modos.

Hoy, Karen me dice “está cerrado trunk”, y “te voy a pasar algunos errores”. Y me empieza a pasar cualquier error que se le ocurre. Resuelvo unos cuantos pero está cerrado trunk así que no los puedo subir. Se lo comento a Karen y me dice que está cerrado trunk (que sí, que ya) y que vaya resolviendo y mañana los revisaremos y los subiremos.

Insiste en que está cerrado trunk, pero al rato me entero -ninguna sorpresa, la verdad- que no es así. Que está cerrado para mi. Que otros pueden subir cosas pero yo no.

Le explico a la gente que sus errores críticos que no les dejan trabajar, están en espera. No les doy más detalle, pero es en espera de que alguien se eche la siesta que necesita.

Al día siguiente, poco antes de que se vaya a hacer el despliegue en los entornos de pruebas, Karen se va a desayunar durante media hora. Yo sigo “castigado”; los arreglos de los errores críticos no podrán subir hasta dentro de unos días.

Mientras yo estoy bloqueado, el Arquitecto sube un cambio que rompe todas las páginas. Le quita importancia bromeando:

Bueno, culpa mía no. Culpa de Steve por haber hecho el despliegue. Ja, ja.

La mejor estrategia frente a los juegos de poder y control es no jugar. Al final se juegan ellos solos.


Me han contratado con la idea de tener por fin a alguien que sepa bien de JavaScript.

El Arquitecto está explicando un poco malamente a un analista que mejor setTimeout que setInterval “porque es mejor con eventos asíncronos”. Se lo explica para que él a su vez se lo explique a la desarrolladora que siempre me recuerda que ella no es desarrolladora, que le dieron un cursillo y le soltaron una aplicación hecha con Dojo.

Aparentemente tienen un problema por el que, a veces, se bloquea el navegador al intentar abrir una ventana en la que se carga un visor de mapas. El Arquitecto habla con gran seguridad:

Puede que sean los setInterval… Bueno, es que hay errores que nunca se sabe…

Evidentemente, a pesar de mis ofrecimientos, a nadie se le va a ocurrir pedirme ayuda a mí. Y, la verdad, me siento afortunado con esa situación. Es una función que escribió ayer un desarrollador que ha declarado abiertamente que no quiere saber nada de lo que pueda decirle y que está llena (la función) de estupideces con this innecesarios.

El Arquitecto ahora está hablando de lo mucho que le gustaría probar a hacer esto usando postMessage porque, dice, “nos ahorramos todos los problemas de comunicación entre dominios”. El visor que se carga está en el mismo dominio, pero eso es irrelevante. Al Arquitecto le gustaría probarlo, aunque no tenga nada que ver con el problema actual.


El GlassFish ese me suena que es un servidor nuevo de Oracle. El Arquitecto, Noviembre 2019


Tenemos una convivencia en la oficina con unas reglas no escritas normales de no matarnos y esas cosas.

Ahora mismo estoy escuchando tres músicas diferentes porque hay a quien le gusta escuchar la música con altavoces. También hay a quien le gusta silbar aunque no sepa hacerlo y a quien le gusta canturrear como si fuera un perro abandonado llorando.

A todos ellos les gusta hacer estas cosas con un volumen suficiente como para que oiga toda esta cacofonía de músicas y lamentos aunque me ponga los auriculares.

Por unos instantes pienso que esas reglas de no asesinar quizá se podrían revisar.


Dos semanas después, sigo castigado sin acceso al SVN. Y no es solo a trunk; tampoco puedo subir nada a la rama de desarrollo.

Como demostración de fuerza sería bastante impresionante3), si no fuera porque me da igual.


Este de aquí es un find. Este de aquí es una búsqueda. Este de aquí es este de aquí. El Arquitecto, explicándose meridianamente.


Están haciendo un proyecto que debería ser ejemplo de cómo hacer las cosas.

“El campo fechaFin ¿es obligatorio?” - pregunta Geoffrey.
“No lo sé. No sé cómo lo he puesto. Haz un test unitario pasándolo a null y mira a ver qué pasa” - dice el Arquitecto.

Me hace recordar que así es como hacen los “tests unitarios”4) cuando los hacen. Escriben código, y al día siguiente escriben tests para ver qué es lo que hicieron ayer.

Los tests están desactivados en el build, obviamente. Hay unos 20 test. Tardan unos 15 minutos.

Es un proyecto que debería servir como ejemplo. A nadie se le había siquiera ocurrido ponerle un readme, unas instrucciones básicas de instalación, una mínima guía de desarrollo.


Long idCode = null;
idCode = idCode.parseLong("612");

La parte de Java, que “está bastante bien”.


Están haciendo pruebas de carga…

“Está el WebLogic a tope de memoria” - dice Marcus, el departamento de sistemas. “Está con las pruebas de la subida de ficheros.”
“Bueno, es normal. Eso lo optimicé para máximo consumo de memoria” - responde el Arquitecto. “Hace 5 pasadas al XML por cada petición…”

“Pero está a tope con solo 5 subidas simultáneas. Y si se ponen a subir a la vez cien usuarios…?”

“Nah, eso no va a pasar. Además blablabla así que en todo caso será un problema de red.” - No dice literalmente blablabla, pero sus palabras suenan muy parecido, aunque mucho más largo.
1)
ahemm
2)
“No como en JavaScript que no tiene tipos” – El Arquitecto
3)
ooooh, aaaah, qué poderío
4)
entre muchas comillas