A la nostra professió, cal sempre caminar amb els ulls oberts i les orelles parades. Fem servir programari per resoldre problemes i, per tal que la nostra trajectòria professional no sigui una repetició constant de la mateixa experiència, sinó una acumulació de coneixement genuí fruit d’afrontar nous reptes, ens cal:
- Experiència, ja sigui la pròpia viscuda o l’aliena llegida, per anticipar les friccions que puguem trobar basant-nos en casos similars.
- Conèixer l’estat de l’art, les tècniques i les tradicions amb què aquests desafiaments s’han enfrontat.
La literatura tècnica és una magnífica font d’aquestes experiències i tècniques. Però això no ve sense una tensió fonamental: el nostre cervell i temps són limitats per absorbir llibres arbitràriament, i al mateix temps, no sabem del cert quan la lectura passada d’un paràgraf o capítol ens traurà l’entrellat d’un desafiament futur. He tingut la sort de trobar plaer en la lectura d’aquest tipus de llibres i avui vull compartir com una lectura, fruit d’una ressenya trobada a Internet, em va permetre afrontar un problema d’alta complexitat a la feina.
Un problema senzill… superficialment
Imaginem un agent de viatges que modifica un formulari de confirmació de reserva, surt de la pantalla i, quan hi torna, espera trobar els seus canvis intactes. Amb variacions més o menys previsibles, és un comportament prou generalitzat i que els usuaris esperen. En el cas que ens ocupa, però, entre l’anada i la tornada poden passar moltes coses:
- El sistema accepta sessions interactives, amb interfície de terminal, que col·lisionen amb l’estat del formulari,
- Les dades també poden modificar-se mitjançant APIs HTTP,
- Abans de sol·licitar o confirmar una reserva, cal reconciliar el context de la transacció amb la intenció de l’usuari expressada al formulari. Una reconciliació que és sotmesa a regles de negoci.
En descriure aquest problema a les parts no tècniques, al meu cap ressonava la idea que, en el fons, aquest és un problema de sincronització molt similar al que enfronten els jocs multijugador. Aquesta analogia il·lustrava bé com múltiples actors poden interactuar amb les mateixes dades alhora, una qüestió que inspira tota l’onada de motors de sincronització que veiem desenvolupar-se avui dia.
El pes de la tradició, o per què el nostre era un cas especial
La indústria del viatge viu en sistemes desenvolupats fa dècades i anteriors a marcs teòrics avui omnipresents com els models relacionals. Tenim estructures jeràrquiques no normalitzades, sense identificadors reals per entitat, i on les posicions dins llistes són significatives i inalterables.
Aquestes limitacions tècniques, que d’entrada semblen un gran obstacle, són també el nostre avantatge. Gràcies a un profund coneixement del domini i dels condicionants històrics d’aquests sistemes, podem crear identificadors sintètics febles. Aquests identificadors, tot i no ser claus primàries “reals” en el sentit modern, ens permeten rastrejar i diferenciar entitats de manera eficient dins d’aquest context tan particular.
Aquests identificadors febles deriven de fets específics del domini, com ara:
- La necessitat que els “Passenger Type Codes” (PTC) coincideixin amb l’oferta, cosa que implica generar o eliminar passatgers seguint regles predeterminades.
- Que dos passatgers no poden tenir el mateix parell Cognoms/Nom, ni aquest superar un determinat nombre de caràcters.
- Que alguns tipus de correu electrònic, telèfons i adreces són sempre únics.
A partir d’aquests fets, vam derivar:
- Funcions hash per a cada entitat, que ens van permetre identificar-les de manera única a través de les transaccions.
- Unes regles per detectar quan una reconciliació no era possible, indicant casos d’irresolubilitat.
- El llistat de casos on hi havia ambigüitat.
Aquestes derivacions van representar una fita clau en l’èxit del desenvolupament. La funció hash va ser fonamental, ja que ens va permetre emparellar dades transitòries amb les del sistema, assegurant una recuperació i assignació correcta d’entitats i relacions. Les regles d’irresolubilitat ens van permetre anticipar possibles problemes en el moment de confirmar la transacció als sistemes legacy i, el que és més important, oferir una interfície d’usuari (UI) adequada que habilitava l’agent per prendre la decisió correcta. Finalment, explicitar els punts d’ambigüitat va ser crucial per a la presa de decisions, sent negociats amb experts del domini i validats mitjançant tests d’acceptació.
Cercant la solució: entitats, punts de control i operacions
Modelant el problema, vaig recuperar la memòria d’un llibre que havia llegit no fa gaire temps:
Kleppmann, M. (2021) Designing Data-Intensive Applications: the big ideas behind reliable, scalable, and maintainable systems. 7a edició. Estats Units: O’Reilly Media.
La part segona del llibre afronta extensament qüestions d’aquesta mena. Allà hi vaig trobar la inspiració per oferir als nostres usuaris una experiència que superés el pobre last-write-wins (l’última escriptura mana) i recuperés la major part possible de l’estat anterior del formulari mantenint la intenció, l’ordre, i la compatibilitat amb les restriccions que imposa el sistema de reserves per poder executar una transacció.
Per assolir una major interactivitat en el futur, el nostre plantejament es basa en les següents definicions clau:
- Una entitat és un objecte dins del domini que té valor per si mateix.
- Una relació és una assignació jeràrquica. Per exemple, un viatger és pare d’un document.
- Una operació és la descripció d’un canvi a una entitat, que pot ser una creació, una edició o una eliminació.
Amb aquests elements, podem construir el concepte d’snapshot o captura:
- Un snapshot és un conjunt d’entitats i relacions. Tot snapshot té una única entitat base que és la reserva mateixa.
I una transacció:
- Una transacció és la terna d’un identificador i de dos snapshots. Els snapshots capturen el concepte de temps, de manera que l’ordre de la terna és significatiu.
Observem que són equivalents, de cara a descriure una transacció assumint que les operacions són reversibles:
- Dos snapshots, inicial i final.
- L’snapshot inicial, i una sèrie d’operacions que aplicades resulten en l’snapshot final.
- L’snapshot final, i una sèrie d’operacions que invertides resulten en l’snapshot inicial.
Aquesta equivalència és fonamental per a la reconciliació de l’estat i la gestió de conflictes.
Aplicació de la solució: Snapshots i Transaccions
En el nostre cas particular, i amb l’objectiu futur d’una reconciliació a temps real (tot i que no en aquesta primera fase), la nostra estratègia de modelatge es basa en tres captures d’estat o snapshots:
- Snapshot A: Quan l’agent accedeix a la vista de confirmació dins d’una sessió, capturem l’estat actual del sistema. Aquest és el punt de partida.
- Snapshot B: En el moment en què l’agent abandona la vista de confirmació, capturem l’estat del formulari. Aquest snapshot representa els canvis que l’agent ha fet directament.
- Snapshot C: Quan l’agent retorna a la vista de confirmació, capturem de nou l’estat actual del sistema. Aquest és l’estat final amb què hem de reconciliar els canvis de l’usuari.
Amb aquestes tres captures, definim dues transaccions clau:
- La transacció externa o de sistema: Captura la transició de l’estat inicial del sistema (Snapshot A) a l’estat final del sistema (Snapshot C). Aquesta transacció representa tots els canvis que han pogut ocórrer fora del control directe de la interfície del formulari, ja sigui per mitjà d’APIs, altres sessions interactives (com la TUI), o modificacions d’altres usuaris o sistemes.
- La transacció interna o transitòria: Captura la transició de l’estat inicial del sistema (Snapshot A) als canvis realitzats per l’agent al formulari (Snapshot B). Aquesta transacció representa la intenció de l’usuari.
Ambdues transaccions les formalitzem a partir de l’Snapshot inicial A, afegint-hi les corresponents operacions que les transformen en l’estat final (C per a l’externa, B per a la interna). Ara, ja podem anticipar que el nostre motor de sincronització es dedicarà precisament a reconciliar aquestes dues transaccions, determinant com aplicar els canvis de l’agent (B) sobre el nou estat del sistema (C), tenint en compte d’on veníem (A).
Aplicant i reconciliant operacions
Els desafiaments en la implementació es centraven en com aplicar i transformar les operacions. L’escenari del formulari és anàleg a reconciliar dues transaccions que han estat aïllades durant cert temps, com si s’hagués perdut la connexió. L’índex natural no ens serveix perquè realment no captura una sessió en viu, amb rellotges compartits o d’altres primitives de sincronització. L’alternativa va ser fer servir una cua de prioritat amb una funció d’scoring o puntuació que considerava, per a cada operació:
- El tipus d’entita (ex. passatger, o reserva).
- L’origen de l’operació (ex. formulari o API).
- La intencionalitat (ex. editar o eliminar).
Acompanyant aquesta cua de prioritat, hi ha un registre de totes les operacions aplicades fins el moment. Aquest historial és absolutament indispensable per garantir que es conserva la intenció de les operacions i poder fer les transformacions necessàries.
Com es pot anticipar, aquesta va ser una part prou complexa del desenvolupament que, gràcies a haver escrit tests de validació, vam poder gestionar amb eines de creació d’escenaris i de contorns de proves.
El resultat final
La definició del marc teòric, amb la bibliografia consultada del magnífic aparell crític que acompanya el llibre de Kleppmann, i la implementació, van ser un procés apassionant tant per la novetat, com pel desafiament i el valor que aportàvem als nostres clients. Vam aconseguir entregar el sistema, fent que el producte fos millor, i al mateix temps crear el fonament per a futurs desafiaments on l’edició i la interacció amb sistemes sigui en temps real.
Personalment, la lectura d’aquell llibre va ser clau per desvetllar l’entrellat d’aquest problema d’alta complexitat. Sense la inspiració i els marcs teòrics que hi vaig trobar, hauria trigat molt més a articular una solució robusta i elegant, o fins i tot podria no haver-la trobat. És un recordatori potent de com la literatura tècnica pot dotar-nos de les eines i el coneixement necessari per afrontar els reptes més grans de la nostra professió.

Deixa un comentari