
Po celá léta byl Vercel ve WebCatalogu výchozím místem, kam jsme nasazovali téměř vše, co běželo na webu. Většina těchto projektů používala Next.js, takže tato kombinace byla přirozená. Naše marketingové weby, produktová rozhraní, nastavení účtu, vývojářská konzole, autentizace, administrační nástroje i API nakonec běžely na zhruba stejném technologickém základu.
Dlouho to fungovalo dobře. Vercel zjednodušoval nasazování, Next.js nám poskytoval produktivní fullstackový framework a o infrastruktuře pod nimi jsme téměř nemuseli přemýšlet.
Časem ale toto pohodlí přestalo odpovídat potřebám našich produktů.
Našim marketingovým webům stále prospívalo SSR (vykreslování na serveru), ale většina ostatních webových aplikací ho nepotřebovala. Mohly fungovat jako běžné SPA (jednostránkové aplikace). Naše API potřebovala standardní prostředí Node.js s nativními závislostmi a dlouhodobě běžícími procesy. Téměř všechno ostatní v našem frontendovém stacku už používalo Vite. Zároveň bylo stále těžší přehlížet náklady na infrastrukturu, zejména s růstem veřejného a automatizovaného provozu.
Nejdřív jsme se pokusili optimalizovat to, co už jsme měli. Zvažovali jsme, že si ponecháme Next.js a přes adaptér ho přesuneme na Cloudflare Workers. Pro marketingové weby jsme vyzkoušeli Astro. Na krátkou dobu jsme naše API umístili do Cloudflare Containers.
Nakonec jsme dospěli k jednoduššímu řešení: většina našich webových aplikací nyní používá Vite + TanStack Router na Cloudflare Workers, marketingové weby používají TanStack Start se SSR na Cloudflare Workers a API používají Hono + tRPC na Railway v podobě dlouhodobě běžících kontejnerů Node.js.
Migrace snížila naše náklady na webovou infrastrukturu zhruba o 80 %. Když jsme navíc zpřísnili bezpečnostní pravidla Cloudflare a začali na okraji sítě blokovat více nepřátelského automatizovaného provozu, celková úspora vzrostla přibližně na 90 %.
Většinu implementačních prací při migraci také provedli AI agenti pro programování, především Claude Fable 5 a Claude Opus 5. Inženýři rozhodovali o architektuře, stanovovali omezení, kontrolovali změny a ověřovali výsledek.
Nezačali jsme odchodem z Vercelu
Naším prvním instinktem bylo optimalizovat stávající řešení.
Nejzřejmějším výchozím bodem byl webcatalog.io: přichází na něj hodně veřejného provozu, zatímco většina jeho stránek se mění poměrně zřídka. Zjistili jsme, že se příliš velká část webu stále vykresluje dynamicky. Opravili jsme proto neúmyslné dynamické vykreslování, na velmi navštěvované trasy přidali ISR (inkrementální statickou regeneraci), umožnili ukládat více stránek do mezipaměti a optimalizovali náročné zpracování souborů sitemap.
Pomohlo to, ale také to jasněji odhalilo základní problém. Trávili jsme stále více času zjišťováním, proč se relativně statický obsah vykresluje dynamicky, které chování frameworku to způsobilo a jaký mechanismus frameworku musíme použít, aby jej opět bylo možné ukládat do mezipaměti.
Mnoho našich dalších aplikací mezitím mělo opačný problém. Nastavení účtu, vývojářská konzole, administrační aplikace ani většina produktových rozhraní vykreslování na serveru vůbec nepotřebovaly. Byly to aplikace běžící v prohlížeči, které komunikovaly s API.
V tu chvíli jsme si přestali klást otázku „Jak snížíme účet za Vercel?“ a začali se ptát: „Co bychom vytvořili, kdyby tyto aplikace už nebyly napsané v Next.js?“
Zvažovali jsme Next.js na Cloudflare
Nejméně rušivou možností bylo ponechat Next.js a přesunout ho z Vercelu na Cloudflare Workers.
Vážně jsme o tom uvažovali, protože Next.js může na Cloudflare běžet prostřednictvím adaptačních vrstev. To by nám umožnilo zachovat velkou část stávající struktury aplikací.
Next.js na Cloudflare jsme však nepovažovali za ekvivalent Next.js na Vercelu. Next.js je nejtěsněji propojený s běhovým prostředím Vercelu, jeho systémem nasazování, chováním mezipaměti a funkcemi platformy. Na Cloudflare musí adaptér tyto předpoklady převést do jiného běhového prostředí.
To může fungovat dobře, ale pokud jsme chtěli stack zjednodušit, přidání další vrstvy kompatibility nám nepřipadalo ideální.
Svou roli hrály i nástroje. Téměř všechno ostatní, co jsme na frontendu vytvářeli, už používalo Vite: desktopové aplikace, rozšíření prohlížeče, SPA i další klientské projekty.
Next.js se výrazně posunul směrem k Turbopacku a Turbopack se velmi zlepšil. Nešlo nám o výhrady k výkonu. Větší rozdíl pro nás představoval ekosystém. Vite už bylo společným základem většiny našeho frontendového kódu, nabízelo širší ekosystém pluginů a umožňovalo sdílet více konfigurace mezi projekty.
Ponechání Next.js na Cloudflare by tedy vyřešilo část problému s hostingem, ale zachovalo by nesoulad mezi frameworky i oddělený ekosystém sestavovacích nástrojů.
Ustoupili jsme proto o krok zpět a podívali se, co jednotlivé typy aplikací skutečně potřebují.
Rozdělili jsme stack podle typu úloh
Jakmile jsme přestali hledat jednu univerzální náhradu za Next.js, architektura se výrazně zjednodušila.
Marketingové weby potřebovaly SSR, protože mají veřejné lokalizované stránky, metadata, kanonické adresy URL, soubory sitemap a návštěvnost z vyhledávačů.
Většina ostatních webových aplikací SSR nepotřebovala a mohla být jednoduše aplikacemi ve Vite používajícími TanStack Router.
Naše API vůbec nepotřebovala framework pro React a lépe jim vyhovovalo standardní běhové prostředí Node.js.
Výsledné rozdělení vypadalo takto:
Webové aplikace
Vite + TanStack Router
│
└── Cloudflare Workers
Marketingové weby
TanStack Start + SSR
│
└── Cloudflare Workers
API
Hono + tRPC
│
└── Railway / kontejnery Node.js
Pravidlo bylo jednoduché: používat SSR tam, kde přináší skutečnou hodnotu, běžnou SPA tam, kde ji SSR nepřináší, a backendové služby provozovat ve vhodném backendovém prostředí.
Z většiny webových aplikací se staly SPA
Migrace na SPA byly nejjednodušší částí.
Obzvlášť rychle se podařilo přesunout webovou aplikaci WebCatalog: připravili jsme základ náhrady ve Vite + TanStack Router, převedli rozhraní, změnili nasazování na Cloudflare Workers a ještě téhož dopoledne odstranili starou verzi v Next.js.
Stejný postup jsme zopakovali u webové aplikace Lexibird, nastavení účtu, vývojářské konzole, autentizace a administračních aplikací. Od vytvoření základu první SPA po odstranění poslední aplikace v Next.js uplynulo asi 19 dní.
U těchto aplikací je provoz na Cloudflare Workers záměrně jednoduchý. Vite sestaví statické soubory, Cloudflare je poskytuje a aplikační trasy mají jako záložní odpověď index.html. Žádné běhové prostředí pro SSR není potřeba, protože tu není nic, co by se muselo vykreslovat na serveru.
Náročnější bylo zachovat chování, které se kolem těchto aplikací postupně vytvořilo. Část autentizační logiky se musela přesunout ze serverových tras Next.js do API a starší verze desktopové aplikace WebCatalog stále závisely na původních koncových bodech, které jsme museli udržet funkční.
Přesunout uživatelské rozhraní v Reactu bylo snadné.
Zachovat stávající rozhraní a jejich chování bylo těžší.
Vyzkoušeli jsme Astro a po pěti dnech ho odstranili
S marketingovými weby to bylo složitější.
Naší první volbou bylo Astro. Pro webcatalog.io a lexibird.com vypadalo jako přirozená volba, protože oba jsou veřejné weby s velkým množstvím obsahu a Astro se dobře integruje s Cloudflare.
Začali jsme oba weby převádět a po pěti dnech jsme přestali.
Nešlo o jedinou zásadní chybu. Postupně se ale hromadily drobné nesoulady. Některé trasy potřebovaly při předvykreslování přístup k databázi, zatímco naše sestavovací prostředí záměrně nemělo přihlašovací údaje k produkční databázi. React islands komplikovaly sdílení stavu aplikace. Narazili jsme také na rozdíly mezi předvykreslenými trasami a trasami vykreslovanými za běhu i na určité potíže s nástroji v našem repozitáři.
Žádný z těchto problémů nebyl neřešitelný. Právě proto bylo nebezpečné pokračovat. Snadno jsme mohli strávit další týdny opravováním jedné věci za druhou.
Místo toho jsme implementaci odstranili a začali znovu.
AI agenti změnili ekonomiku tohoto rozhodnutí. Velká část mechanického převodu nespotřebovala týdny práce inženýrů, takže vynaložené náklady byly menší a bylo snazší přiznat si, že dáváme přednost jiné architektuře.
TanStack Start nám vyhovoval lépe
Migraci marketingových webů jsme zahájili znovu z původního kódu v Next.js, tentokrát s použitím TanStack Start.
Vyhovoval nám mnohem přirozeněji. V aplikacích typu SPA jsme už používali TanStack Router, takže směrování, loadery, parametry vyhledávání i navigace vycházely z podobných principů. Kód zůstal běžným Reactem a TypeScriptem a sestavovacím nástrojem bylo Vite.
lexibird.com jsme na TanStack Start převedli za den. Následoval webcatalog.io.
Výhodou nebylo to, že by TanStack Start zázračně vyřešil každý problém. Zapadal ale do směru, kterým se už ubíral zbytek našeho stacku.
V monorepozitáři na tom záleží. Sdílené sestavovací nástroje znamenají více znovupoužitelných pluginů a konfigurace, méně výjimek při ladění, méně přepínání kontextu pro vývojáře a méně konvencí specifických pro jednotlivé projekty, které musejí programovací agenti objevovat.
Cloudflare byl pro náš provoz mnohem levnější
Architektura byla jen jedním z důvodů, proč nám klesly náklady. Významnou roli hrály ceny Cloudflare, zejména cena přenosu dat.
Placený tarif Cloudflare Workers v současnosti účtuje především požadavky a využití CPU a za Workers nepřidává poplatky za přenos dat ani šířku pásma. (developers.cloudflare.com) Pro veřejné weby je to velmi důležité: požadavek může spotřebovat jen málo CPU, ale přesto přenášet HTML, JavaScript, obrázky, fonty a další soubory.
Cenový model Vercelu zahrnuje také položky a limity související se síťovým provozem, včetně Fast Data Transfer a Fast Origin Transfer. (vercel.com) Pro náš profil provozu vycházel Cloudflare výrazně lépe.
Úspory nevznikly jen změnou poskytovatele. Mnoho aplikací jsme také převedli na statické sestavení ve Vite, omezili zbytečné SSR, výslovněji nastavili ukládání do mezipaměti a přesunuli API do běhového prostředí, které jim lépe vyhovovalo. Ceny Cloudflare, zejména v oblasti přenosu dat, však významně přispěly k přibližně 80% snížení nákladů, které jsme zaznamenali.
Toto číslo platí pro naši konkrétní zátěž. Netvrdíme, že každá aplikace přesunutá z Vercelu na Cloudflare ušetří 80 %.
API nakonec skončila na Railway
API představovala samostatný problém.
Přestože šlo o projekty v Next.js, byla na něm už z velké části nezávislá. API WebCatalogu mělo přibližně 34 000 řádků, ale next/server importovalo jen sedm souborů. tRPC už používalo svůj adaptér Fetch a Inngest podporoval Hono.
HTTP vrstvu Next.js jsme nahradili Honem. Tato část byla překvapivě malá.
Těžší otázkou bylo, kde API provozovat. Samotné Workers se nehodily, protože API používají Node.js a nativní závislosti, například sharp.
Nejprve jsme vyzkoušeli Cloudflare Containers, díky nimž jsme mohli Vercel rychle opustit. Po nasazení do produkce jsme ale zjistili, že tento provozní model vyžaduje více ručního nastavování kapacity, než jsme tehdy chtěli.
API jsme tedy přesunuli ještě jednou.
Dnes běží na Railway jako běžné dlouhodobě provozované kontejnerové služby Node.js. CI (průběžná integrace) sestavuje obrazy Dockeru a odesílá je do našeho registru; Railway zajišťuje horizontální automatické škálování.
Ne všechno musí běžet na okraji sítě.
Odchod z Vercelu znamenal převzít více odpovědnosti
Nižší cena měla svou protiváhu.
Vercel a Next.js zajišťovaly mnoho infrastrukturních funkcí na vyšší úrovni. S TanStack Start a Workers jsme místo toho přešli k explicitnímu ukládání HTTP odpovědí do mezipaměti. Stránka se vykreslí, my vrátíme hlavičky pro ukládání do mezipaměti a Cloudflare odpověď uloží. Ukládání do mezipaměti v prohlížeči a na okraji sítě může mít odlišná pravidla, včetně chování stale-while-revalidate na okraji sítě.
Líbilo se nám, že o těchto věcech rozhodujeme výslovně. Když ale infrastrukturu spravujete přímo, odpovídáte i za své chyby.
Pár jsme jich udělali. Jedno produkční pravidlo mezipaměti omylem zachytilo odpovědi serverových funkcí TanStack specifické pro jednotlivé návštěvníky. Jiné nastavení mezipaměti špatně spolupracovalo se záložním směrováním SPA. Tyto chyby nám užitečně připomněly, že správnost migrace nelze prokázat jen z repozitáře. Součástí systému je i produkční infrastruktura.
AI agenti změnili způsob naší migrace
Většinu implementačních prací provedli programovací agenti, především Claude Fable 5 a Claude Opus 5.
Migrace mezi frameworky se pro agenty neobyčejně hodí, protože velká část práce má jasnou předlohu ve stávající aplikaci. Přesuň tuto trasu, zachovej tuto URL, nahraď toto API frameworku, ponech stejná metadata, spusť kontrolu typů, oprav chyby a porovnej výsledek se starou implementací.
Pro webcatalog.io jsme vedli přehled migrace, který práci rozděloval do fází, jako byly základy, produktové stránky, katalogové stránky, vyhledávání, blog, ceník, přesměrování, soubory sitemap a přepnutí na novou verzi. Místo zadání „převeď webcatalog.io na TanStack Start“ jsme agentovi dávali ohraničené úkoly s výslovně stanovenými požadavky na zachování chování.
Stávající aplikace se stala specifikací. Úkolem agenta bylo reprodukovat její chování v nové architektuře, nikoli znovu vymýšlet produkt.
Lidská práce se tak přesunula od ručního převádění kódu k otázkám, jako jsou: Má tato stránka skutečně používat SSR? Které chování je záměrné? Co se smí ukládat do sdílené mezipaměti? Které adresy URL musejí zůstat stejné? Kde má probíhat autentizace? Kdy je čas přestat se snažit zvolený přístup zprovoznit?
Výstupy jsme nadále kontrolovali, spouštěli sestavení a kontrolu typů a ručně ověřovali rizikovější oblasti, jako jsou autentizace, SEO (optimalizace pro vyhledávače), přesměrování a ukládání do mezipaměti.
Důležitou změnou nebylo to, že za nás AI psala kód. Důležité bylo, že díky AI zlevnilo experimentování.
Bylo mnohem snazší obhájit rozhodnutí vyzkoušet Astro a po pěti dnech ho odstranit. Přesunout API a pak se rozhodnout, že jim lépe vyhovuje jiná platforma, bylo méně bolestivé. Mechanická práce byla levnější, a tak jsme mohli změnit směr, když architektura nebyla správná, místo abychom pokračovali jen proto, že jsme do ní už příliš investovali.
Díky agentům už nebyla implementační kapacita tak vzácná.
O to důležitější byly architektura, stanovení omezení, kontrola a ověřování.
Z 80 % se stalo přibližně 90 %
Po migraci byly naše náklady na webovou infrastrukturu zhruba o 80 % nižší.
Pak jsme začali věnovat více pozornosti tomu, které požadavky se k aplikacím vůbec dostávají.
Nezanedbatelná část veřejného internetového provozu je automatizovaná: vyhledávače, AI crawlery, agenti, scrapery, skenery a méně přátelští boti. Část tohoto provozu je užitečná, část nikoli.
Protože aplikace běžely přímo za Cloudflare, zpřísnili jsme bezpečnostní pravidla, aby bylo možné nepřátelský automatizovaný provoz odmítnout na okraji sítě dříve, než spustí výpočty v aplikaci nebo práci s databází.
Celková úspora oproti původnímu řešení pak dosáhla přibližně 90 %.
Na výsledném snížení nákladů se podílelo několik věcí současně: nižší ceny Cloudflare, zejména za přenos dat; méně práce na serveru; vhodnější běhové prostředí pro naše API; explicitnější ukládání do mezipaměti; a menší počet zbytečných požadavků, které se vůbec dostaly k aplikacím.
Kde jsme skončili
V našem repozitáři už nezůstala žádná aplikace v Next.js. Většina našich webových aplikací jsou nyní SPA ve Vite + TanStack Router na Cloudflare Workers. webcatalog.io a lexibird.com používají TanStack Start na Cloudflare Workers, protože těmto webům SSR skutečně prospívá. Naše API používají Hono + tRPC na Railway a běží jako běžné kontejnery Node.js. Téměř všechny naše frontendové projekty nyní patří do ekosystému Vite.
Náklady na infrastrukturu klesly zhruba o 80 %, k čemuž výrazně přispěly mnohem výhodnější ceny Cloudflare pro náš provoz, zejména za přenos dat. Filtrování nepřátelského automatizovaného provozu na okraji sítě posunulo celkovou úsporu přibližně na 90 %.
Nejdůležitější je pro nás ale výsledek v oblasti architektury. Marketingové weby mají SSR, všechno ostatní, co může být SPA, je SPA, a API běží na plnohodnotných serverech, když je to jednodušší.
Začali jsme tím, že jsme se snažili zlevnit Vercel.
Skončili jsme zjištěním, že většinu architektury, za kterou jsme platili, vůbec nepotřebujeme.