La Mentira de la Modularidad
Seguro hemos participado en reuniones donde se ha tirado la popular analogía de los Legos. Legos por acá, legos por allá. Se entiende; la analogía aparece a mano cuando se intenta enfatizar que algo es relativamente autocontenido y que, como tal, puede formar parte de distintos esquemas dependiendo de cómo elija el ocasional arquitecto combinarlos.
Ahora bien, es sólo una analogía, todo bien, calmate Ignacio (?). Pero despanzurrémosla un poco para ver por qué se queda corta y termina siendo sólo un sueño de verano.
Cuando diseñamos sistemas de cierta complejidad, que a la distancia podrían asemejarse a piezas o “legos” encastrando unos con otros para formar un todo, nada es como parece. Históricamente, no hemos sabido resolver el asunto de cómo combinamos el comportamiento de las partes para lograr un comportamiento armónico del conjunto de una forma más o menos modular. Cuando los bloques en cuestión son un cacho de material inanimado, bárbaro. Pero cuando son más que eso, todos los enfoques “modulares” que hemos visto como humanidad se quedan a medio camino al olvidarse de modularizar lo más importante (y lo más jugoso si se quiere): el comportamiento.
Nos prometieron autos voladores, y todavía ni siquiera podemos diseñar “cosos” tecnológicos sin una buena cuota de neurosis colectiva.
Alguien alguna vez dijo que en la variedad está el buen gusto y en la ingeniería de sistemas complejos esto significa la ruina, porque tenemos que lidiar con el dolor de tener que coreografiar “modelos de programador1” y comportamientos variados que encima son función del tiempo: no sólo cambian entre diseño y diseño, sino a veces también cambian en un mismo diseño o arquitectura cuando un proveedor por ejemplo nos obsoleta un componente y de golpe nos ofrece otro que es, a priori, superior pero que se comporta y configura de una forma completamente distinta.
Por ejemplo, si agarramos dos sensores del mismo tipo—por caso, tomemos dos sensores de temperatura—de dos fabricantes diferentes, muy probablemente el comportamiento sea diferente, aunque los dos provean el mismo “servicio” a la causa: informar una mísera temperatura. Pero, para que veamos que nuestra miseria no conoce límites, dos sensores de un mismo fabricante tranquilamente pueden tener comportamientos y modelos de programador diferentes. Por ejemplo, si vamos y compramos en un mismo fabricante dos sensores de temperatura I2C que hasta sean compatibles pin a pin, el hecho de que usen la misma interfaz de datos, el mismo packaging y hasta los mismos condenados pines nos da una ilusión de modularidad que tendemos a festejar prematuramente, sólo para desayunarnos que la estructura interna, los registros, y la funcionalidad entre estos dos sensores son totalmente diferentes. Podremos reusar alguna capa de software hasta cierto punto, seguro, pero en algún momento vamos a tener que escribir una “capa de negocios” distinta para uno y de otro sensor. Bajo la noble premisa de la variedad y el buen gusto, nos ponen en el brete de tener que hacer malabarismos para coordinar configuración y comportamiento para que no se nos caiga la casa de naipes.
Pocas veces se discute modularidad en un sentido lo suficientemente amplio, desde la interfaz física hasta las capas superiores que definen cómo funciona la cosa. Dos objetos pueden ser modulares si los miramos desde cierto ángulo y ser completamente no-modulares vistos desde otro ángulo. Nadie se ha calentado nunca por estandarizar comportamiento y modelos de programador, y eso nos sigue alejando de cualquier panacea “Lego”. Se entiende, la variedad de comportamiento es también una inoxidable herramienta de marketing para atar clientes a un producto al hacerles sentir que cambiar requeriría una nunca atractiva cantidad de trabajo2.
Y esto es independientemente de donde nos paremos en la jerarquía del sistema. Si hablamos de un diminuto sensor sentado en un PCB, un sensor más complejo o un subsistema entero como un satélite en una constelación, el marco de referencia del sistema es irrelevante para la discusión, el problema es el mismo, solo que las dimensiones y complejidades relativas varían. Cuánto más complejo el componente, más intrincado es el comportamiento por ende más laborioso es el acoplamiento de ese comportamiento en el sistema general.
Esto implica que, para cualquier sistema compuesto por una colección de elementos discretos cuyos comportamientos son variados, hay que estar todo el tiempo acomodándose y coreografiando esta red de funcionalidades para que convivan como si fuesen una gran familia disfuncional. Esto incluye leer hojas de datos, ICDs, manuales de referencia, tener reuniones, mandar emails, tomar cursos, leer erratas. Es un proceso tedioso que hemos internalizado hasta el punto que nos parece normal.
Alguien dirá: es imposible predecir qué funcionalidad va a tener un sistema. Seguro, pero a lo que voy acá es que tal vez esa funcionalidad global puede desgranarse en un conjunto de sub-funcionalidades que podrían ser más modularizadas de lo que actualmente son. Para ser menos tangencial: en algún futuro no aceptaremos más que dos sensores, para seguir con el ejemplo que usé más arriba, se comporten de manera tan distinta cuando ambos hacen exactamente lo mismo.
Lejos parece quedar ese arco iris donde las analogías como “plug & play” y “Lego” viven junto al duende que custodia la olla llena de oro. Los sistemas complejos fueron, son y—a menos que una suerte de inteligencia artificial mágica nos de una sorpresa en el futuro— serán artesanal y dolorosamente concebidos a partir de miles de horas-silla leyendo innumerables páginas de documentos para entender cómo funcionan y así poder escribir millones de líneas de código que habremos de reescribir ni bien la arquitectura cambie.
Llegará acaso el día donde diseñar un sistema se reduzca a combinar elementos donde no sólo las interfaces físicas, los conectores y los factores de forma sean estándar, sino también los comportamientos y los “modelos de programador”, es decir, la forma en que configuramos las cosas.
Básicamente, el "modelo del programador" es el conjunto todos los elementos (registros, archivos, perillas, etc) que un dispositivo expone a un usuario para poder configurarlo en pos de lograr un cierto comportamiento dado.
Tenemos que agradecer que los vendedores de cocinas, heladeras y máquinas de café se dieron cuenta hace tiempo que les conviene mantener más o menos la forma de usar estos artefactos y que la diferenciación tiene que venir por otro lado y no por el lado de torturar al usuario con nuevas curvas de aprendizaje.