L'ecosistema JavaScript ha evolucionat per oferir entorns d'execució alternatius a Node.js que prometen més eficiència, arrencades instantànies i una integració nativa amb la plataforma web. Però quan un equip de desenvolupament decideix abandonar Node per adoptar workerd —el runtime open-source que impulsa Cloudflare Workers— s'enfronta a una realitat implacable: els SDK de Node, dissenyats per a un món amb sistema de fitxers, mòduls natius i estat persistent en memòria, simplement es trenquen. En aquest article compartim la nostra experiència real migrant un backend SaaS a workerd, les fallades que vam trobar, les solucions que vam implementar i les lliçons que tot equip tècnic hauria de considerar abans d'emprendre un camí similar.
A Q2BSTUDIO, una empresa de desenvolupament de programari i tecnologia especialitzada en aplicacions a mida, estem acostumats a prendre decisions d'arquitectura que maximitzin la flexibilitat i minimitzin els costos operatius. El nostre producte principal és un assistent de suport potenciat per intel·ligència artificial, desplegat com a backend serverless. Després de mesos funcionant sobre Node.js, vam notar que els temps d'arrencada en fred, la gestió de contenidors i la dependència d'imatges dockeritzades començaven a alentir tant el desenvolupament com l'experiència d'usuari. Vam decidir migrar a workerd, seduïts per la promesa d'un runtime universal amb arrencades gairebé instantànies i desplegaments atòmics. El que no vam anticipar és que la majoria dels SDK de tercers que fèiem servir —Stripe, Resend, Better Auth— es trencarien de manera categòrica en el nou entorn.
El problema fonamental és que workerd implementa l'estàndard Web Platform: fetch, Request/Response, crypto.subtle, TextEncoder, streams. No ofereix la llibreria estàndard de Node, ni sistema de fitxers, ni estat de mòdul persistent entre peticions. Els SDK de Node, fins i tot aquells amb una API pública neta, arrosseguen dependències profundes que invoquen mòduls natius com crypto, fs o http. Quan l'empaquetador de workerd troba aquestes dependències, la compilació falla. No hi ha marge per al 'works on my machine': l'error passa en temps de construcció, no en producció. És brutal, però brutal en CI, no a les tres de la matinada.
El primer SDK que va sucumbir va ser el de Resend, el nostre proveïdor de correu transaccional. El paquet oficial de npm semblava inofensiu, però tirava de svix, una llibreria de verificació de webhooks que al seu torn necessitava mòduls de Node. Com que enviar un correu electrònic és una única crida HTTP, el vam reemplaçar per una funció fetch directa a l'API de Resend. El codi resultant és mínim: una petició POST amb capçalera Bearer i cos JSON, sense client, sense reintents automàtics, sense arbre de dependències. A més, vam implementar un petit validador d'adreces que rebutja el símbol $ —no per estàndard RFC, sinó per detectar variables d'entorn sense expandir que causarien errors 422. La lliçó: de vegades el més simple funciona millor, i eliminar un SDK de tercers pot reduir la superfície d'atac i millorar el manteniment.
El segon cop va arribar amb el SDK de Stripe per a Node, la peça central de la nostra facturació per ús. Stripe espera cossos codificats en formulari amb una convenció de claudàtors per niar objectes —line_items[0][price]=price_123— que el SDK amaga darrere d'una capa d'abstracció. Sense aquesta capa, vam haver d'implementar un codificador recursiu manual. Més crític encara: la verificació de webhooks de Stripe utilitza HMAC-SHA256 i requereix una comparació en temps constant per evitar fuites d'informació. Vam escriure la nostra pròpia funció verifyStripeSignature, que extreu la marca de temps i la signatura de la capçalera stripe-signature, calcula el HMAC amb crypto.subtle i compara en temps constant, rebutjant signatures de més de 300 segons d'antiguitat. Aquest mateix patró el vam replicar per als webhooks de Resend, però amb diferències subtils: Svix signa ${id}.${timestamp}.${body}, utilitza clau codificada en base64 i signatures separades per espais. Dos esquemes, dos verificadors manuals que ara mantenim nosaltres. El cost real d'abandonar el SDK és que els documents del proveïdor, no el seu codi, es converteixen en la nostra especificació.
El tercer SDK que no va sobreviure va ser el plugin de passkeys de Better Auth. Mai no vam cridar el codi ofensiu: el plugin depenia de @simplewebauthn/server, que al seu torn depenia de @peculiar/x509 i asn1js —un parser de certificats X.509 necessari per a la verificació d'atestació WebAuthn. A workerd, aquesta cadena de dependències va trencar l'empaquetat. La resta de Better Auth funciona perfectament; només el plugin de passkeys va ser eliminat abans del llançament. La moral: la dependència que mata el teu desplegament rarament està al teu package.json; sol ser el parser de certificats que el teu plugin d'autenticació necessita per a un flux que potser mai no utilitzis.
Més enllà dels SDK trencats, workerd imposa canvis subtils al model d'execució. Les variables d'entorn arriben com a bindings a cada petició, no com a process.env a l'àmbit global. Per tant, construïm el client d'autenticació a cada sol·licitud: createAuth(env). L'estat en memòria és un miratge perquè workerd distribueix el trànsit entre múltiples isolates efímers; el limitador de taxa per defecte de Better Auth, que utilitza memòria local, esdevé decoratiu. El vam reemplaçar per un comptador respatllat al binding d'estat KV, que sí que persisteix entre isolates. També vam descobrir que no hi ha sistema de fitxers, així que el nostre widget incrustat se serveix com una constant de cadena generada en temps de construcció, versionada atòmicament amb l'API que el serveix.
L'absència d'atomicitat a KV ens va obligar a decidir explícitament com fallar a cada punt. El limitador de taxa a endpoints públics falla obert —si el magatzem no està disponible, permetem el pas— perquè no volem que un problema d'infraestructura bloquegi tots els clients. La reserva d'idempotència a la ruta de facturació falla tancat —si no podem verificar l'estat, bloquegem l'operació— per evitar duplicats amb diners pel mig. Cada decisió de fallada està documentada al codi, i l'equip sap exactament què esperar davant d'una caiguda parcial.
No tot van ser trencaments. Hono, el framework HTTP que fem servir, funciona de manera nativa a workerd. Drizzle es connecta sense problemes a la base de dades SQLite compatible amb D1. Zod, el validador d'esquemes, és JavaScript pur. El nucli de Better Auth funciona. El SDK de Vercel AI i el nostre proveïdor de gateway d'IA també empaqueten sense errors. L'ecosistema web estàndard és sòlid; el problema es concentra als SDK de proveïdors amb arbres de dependències profunds, i no es pot predir simplement llegint les importacions del teu propi codi.
Des d'una perspectiva empresarial, la migració a workerd ens va oferir desplegaments que no poden funcionar a mitges: si empaqueta, funciona. La superfície de dependències es va reduir dràsticament; la integració amb Stripe va passar d'un SDK sencer a 289 línies que qualsevol desenvolupador pot llegir en una asseguda. Cada crida HTTP de facturació és visible en un sol fitxer. La portabilitat web estàndard permet executar el mateix codi a Cloudflare Workers, Deno o qualsevol runtime que implementi la plataforma web, la qual cosa encaixa amb el nostre model d'autohostatge. Tanmateix, vam pagar un preu: ara som els mantenedors de clients que abans mantenien els proveïdors. Quan Stripe llança una nova versió d'API, ningú actualitza un paquet per nosaltres; llegim el changelog i editem stripe.ts. Posseïm codi sensible de seguretat —dos verificadors de webhooks amb comparació en temps constant— que hem d'encertar, provar i mantenir correcte.
El balanç, per a un equip petit amb una superfície API simple i bones proves, és positiu. Però si el teu backend toca vint APIs de proveïdors amb superfícies canviants, construir vint clients manualment és un tracte molt pitjor, i un runtime Node que executi els SDK oficials és una elecció defensable. Si decideixes fer el salt, comença on hauríem d'haver començat nosaltres: no amb el teu package.json, sinó amb npm ls --all i una anàlisi dura del que arrosseguen les dependències de les teves dependències. El SDK que trenca el teu desplegament mai no és el que vas importar.
A Q2BSTUDIO, apliquem aquesta mateixa filosofia a tots els nostres projectes de cloud AWS/Azure, on l'elecció del runtime i la gestió de dependències són crítiques per oferir solucions escalables i segures. També integrem serveis de ciberseguretat per protegir els webhooks i les claus d'API, i utilitzem Business Intelligence amb Power BI per monitoritzar el comportament del sistema en temps real, detectant patrons d'ús i possibles anomalies. Els nostres agents IA es beneficien d'un runtime lleuger que permet respostes ràpides sense la sobrecàrrega d'un contenidor pesat. La lliçó final: migrar a workerd no és un caprici tècnic, sinó una decisió estratègica que requereix auditar no només les teves importacions, sinó tot l'arbre de dependències transitives. Quan ho fas bé, guanyes en velocitat, fiabilitat i control. Quan no, aprens —com nosaltres— que el que es trenca mai no és el que esperes.




