How we migrated 30+ apps from Webpack to RSBuild by introducing the internal scripts package
Organization contextLink to heading: Organization context
Big platform for managing and creating establishments
- 10+ million users
- 35+ frontend apps
- 300+ developers from multiple companies
- 110+ components in the single design-system package
- Single Page Application based frontend architecture
- micro-frontends based on module-federation for common UI components (headers, sidebars, footers)
Problem spaceLink to heading: Problem space
When our FE platform team formed in May 2023, Webpack was the standard choice.
This work was done together with Jakub, Marcin, and Mateusz.
We tackled this step-by-step: abstract the complexity first, then
swap the bundler.
We needed module federation for our micro-frontend architecture, which ruled out Vite and modern alternatives.
The real problem wasn't Webpack itself, it was how we'd implemented starter-kit initially:
Scattered ownershipLink to heading: Scattered ownership
- build configs lived in 30+ repositories across independent stream-aligned teams
- each app, besides its dependencies, got many extra ones for "free" from us
- module-federation config in each repository
- security was not great, because stream-aligned teams were not interested in bumping bundler-related packages
Developer experience not good enoughLink to heading: Developer experience not good enough
- HMR took a dozen seconds, scaling with app size
- slow dev-server startup
- slow CI builds up to 4-5 minutes in medium-sized SPA
- devs were asking for migration to Vite (they didn't know at this time, that module-federation was bound to Webpack)
While smaller apps tolerated this setup, we knew it couldn't last. Every month made the problem worse, more drift, more tech debt.
Path forwardLink to heading: Path forward
We knew we needed a scripts package. Next.js does this, CRA did it, nothing revolutionary. But it would let us swap bundlers later without coordinating 30+ teams.
The pattern is simple:
- bundler dependencies and config live in the scripts package, not in apps
- consumer apps get clean commands, like: build, build-analyze, start, dev etc
- one minimal config file per app
init/migrateone-time commands make adoption painless for existing apps- new apps from our starter kit come with scripts package pre-configured
One package owns the complexity. Consumer apps stay focused on business logic.
With first release of the scripts package, we have solved the issue with scattered ownership. Yet, the issue with slow Webpack remains the same; we have to find an alternative.
Why RSBuildLink to heading: Why RSBuild
In Q1 2024, we evaluated two serious candidates: Vite and RSBuild.
Vite was temptingLink to heading: Vite was tempting
- Mature and battle-tested on production
- Massive ecosystem and community momentum
- Lightning-fast dev server with native ESM
- Developer favorite (our teams were already asking for it)
But Vite had a dealbreaker: module federation support was experimental and unstable. We couldn't risk our federated architecture on an alpha-quality plugin.
RSBuild checked every boxLink to heading: RSBuild checked every box
- compatibility with the most important Webpack APIs
- many built-in plugins
- significant performance improvements: 40% faster local builds, 30% faster CI (tested on our starter kit)
- built-in fully compatible module-federation plugin
- modern DX with snappy HMR
- faster builds and dev server startups than Vite
You can think of RSBuild as modernized, fast version of CRA, batteries-included.
We were early adopters. RSBuild was young (pre-1.0 at the time), with a smaller community than Vite. But the module federation stability and Webpack compatibility made it the pragmatic choice for our constraints.
Migration processLink to heading: Migration process
After initial migration to the scripts (still Webpack based) when the clients:
- initialized the scripts package to their applications
- with the help of the
init(one-time command) removed the config and dependencies of tooling - had proper setup in config file of scripts
- had proper commands like
build-analyzeto gather before/after metrics for next migration to RSBuild
We were ready for another, less painful migration, to migrate platform from Webpack to RSBuild.
Phase 1: Experimental Rollout (Q1-Q2 2024)Link to heading: Phase 1: Experimental Rollout (Q1-Q2 2024)
While RSBuild was still pre-1.0, teams could opt-in with two steps:
- Bump scripts package (minor version—nothing breaking)
- Set
bundler: "experimental-rsbuild"in the config file
That's it. No code changes. No refactoring. No coordination.
Fortunately for us, there were 4-5 Teams eager to test RSBuild on the production. It gave us valuable insights and data.
We kept it experimental since RSBuild was on 0.x versions. The RSBuild team was working toward stability, which arrived in August 2024 with their 1.0 release.
Phase 2: Default MigrationLink to heading: Phase 2: Default Migration
After RSBuild 1.0, we made it the default bundler. For the remaining apps, migration was even simpler:
- bump the scripts version to the new major
- removal of
bundlerfield from the config file
Total migration time 5 minutes, it took them more to gather the data, before/after for:
- build execution time
- bundle size
- dev-server startup time
The way how smooth it was, I can't imagine the blood and tears of manual per-repo migrations without scripts.
OutcomesLink to heading: Outcomes
PerformanceLink to heading: Performance
We don't have comprehensive data from all teams, but the signal was clear:
- local builds: 40% faster
- CI builds: 30% faster
- dev server startup: 0.3s from around 2-3s
- biggest app (88000 LoC) CI build 30% faster
- biggest app (88000 LoC) local build around 50% faster
Developer experienceLink to heading: Developer experience
- instant HMR
- local dev-server readiness below 1s even in bigger apps
- unified tooling for bundle analysis under 1 command (RSDoctor and Webpack bundle analyzer)
- lack of Vite is no longer a problem 😉
- faster onboarding for devs, less dependencies to reduce cognitive load
Strategic impactLink to heading: Strategic impact
- ~20-30% reduction in CI compute time across the platform
- zero rollbacks for phase 2 default migration
- one package to update instead of 30+ repos with stale dependencies
- we already added
experimental-viteoption (Vite with Rolldown on the way) - time saved NOT doing manual migrations (30+ apps x 8-16 hours)
What could go betterLink to heading: What could go better
Initial migration data gatheringLink to heading: Initial migration data gathering
Problem: When teams migrated to scripts (Webpack-based), we didn't force them to capture before/after metrics.
The impact: We have strong anecdotal evidence but weak quantitative proof. This makes it harder to demonstrate ROI and justify future platform investments.
Potential solution: Make metrics collection part of the migration script itself. Auto-capture build times, bundle sizes, and CI duration before/after—store in a central dashboard.
CI metrics should be infrastructure-levelLink to heading: CI metrics should be infrastructure-level
Problem: We asked teams to manually gather CI metrics. Most didn't. The few who did used inconsistent methods (different runners, state of app etc).
Potential solution: Build CI analytics into our infrastructure from day one. Aggregate build times, failure rates, and compute costs automatically.
This allows to track the progress and ongoing measurements, what has changed after few months.
Scripts should've existed from day oneLink to heading: Scripts should've existed from day one
Problem: We launched starter kit with inline Webpack configs, then introduced scripts later.
Potential solution: Abstract early. Maintaining a shared package is easier than coordinating 30+ repo migrations.
Lack of ROI calculation frameworkLink to heading: Lack of ROI calculation framework
Problem: We calculated rough impact estimates (240-480 hours saved, 20-30% CI reduction) but had no systematic way to track ongoing value or convert technical wins to financial terms.
Potential solution: Build ROI instrumentation into platform tooling. Auto-track:
- hours saved per team weekly
- infrastructure cost changes,
- deployment frequency improvements
Generate some reports on demand.