
Ani la rând, Vercel a fost platforma implicită pe care implementam aproape tot ce publicam pe web la WebCatalog. Majoritatea acestor proiecte foloseau Next.js, așa că asocierea era firească. Site-urile de marketing, interfețele produselor, setările conturilor, consola pentru dezvoltatori, autentificarea, instrumentele de administrare și API-urile noastre au ajuns toate să folosească aproximativ aceeași stivă tehnologică.
Multă vreme, soluția a funcționat bine. Vercel simplifica implementările, Next.js ne oferea un framework full-stack productiv, iar noi rareori trebuia să ne gândim la infrastructura din spatele lor.
În cele din urmă, această comoditate nu a mai corespuns nevoilor produselor noastre.
Site-urile noastre de marketing beneficiau în continuare de SSR (randare pe server), dar majoritatea celorlalte aplicații web nu aveau nevoie de el. Puteau fi simple SPA-uri (aplicații cu o singură pagină). API-urile noastre aveau nevoie de un mediu Node.js obișnuit, cu dependențe native și procese care rulează continuu. Aproape toate celelalte proiecte din stiva noastră frontend foloseau deja Vite. În același timp, costurile infrastructurii deveneau tot mai greu de ignorat, mai ales pe măsură ce creștea traficul public și automatizat.
Inițial, am încercat să optimizăm ce aveam deja. Am luat în calcul păstrarea Next.js și mutarea lui pe Cloudflare Workers printr-un adaptor. Am încercat Astro pentru site-urile de marketing. Pentru scurt timp, ne-am găzduit API-urile pe Cloudflare Containers.
În cele din urmă, am ajuns la o soluție mai simplă: majoritatea aplicațiilor noastre web folosesc acum Vite + TanStack Router pe Cloudflare Workers, site-urile de marketing folosesc TanStack Start cu SSR pe Cloudflare Workers, iar API-urile folosesc Hono + tRPC pe Railway, sub formă de containere Node.js care rulează continuu.
Migrarea ne-a redus costurile infrastructurii web cu aproximativ 80%. După ce am înăsprit și regulile de securitate Cloudflare și am blocat mai mult trafic automatizat ostil la marginea rețelei, economiile totale au ajuns la circa 90%.
Cea mai mare parte a implementării migrării a fost realizată și de agenți AI de programare, în principal Claude Fable 5 și Claude Opus 5, în timp ce inginerii au decis arhitectura, au definit constrângerile, au revizuit modificările și au verificat rezultatul.
Nu am început prin a părăsi Vercel
Primul nostru impuls a fost să optimizăm configurația existentă.
webcatalog.io era cel mai evident punct de plecare, deoarece primește mult trafic public, în timp ce majoritatea paginilor sale se schimbă relativ rar. Am constatat că prea mult din site era încă randat dinamic, așa că am eliminat randarea dinamică neintenționată, am adăugat ISR (regenerare statică incrementală) pe rutele cu trafic intens, am făcut mai multe pagini cache-uibile și am optimizat comportamentul costisitor al sitemap-urilor.
Munca a ajutat, dar a scos și mai clar în evidență problema de fond. Petreceam tot mai mult timp încercând să înțelegem de ce un conținut relativ static era dinamic, ce comportament al frameworkului provocase asta și ce mecanism al frameworkului trebuia să introducem pentru a-l face din nou cache-uibil.
Între timp, multe dintre celelalte aplicații ale noastre aveau problema opusă. Setările conturilor, consola pentru dezvoltatori, aplicațiile de administrare și majoritatea interfețelor produselor nu aveau deloc nevoie de randare pe server. Erau aplicații care rulau în browser și comunicau cu API-uri.
În acel moment, întrebarea a încetat să mai fie „Cum ne reducem factura Vercel?” și a devenit „Ce am construi dacă aceste aplicații nu ar fi deja aplicații Next.js?”
Am luat în calcul Next.js pe Cloudflare
Opțiunea care ar fi produs cele mai puține perturbări era să păstrăm Next.js și să îl mutăm de pe Vercel pe Cloudflare Workers.
Am analizat serios această variantă, deoarece Next.js poate rula pe Cloudflare prin straturi de adaptare, ceea ce ne-ar fi permis să păstrăm o mare parte din structura existentă a aplicațiilor.
Dar nu consideram că Next.js pe Cloudflare este echivalent cu Next.js pe Vercel. Next.js este integrat cel mai strâns cu mediul de execuție, sistemul de implementare, comportamentul de cache și funcționalitățile platformei Vercel. Pe Cloudflare, un adaptor trebuie să transpună aceste presupuneri într-un mediu de execuție diferit.
Asta poate funcționa bine, dar, dacă obiectivul nostru era să simplificăm stiva tehnologică, adăugarea unui nou strat de compatibilitate nu părea ideală.
Mai exista și o problemă mai largă legată de instrumente. Aproape tot ce construiam în frontend folosea deja Vite: aplicații desktop, extensii de browser, SPA-uri și alte proiecte care rulează pe partea de client.
Next.js se orientase puternic către Turbopack, iar Turbopack s-a îmbunătățit considerabil. Nu era vorba despre o nemulțumire legată de performanță. Pentru noi, diferența mai importantă era ecosistemul. Vite era deja baza comună pentru cea mai mare parte a codului nostru frontend, cu un ecosistem de pluginuri mai amplu și configurații mai ușor de reutilizat între proiecte.
Prin urmare, păstrarea Next.js pe Cloudflare ar fi rezolvat o parte din problema găzduirii, dar ar fi menținut atât nepotrivirea dintre frameworkuri, cât și un ecosistem separat de instrumente de compilare.
Așa că am făcut un pas înapoi și am analizat de ce avea nevoie, de fapt, fiecare tip de aplicație.
Am împărțit stiva tehnologică după tipul aplicației
Odată ce am încetat să căutăm un înlocuitor universal pentru Next.js, arhitectura a devenit mult mai simplă.
Site-urile noastre de marketing aveau nevoie de SSR deoarece au pagini publice și localizate, metadate, URL-uri canonice, sitemap-uri și trafic din motoarele de căutare.
Majoritatea celorlalte aplicații web nu aveau nevoie de SSR și puteau fi pur și simplu aplicații Vite care folosesc TanStack Router.
API-urile noastre nu aveau deloc nevoie de un framework React și se potriveau mai bine într-un mediu de execuție Node.js obișnuit.
Împărțirea finală arăta astfel:
Aplicații web
Vite + TanStack Router
│
└── Cloudflare Workers
Site-uri de marketing
TanStack Start + SSR
│
└── Cloudflare Workers
API-uri
Hono + tRPC
│
└── Railway / containere Node.js
Regula a devenit simplă: folosește SSR acolo unde aduce un beneficiu real, folosește un SPA simplu acolo unde SSR nu aduce beneficii și rulează serviciile backend într-un mediu de execuție pentru backend.
Majoritatea aplicațiilor web au devenit SPA-uri
Migrările la SPA au fost partea cea mai ușoară.
Aplicația web WebCatalog a fost migrată deosebit de repede: am creat structura inițială a înlocuitorului Vite + TanStack Router, am portat interfața, am mutat implementarea pe Cloudflare Workers și am eliminat vechea versiune Next.js în aceeași dimineață.
Am repetat același proces pentru aplicația web Lexibird, setările conturilor, consola pentru dezvoltatori, autentificare și aplicațiile de administrare. De la crearea structurii inițiale a primului SPA până la ștergerea ultimei aplicații Next.js au trecut aproximativ 19 zile.
Pentru aceste aplicații, Cloudflare Workers este intenționat cât se poate de simplu. Vite construiește resursele statice, Cloudflare le servește, iar rutele aplicației folosesc index.html ca soluție de rezervă. Nu există un mediu de execuție SSR, pentru că nimic nu necesită randare pe server.
Partea mai dificilă a fost să păstrăm comportamentele acumulate în timp în jurul acestor aplicații. O parte din logica de autentificare a trebuit mutată din rutele de server Next.js în API, iar versiunile mai vechi ale aplicației desktop WebCatalog depindeau încă de endpointuri moștenite, pe care a trebuit să le menținem funcționale.
Mutarea interfeței React a fost ușoară.
Păstrarea contractelor a fost mai grea.
Am încercat Astro și l-am eliminat cinci zile mai târziu
Site-urile de marketing au fost mai dificile.
Prima noastră alegere a fost Astro, care părea potrivit pentru webcatalog.io și lexibird.com, deoarece ambele sunt site-uri publice, bogate în conținut, iar Astro se integrează bine cu Cloudflare.
Am început să portăm ambele site-uri, iar cinci zile mai târziu ne-am oprit.
Nu a existat un singur neajuns decisiv. În schimb, micile nepotriviri continuau să se adune. Unele rute aveau nevoie de acces la baza de date în timpul prerandării, deși mediul nostru de compilare nu avea, în mod intenționat, date de autentificare pentru baza de date de producție. Insulele React făceau mai puțin naturală partajarea stării aplicației. Ne-am lovit și de diferențe între rutele prerandate și cele randate în timpul execuției, precum și de unele dificultăți legate de instrumentele din depozitul nostru de cod.
Niciuna dintre aceste probleme nu era imposibil de rezolvat. Tocmai de aceea ar fi fost periculos să continuăm. Am fi putut petrece cu ușurință încă câteva săptămâni rezolvând câte o problemă după alta.
În schimb, am șters implementarea și am luat-o de la capăt.
Agenții AI au schimbat calculul din spatele acestei decizii. O mare parte din munca mecanică de portare nu consumase săptămâni din timpul inginerilor, așa că investiția deja făcută era mai mică și ne-a fost mai ușor să recunoaștem că preferam o altă arhitectură.
TanStack Start ni s-a potrivit mai bine
Am reluat migrarea site-urilor de marketing pornind de la codul Next.js original, de data aceasta folosind TanStack Start.
S-a potrivit mult mai natural, deoarece foloseam deja TanStack Router în aplicațiile SPA, astfel încât rutarea, funcțiile de încărcare a datelor, parametrii de căutare și navigarea urmau concepte similare. Codul a rămas React și TypeScript obișnuit, iar sistemul de compilare era Vite.
Am portat lexibird.com pe TanStack Start într-o zi. A urmat webcatalog.io.
Avantajul nu a fost că TanStack Start ar fi rezolvat ca prin magie orice problemă. Avantajul a fost că se potrivea cu direcția în care se îndrepta deja restul stivei noastre tehnologice.
Într-un monorepo, asta contează. Instrumentele de compilare comune înseamnă mai multe pluginuri și configurații reutilizabile, mai puține cazuri speciale la depanare, mai puține schimbări de context pentru dezvoltatori și mai puține convenții specifice fiecărui proiect pe care agenții de programare trebuie să le descopere.
Cloudflare a fost mult mai ieftin pentru traficul nostru
Arhitectura a fost doar o parte din motivul pentru care factura noastră a scăzut. Prețurile Cloudflare au fost un factor major, în special în privința lățimii de bandă.
Cloudflare Workers Paid taxează în prezent în principal pe baza cererilor și a utilizării procesorului și nu adaugă costuri pentru transferul de date sau lățimea de bandă în cazul Workers. (developers.cloudflare.com) Acest lucru contează foarte mult pentru site-urile publice, deoarece o cerere poate folosi foarte puțin procesor, dar poate transfera totuși HTML, JavaScript, imagini, fonturi și alte resurse.
Modelul de tarifare Vercel include și categorii de utilizare și limite incluse legate de rețea, printre care Fast Data Transfer și Fast Origin Transfer. (vercel.com) Pentru profilul nostru de trafic, costurile Cloudflare au fost semnificativ mai avantajoase.
Economiile nu au provenit doar din schimbarea furnizorului. Am mutat și multe aplicații pe versiuni statice construite cu Vite, am redus SSR-ul inutil, am făcut regulile de cache mai explicite și am mutat API-urile într-un mediu de execuție mai potrivit. Dar prețurile Cloudflare, în special cele legate de lățimea de bandă, au contribuit substanțial la reducerea de aproximativ 80% pe care am observat-o.
Această cifră este specifică aplicațiilor și traficului nostru. Nu susținem că orice aplicație mutată de pe Vercel pe Cloudflare va economisi 80%.
API-urile au ajuns pe Railway
API-urile reprezentau o problemă separată.
Deși erau proiecte Next.js, ele erau deja în mare parte independente de Next.js. API-ul WebCatalog avea aproximativ 34.000 de linii, dar numai șapte fișiere importau next/server. tRPC folosea deja adaptorul său Fetch, iar Inngest era compatibil cu Hono.
Am înlocuit stratul HTTP din Next.js cu Hono. Această parte a fost surprinzător de simplă.
Întrebarea mai dificilă era unde să îl rulăm. Workers obișnuiți nu erau potriviți, deoarece API-urile folosesc Node.js și dependențe native precum sharp.
Inițial, am încercat Cloudflare Containers, ceea ce ne-a permis să plecăm rapid de pe Vercel. Dar, după ce am rulat această configurație în producție, am constatat că modelul operațional necesita mai multe ajustări manuale ale capacității decât ne doream la momentul respectiv.
Așa că am mutat din nou API-urile.
Astăzi, ele rulează pe Railway ca servicii obișnuite în containere Node.js cu durată lungă de viață. CI (integrarea continuă) construiește imaginile Docker și le publică în registrul nostru, iar Railway gestionează scalarea automată pe orizontală.
Nu totul trebuie să ruleze la marginea rețelei.
Plecarea de pe Vercel a însemnat mai multe responsabilități pentru noi
Costurile mai mici au venit cu un compromis.
Vercel și Next.js gestionaseră multe aspecte ale infrastructurii la un nivel mai înalt. Cu TanStack Start și Workers, am trecut în schimb la folosirea explicită a cache-ului HTTP. O pagină este randată, returnăm antete de cache, iar Cloudflare stochează răspunsul în cache. Cache-ul din browser și cel de la marginea rețelei pot avea politici diferite, inclusiv comportamentul stale-while-revalidate la marginea rețelei.
Ne-a plăcut faptul că aceste decizii au devenit explicite, dar infrastructura explicită înseamnă și că greșelile îți aparțin.
Am făcut câteva. O regulă de cache din producție s-a aplicat din greșeală răspunsurilor funcțiilor de server TanStack specifice fiecărui vizitator, iar o altă configurație de cache a interacționat prost cu comportamentul de rezervă al SPA-ului. Aceste erori ne-au reamintit că funcționarea corectă a unei migrări nu poate fi demonstrată doar examinând depozitul de cod. Și infrastructura de producție face parte din sistem.
Agenții AI au schimbat modul în care am migrat
Cea mai mare parte a implementării a fost realizată de agenți de programare, în principal Claude Fable 5 și Claude Opus 5.
Migrările între frameworkuri se potrivesc deosebit de bine agenților, deoarece o mare parte din muncă are o referință existentă clară. Mută această rută, păstrează acest URL, înlocuiește acest API al frameworkului, păstrează aceleași metadate, rulează verificarea tipurilor, corectează erorile și compară rezultatul cu vechea implementare.
Pentru webcatalog.io, am ținut un registru al migrării care împărțea munca în etape precum fundația, paginile produselor, paginile catalogului, căutarea, blogul, prețurile, redirecționările, sitemap-urile și trecerea la noua versiune. În loc să îi cerem unui agent „să migreze webcatalog.io la TanStack Start”, i-am dat sarcini bine delimitate, cu cerințe explicite care trebuiau păstrate.
Aplicația existentă a devenit specificația. Sarcina agentului era să reproducă comportamentul folosind noua arhitectură, nu să reinventeze produsul.
Acest lucru a mutat efortul oamenilor de la traducerea manuală a codului către întrebări precum: Această pagină chiar ar trebui să folosească SSR? Care comportament este intenționat? Ce poate fi stocat în cache la nivel global? Ce URL-uri trebuie să rămână identice? Unde ar trebui să aibă loc autentificarea? Când ar trebui să încetăm să mai încercăm să facem o abordare să funcționeze?
Am continuat să revizuim rezultatele, să rulăm compilări și verificări ale tipurilor și să verificăm manual zonele cu risc mai ridicat, precum autentificarea, SEO (optimizarea pentru motoarele de căutare), redirecționările și cache-ul.
Schimbarea importantă nu a fost că AI-ul a scris cod pentru noi. A fost faptul că AI-ul a făcut experimentarea mai ieftină.
A devenit mult mai ușor de justificat să încercăm Astro și să îl eliminăm după cinci zile. A fost mai puțin dureros să mutăm API-urile o dată și apoi să decidem că o altă platformă era mai potrivită. Munca mecanică devenise mai ieftină, așa că puteam schimba direcția când arhitectura era greșită, în loc să continuăm doar pentru că investiserăm deja prea mult în ea.
Agenții au făcut ca implementarea să nu mai fie o resursă atât de limitată.
Asta a făcut arhitectura, constrângerile, revizuirea și verificarea și mai importante.
80% au devenit aproximativ 90%
După migrare, costul infrastructurii noastre web era cu aproximativ 80% mai mic.
Apoi am început să acordăm mai multă atenție cererilor care ajungeau efectiv la aplicații.
O parte semnificativă a traficului de pe internetul public este automatizată: motoare de căutare, crawlere AI, agenți, programe de extragere a datelor, scanere și boți mai puțin prietenoși. O parte din acest trafic este utilă, iar o parte nu.
Cu aplicațiile aflate direct în spatele Cloudflare, am înăsprit regulile de securitate astfel încât traficul automatizat ostil să poată fi respins la marginea rețelei, înainte să declanșeze procesare în aplicații sau operațiuni în baza de date.
După aceea, economiile totale au ajuns la aproximativ 90% față de configurația veche.
Reducerea finală a rezultat din mai mulți factori care au acționat împreună: prețurile mai mici ale Cloudflare, în special pentru lățimea de bandă; mai puține operațiuni pe server; un mediu de execuție mai potrivit pentru API-urile noastre; reguli de cache mai explicite; și mai puține cereri inutile care ajungeau la aplicații.
Unde am ajuns
Nu mai există nicio aplicație Next.js în depozitul nostru de cod. Majoritatea aplicațiilor noastre web sunt acum SPA-uri Vite + TanStack Router pe Cloudflare Workers. webcatalog.io și lexibird.com folosesc TanStack Start pe Cloudflare Workers, deoarece aceste site-uri beneficiază cu adevărat de SSR. API-urile noastre folosesc Hono + tRPC pe Railway, rulând în containere Node.js obișnuite. Aproape toate proiectele noastre frontend fac acum parte din ecosistemul Vite.
Costul infrastructurii noastre a scăzut cu aproximativ 80%, la care au contribuit substanțial costurile mult mai avantajoase ale Cloudflare pentru traficul nostru, în special în privința lățimii de bandă. Filtrarea traficului automatizat ostil la marginea rețelei a dus economiile totale la circa 90%.
Dar rezultatul care contează cel mai mult pentru noi ține de arhitectură. Site-urile de marketing primesc SSR, tot ce poate fi un SPA este un SPA, iar API-urile rulează pe servere propriu-zise atunci când acestea reprezintă soluția mai simplă.
Am început încercând să reducem costurile Vercel.
Am încheiat realizând că nu aveam nevoie de cea mai mare parte a arhitecturii pentru care plăteam.