Doblem productivitat amb Voyager
Albert Cervera i Areny
Hola,
la setmana vinent et començaré a explicar coses dels avenços que hem fet en IA però aquesta setmana toca parlar de com creem pàgines web integrades a Tryton.
Llunes i tecnologies
El nom de Tryton prové de la combinació del nom del llenguatge de programació Python i una de les llunes de Neptú: Tritó.
Altres llunes del mateix planeta com Sao o Proteus han donat nom a altres components de l'ecosistema Tryton.
I abans de començar a treballar a NaN-tic, el Raimon Esteve va batejar amb el nom de Galatea (efectivament, una altra dels satèl·lits de Neptú), un conjunt de components software que va crear basant-se en altres tecnologies ben establertes (com Flask i Jinja) que ens ha permès durant anys oferir solucions web 100% integrades a l'ERP.
Amb Galatea hem creat pàgines web, botigues online, formularis de captació de dades i extranets.
El gran avantatge és que ens permet aprofitar el que anomenem la "lògica de negoci". És a dir, tots els càlculs que ja fa l'ERP per portar a terme les tasques del dia a dia s'aprofiten per crear la pàgina web. Evitant molts problemes d'errors derivats de fer les coses dues vegades amb dues tecnologies diferents.
En algunes ocasions hem explorat altres tecnologies JavaScript (com Angular o Ionic), però tenen el problema de què el món JavaScript canvia molt ràpidament, normalment amb pocs beneficis. Cada 2 anys sempre hi ha algú que descobreix la sopa d'all i decideix canviar-ho tot. I és per això que ja fa uns anys que estem mirant de deixar d'utilitzar aquestes tecnologies.
El que millor ens ha funcionat ha estat Galatea.
Simplement funciona.
Web vs Tryton
Malgrat això no podíem deixar d'observar la diferència de productivitat que hi ha entre desenvolupar una nova funcionalitat per Tryton vs una web.
Quan fem una millora per un client per a Tryton som molt productius i com et vaig explicant setmanalment treballem constantment per continuar millorant.
És lògic ser més productius amb Tryton, perquè treballem en un sistema concebut per fer ràpidament i de forma robusta allò que és més habitual i ho fem amb alguns sacrificis: potser no tenim la interfície gràfica perfecte, però el fet de poder-ho fer molt més ràpid i econòmic compensa.
A més, l'usuari ja està entrenat en l'entorn, el que facilita molt la feina. Ni l'usuari ni nosaltres hem de parar gaire atenció a l'estètica: el focus és la productivitat.
Però quan fem una web ens dirigim a gent de fora de l'empresa i per tant, el paradigma canvia totalment: l'estètica i usabilitat passen a ser importants, ens cal tenir molta més flexbilitat i amb ella disminueix la productivitat.
I això és inevitable.
Oi?
Ben pensat, una cosa és assumir que desenvolupar webs serà menys eficient que desenvolupar mòduls per l'ERP i una altra qüestió és: en quina proporció?
Si el nostre objectiu és desenvolupar software a mida amb la mateixa eficiència que si fos software estàndard, per què limitar-nos a aquells desenvolupaments que presentem als usuaris de l'ERP?
És el desenvolupament web que fem actualment amb Galatea l'equilibri perfecte entre flexibilitat i productivitat?
Com que no hi ha res perfecte, la respostà és fàcil: no.
Així que ens vam fer totes aquestes preguntes i vam analitzar quines prestacions ens dona Tryton que trobem a faltar quan desenvolupem webs.
Quines tecnologies i paradigmes de programació actuals ens podrien ajudar a reduir la bretxa?
I vam arribar a aquesta relació:
- A diferència de Tryton, el desenvolupament web no permet reutilitzar molta part de la feina feta entre projectes
- La manca de tests automàtics produeix més incidències
- Hi ha molta dispersió en els fitxers de codi, fer un canvi pot implicar canvis a diversos fitxers i és difícil conèixer bé tot el que cal tocar
- En un intent de permetre reutilitzar codi entre projectes, es requereix molta configuració inicial i fa que la lògica és més complexa que no pas amb el sistema de mòduls de Tryton
- És complicat ampliar funcionalitats existents perquè no és evident les conseqüències que tindrà per les diferents webs
- Quan necessitem que la web sigui molt interactiva (i en gairebé tots els projectes n'hi ha almenys una part) ens cal treballar amb JavaScript, el que complica encara més el desenvolupament
- La gestió de sessions d'usuari és una mica caòtica, excessivament flexible i es fa difícil saber on ha d'anar certa informació
- Depenem d'un número de tecnologies una mica massa gran, seria interessant simplificar-ho
Voyager per turistes espacials
Si has arribat fins aquí però et perds amb els tecnicismes simplement t'explicaré que hem creat Voyager, un producte construït des de zero a NaN-tic que resol tots els punts anteriors, mantenint els avantatges de Galatea.
La sonda espacial Voyager 2 va sortir l'any 1977 per explorar els planetes del sistema solar exterior i entre moltes altres fites, va descobrir diverses llunes de Neptú i com no, Galatea.
El nostre Voyager és el resultat d'explorar i aprendre dels aprenentatges dels darrers 10 anys amb Galatea, Flask, Angular, Ionic, React, ReactPy, Dominate, HTMX... i el propi Tryton.
Voyager és la tecnologia que hem creat per construir una nova generació de solucions web pels nostres clients a partir d'ara.
I no, no és un futurible.
Ja l'estem utilitzant per crear una botiga online per un dels nostres clients i la millora és evident i notable. Fins al punt que estem veient que tardem menys de la meitat del temps que tardàvem abans.
Reduir dràsticament aquests costos ens permetrà continuar incrementant el valor que aportem als nostres clients.
Voyager per astronautes professionals
Si decideixes llegir aquesta secció tingues en compte que aquí hi explicaré la tecnologia amb pèls i senyals. No és contingut per a tots els públics. Si no et va la cosa tan hardcore et recomano passar directament a la conclusió ;-)
Altres tecnologies i conceptes
A banda de la llista de punts de millora que he anomenat anteriorment, també hi havia punts forts que volíem mantenir i influències i aspectes interessants que hem vist en altres tecnologies:
- Utilitzar el mínim possible JavaScript, encara que potser serà imprescindible en alguns casos, això significa generar el màxim possible a la banda del servidor.
- Els frameworks JavaScript tenen la seva gràcia, especialment hem tingut experiència interessant amb React, però igualment requereix crear webservices que compliquen el desenvolupament i continuen tenint el problema d'haver de tocar en diversos fitxers per qualsevol canvi que es vol fer. A més, a la mica que la web creix, React i altres frameworks es tornen notablement més complexos i sovint requereixen paradigmes nous i més difícils d'entendre per tots els desenvolupadors.
- El concepte de components de React és molt interessant i facilita la modularització de l'aplicació
- De React també és molt interessant la forma en què dins de JavaScript es pot posar codi HTML mitjançant JSX. Podríem tenir quelcom semblant a la banda del servidor en Python?
- ReactPy mira d'implementar-ho però sembla un projecte massa poc madur i l'stack, en el fons, massa complex
- HTMX semblava una tecnologia molt interessant a explorar per minimitzar l'ús de JavaScript i s'acopla bé al concepte de components
- També vam veure que TailwindCSS guanyava adeptes en detriment de Bootstrap que havíem utilitzat fins ara. Tailwind també ens ajuda a tenir el codi menys separat. Tot és al mateix fitxer.
La solució tècnica
Hem creat un mòdul Tryton que hem anomenat voyager i amb les següents característiques i eines:
- Depèn del mòdul
web_userde Tryton core, així deixem de tenir la nostra pròpia implementació del concepte usuari web - Implementa una classe
Componentque hereda deModelView. Això vol dir que un component es registra al pool com la resta de classes Tryton i per tant pot ser sobrecarregat i cridat per qualsevol altre mòdul Tryton. - Un script d'arrencada que permet arrencar qualsevol web que es vulgui crear. L'script està pensat perquè no sigui necessària cap configuració a la base de dades abans de poder començar a servir contingut. És arrencar i funcionar.
- Un model
Websitepermet tenir configuracions que poden ser necessàries per l'entorn de producció, però no imprescindibles per començar a fer proves. - Un model
Sessionsempre disponible en el context d'execució de les peticions web per poder vincular registres que es creïn a la base de dades amb una sessió en concret. La sessió també té unuser, que s'omple si l'usuari fa login. - El concepte
Triggerque permet implementar seccions dinàmiques a la web sense necessitat d'utilitzar ni una línia de JavaScript.
Deixa'm que entri en detall dels conceptes de components i de triggers.
Components
Un component del Voyager és qualsevol part d'una pàgina web. Sigui la pàgina sencera, un formulari, un fragment que permet mostrar un producte, etc.
Un component ha d'implementar sempre una funció render(self) que és l'encarregada de retornar el codi HTML.
Aquest HTML però, no s'ha de retornar com un string sinó com un objecte dominate.
Dominate és una llibreria python molt interessant que ens permet crear codi HTML amb codi python pur. Fa la funció de JSX però fins i tot té alguns avantatges addicionals com el fet de que no requereix cap preprocessador i el fet que no requereix tancar tags, així que no hi ha errors de sintaxi tontos.
A més a més, en molts casos redueix el número de línies de codi comparat amb el codi HTML original fins a arribar a una reducció del 50% en alguns casos!
Fes una ullada a aquest exemple, on pots veure un component (CartWidget) fent ús d'un altre component (LineWidget):
from trytond.modules.voyager import Component
class CartWidget(Component):
'Voyager Cart'
___name__ = 'voyager.cart'
def render(self):
pool = Pool()
LineWidget = pool.get('voyager.cart.line')
Sale = pool.get('sale.sale')
sale = Sale.get_sale_from_session()
main = div()
with main:
h1(_('Cart'))
for line in sale.lines:
LineWidget(product=line.product, quantity=line.quantity)
return main
Això permet a un nou mòdul modificar el render de voyager.cart.line sense haver de modificar voyager.cart.
A més a més, els Component suporten lazy loading per defecte. Això vol dir que la pàgina principal pot incorporar la cistella de la compra simplement fent CartWidget(lazy_loading=True), el que farà renderitzar la pàgina sencera mostrant temporalment un missage de Loading... en el lloc on hi hauria d'haver la cistella. Immediatament, però, el navegador farà la crida al servidor i n'actualitzarà el contingut.
Això permet fer que la pàgina inicial carregui molt ràpidament, deixant els elements lents per una mica més tard. Si ho vols, per cada component pots alterar el contingut que s'ha de mostrar mentres no es carrega la pàgina definitiva, simplement sobrecarregant el mètode lazy_content(self).
Aquest sistema de càrrega dinàmica l'hem implementat sense necessitat d'incorporar ni una sola línia de codi JavaScript gràcies a fer ús d'HTMX. I és que hem decidit que aquest serà un altre element bàsic d'aquest framework.
Triggers
De fet, també hem integrat el sistema de triggers que permet HTMX.
Per exemple, només cal afegir una declaració i modificar una línia del codi anterior per tal de fer que el contingut de la cistella s'actualitzi cada vegada que es produeixi algun canvi:
from trytond.modules.voyager import Component
class CartWidget(Component):
'Voyager Cart'
___name__ = 'voyager.cart'
updated = Trigger()
def render(self):
pool = Pool()
LineWidget = pool.get('voyager.cart.line')
Sale = pool.get('sale.sale')
sale = Sale.get_sale_from_session()
main = div(hx-trigger=f'{self.updated} from:body', hx-get=self.url())
with main:
h1(_('Cart'))
for line in sale.lines:
LineWidget(product=line.product, quantity=line.quantity)
return main
Llavors, qualsevol altra punt de l'aplicació podrà llançar el trigger amb Trigger.add_trigger(CartWidget.updated) i Voyager + HTMX ja s'encarreguen de la resta.
Fixa't a més, que hem fet que els triggers siguin atributs de la classe. Això ho fem perquè així el propi intèrpret de python ja ens detectarà si cometem qualsevol error tipogràfic i no ens hem d'estar barallant amb problemes derivats d'haver posat un text incorrecte a hx-trigger.
No sé si et pots arribar a imaginar la quantitat de codi JavaScript + webservices que estalvia aquest mecanisme....
No entraré en més detalls però els components també admeten paràmetres de la mateixa manera que Tryton admet camps i endpoints addicionals, a part del render().
El que ens estalviem...
Amb aquests canvis passem a dependre de Tailwind, Dominate i HTMX però eliminem moltes dependències:
- Bootstrap
- Flask
- Flask-Login
- Flask-Babel
- Flask-WTF
- flask_tryton
- Flask-Session
- Flask-paginate
- wtforms-html5
A més, Dominate és una llibreria molt senzilla que ens estalvia centenars de línies de codi, HTMX també és petit i la documentació es llegeix en una hora. I Tailwind és més gran, però no pas més que Bootstrap, i el concepte s'entén fàcilment. El codi resultant amb Tailwind és més senzill i flexible.
També hem comprat la llicència d'equips de TailwindUI, la qual ens dóna access a +500 components i plantilles que podem aplicar en minuts i que són fàcilment personalitzables a les necessitats de cada client.
Creiem que a més de donar-nos més velocitat de desenvolupament, obtindrem un disseny molt més atractiu i professional d'entrada. Probablement els clients també estalviaran en costos de dissenyadors externs.
Conclusió
Se'm fa complicat transmetre l'avenç que suposa Voyager. Per nosaltres serà un abans i un després que es resumeix en:
- Doblarem productivitat en desenvolupament
- Reduirem línies de codi i abaratirem el cost de manteniment
- Oferirem webs més atractives
Aconseguir només una d'aquestes tres coses ja seria motiu de satisfacció però poder fer les tres a la vegada ens fa realment feliços!
Salut i bon cap de setmana!