
Achieving 87x Faster Server-Side Action Builds: Migrating from Rollup to Rolldown
Posted on
Introduction
I'm @kazupon from the Developer Experience team.
PLAID offers a product called KARTE that enables continuous improvement by providing popup dialogs to users visiting their website through campaigns, analyzing visualized user behavior, and implementing measures. We provide an editor called "Flex Editor" for editing these campaigns.
We've achieved a massive performance improvement in KARTE's no-code editor Flex Editor by accelerating action build times up to 87x, dramatically enhancing the user experience. In this post, I'll walk you through our journey of migrating from Rollup to Rolldown for server-side build optimization.
This article is a follow-up to our previous post:
How We Made Rollup Build Times 10x Faster to Improve Preview Display
Actions are applications that allow editing action templates (internally called "campaign templates") through KARTE's admin panel. They're edited using "Flex Editor", a no-code editor that builds JavaScript code server-side for execution as marketing campaigns on websites.
The Challenge: Build Times Hurting UX
When users save edited actions in Flex Editor, they're built server-side for web delivery. However, lengthy build times were causing slow server responses, forcing users to wait and degrading the overall experience.
According to our DataDog metrics, action-compiler build times ranged from 5-10 seconds, sometimes exceeding 20 seconds.
The primary culprit? JavaScript-based bundlers running server-side.
As mentioned in our previous article, action-compiler internally uses Rollup, a JavaScript-based module bundler. Module bundling is inherently CPU-intensive. While modern JavaScript engines like V8 and SpiderMonkey can achieve near-native performance through internal optimizations, they're not ideal for CPU-heavy tasks.
Bundlers like Rollup, which parse massive amounts of JavaScript code and perform tree-shaking on module graphs, suffer from JavaScript's event-loop-based single-threaded architecture when it comes to CPU-intensive operations.
While Web Workers or Node.js worker threads (node:worker_thread
) can provide parallelization, Rollup's architecture wasn't designed with parallel bundling optimization in mind.
Additionally, to maximize action performance on websites, we use Terser (a code minifier) for minification. Since minification involves static code analysis followed by compression, it adds even more CPU overhead on top of Rollup's bundling process.
The Solution: Migrating to Rolldown
To tackle server-side action build times, we decided to migrate from Rollup to Rolldown.
Rolldown is a high-performance JavaScript bundler written in Rust with Rollup API compatibility, promising significant bundling performance improvements.
Rolldown addresses Rollup's performance challenges through several innovations. Bundler operations like JavaScript module parsing and module graph tree-shaking are accelerated using crates from the Oxc project and Rust-side multi-threaded parallelization.
While maintaining API compatibility for existing Rollup plugins, Rolldown's architecture enables plugins to run in parallel on the JavaScript side through Workers, coordinating with the Rust side.
At the time of writing, Rolldown is at v1.0.0 beta.32. When we evaluated migration (January 2025), we found:
- Core bundler implementation was nearly complete, with only edge cases and bug fixes remaining, indicating stability
- Major Rollup plugins were compatible with Rolldown, maintaining compatibility
- I verified bundling behavior in my OSS projects, confirming proper operation and faster bundling than Rollup
- E2E tests confirmed bundled JavaScript modules worked correctly
These factors and verification results led us to adopt Rolldown for the Flex Editor project.
Migration Approach
Thanks to Rolldown's Rollup API compatibility, migrating my OSS projects and packages within Flex Editor was straightforward. However, action-compiler presented unique challenges.
As explained in our previous article, we have two types of action-compiler:
- For Browser: Used for action preview functionality in Flex Editor's GUI application
- For Server (Node.js): Used server-side to build actions for deployment as campaign applications on websites
Both environments use Rollup to build actions into JavaScript code. It's a single-source package optimized for performance in each environment.
The browser-oriented action-compiler pre-bundles JavaScript modules that actions depend on to improve editor preview UX, as mentioned in our previous article. It even bundles Rollup itself into action-compiler for optimized build speed.
The server-oriented action-compiler bundles with Rollup and minifies with Terser (via Rollup plugin) to maximize action performance. Like the browser version, action-compiler itself is bundled, but unlike the browser version, it simply imports Rollup from node_modules rather than bundling Rollup into action-compiler.
While each environment uses Rollup appropriately with environment-specific configurations, maintaining a single source while using Rolldown only server-side required careful planning. We wanted to avoid code complexity and increased maintenance cognitive load from implementing Rolldown-specific code.
Leveraging API Compatibility for Rolldown Migration
We achieved server-side action-compiler Rolldown migration by leveraging API compatibility and using a bundler to rewrite Rollup imports in server-side implementation code.
Specifically, we implemented a Rollup plugin that replaces import { rollup } from 'rollup'
with import { rolldown as rollup } from 'rolldown'
.
This gets a bit complex, but action-compiler is a JavaScript package that compiles (bundles) actions into JavaScript code using Rollup. As mentioned, we bundle action-compiler itself for browser and server optimization. (In other words, we're bundling a bundler with a bundler!)
Here's the relevant excerpt from action-compiler's bundler configuration file (rolldown.config.js) - note that action-compiler's bundler is now Rolldown:
// ...
/**
* Replace import syntax with rolldown when using in Node.js environment
*/
if (isNode && useRolldown) {
plugins.push({
name: 'replace-import-syntax',
transform(code, id) {
if (id.endsWith('src/prod.ts') || id.endsWith('src/dev.ts')) {
return code.replace(/import\s+{\s+rollup\s+}\s+from\s+['"]rollup['"]\s*;?/gm, () => {
return `import { rolldown as rollup } from 'rolldown';`
})
}
}
})
}
// ...
That's it!
(The implementation uses simple regex string replacement rather than AST-based static analysis since we're only transforming two files.)
isNode
determines if the bundle target is Node.js - true
for Node.js (server-side), false
for browser. useRolldown
determines whether to use Rolldown server-side - true
uses Rolldown, false
continues using Rollup.
When both isNode
and useRolldown
are true
, the bundler registers a plugin that rewrites import { rollup } from 'rollup'
to import { rolldown as rollup } from 'rolldown'
in action-compiler's code.
By setting these flags appropriately (both true
) when bundling action-compiler for server use, the code is transformed to use Rolldown. Using the bundler eliminated the need for Rolldown-specific code in action-compiler itself.
Rolldown Server-Side Build Performance
With Rollup successfully replaced by Rolldown in server-side action-compiler, we measured build performance using the following configurations and environment.
Metrics
- Rollup[1] (no minification)
- Rolldown[2] (no minification)
- Rollup + Terser (
@rollup/plugin-terser
[3]) - Rolldown + SWC (
rollup-plugin-swc3
[4]) - Rolldown + built-in minifier (
oxc-minify
[5])
For comparison, we included cases without minification and the Rollup + Terser combination. Since oxc-minify
was in WIP status during our implementation and might not function correctly for actions in the Flex Editor beta production environment, we also measured performance with Terser and SWC available as Rollup plugins.
Environment
Performance measurements were conducted on:
- Machine specs:
- Model: MacBook Pro 14-inch, 2021
- Chipset: Apple M1 Max
- Memory: 64GB
The action structure for build performance testing:
- Build target action structure:
- index.tsx
- App.svelte
- gen.ts
- customScript.ts
- karte-template.json
- props.json
- variablesQuery.json
- localVariablesQuery.json
The action UI represents a campaign like this:
While we'll skip the detailed file contents, when editing and saving in the editor, action file code is serialized into JSON with this structure and sent server-side for building:
For these performance measurements, we used server-oriented action-compiler to build this action code.
Build Performance Results
Here are our performance measurement results:
Build Times
Configuration | Max Build Time | Min Build Time | Average Build Time |
---|---|---|---|
Rollup (no minification) | 560.596119 ms | 205.13774 ms | 236.0695421 ms |
Rolldown (no minification) | 112.801283 ms | 23.01124 ms | 33.42461761 ms |
Rollup + Terser (@rollup/plugin-terser ) |
2424.394387 ms | 959.403731 ms | 1149.732976 ms |
Rolldown + SWC (rollup-plugin-swc3 ) |
235.093658 ms | 106.845099 ms | 125.2551525 ms |
Rolldown + built-in (oxc-minify ) |
143.829239 ms | 28.230994 ms | 40.44968284 ms |
Without minification, Rolldown clearly outperforms Rollup. Comparing Rolldown's minimum build time to Rollup's maximum shows approximately 24x improvement, with an average 9x performance boost. These results confirm Rolldown's acceleration benefits for action builds.
With minification, Rolldown builds still outperform Rollup + Terser. Comparing Rolldown + SWC's minimum build time to Rollup + Terser's maximum shows about 23x improvement, averaging 9x faster. With Rolldown's built-in minifier, we achieved an incredible 87x improvement at minimum and 29x on average.
These results demonstrate that while the SWC minifier via rollup-plugin-swc3
provides acceleration, using Rolldown's built-in oxc-minifier
delivers overwhelming build performance gains.[6]
File Sizes
Configuration | Minified Size | Gzipped Size |
---|---|---|
Rollup + Terser | 92.15 kb | 31.38 kb |
Rolldown + SWC | 114.73 kb | 39.11 kb |
Rolldown + built-in minifier | 122.09 kb | 40.35 kb |
File size results revealed interesting insights for action builds.
Various Rolldown minification configurations resulted in approximately 20-25% larger bundle sizes compared to Rollup + Terser. This is because Svelte runtime code used by actions is pre-bundled with Rolldown during action-compiler bundling.
Rolldown's built-in minifier oxc-minify
has several open issues regarding minification optimization. Our analysis of the built code confirmed issues matching these reported problems.
The reason for pre-bundling Svelte runtime code is to accelerate Flex Editor preview display, as explained in our previous article.
Comparing minification performance between SWC (rollup-plugin-swc3
) and oxc-minify
, SWC showed higher compression rates.
Trade-offs with Rolldown Adoption
Performance measurements of Rolldown-powered server-side action-compiler showed build times exceeded expectations, while bundled action JavaScript file sizes increased 20-27% compared to Rollup + Terser.
Our goal was to accelerate server-side action build times and improve the save experience in Flex Editor. After team discussion, we decided to adopt Rolldown with its built-in minifier (oxc-minifier
) for server-side build acceleration.
The trade-off: JavaScript action files delivered will be larger than Rollup-bundled versions. However, new actions from Flex Editor have been reduced to 1/10th the size of current production actions through architecture renewal. Server-side build acceleration with Rolldown not only improves DX through reduced server infrastructure costs but also enhances end-user UX when saving in Flex Editor. The dual benefits of DX and UX improvements drove our decision to adopt Rolldown with built-in oxc-minifier
.
Production Build Performance
At the time of writing, we've deployed Rolldown-powered server-side action-compiler to production as a beta for select customers using Flex Editor. We continuously monitor server-side action-compiler performance with DataDog. Here's the graph after introducing Rolldown-powered action-compiler to production:
In production, the system runs in a Kubernetes (k8s) container environment. Pre-migration action-compiler build times of 5-10 seconds have dramatically reduced to hundreds of milliseconds. The accelerated server-side build times when editing and saving actions in Flex Editor result in faster server responses and noticeably improved editor UX.
Future Outlook
Towards Rolldown's Official Release
While Flex Editor currently uses Rolldown beta, we'll continue updating toward the official release. We hope the file size issues mentioned earlier will be resolved by Rolldown's official release. Vite is also progressing with Rolldown integration through rolldown-vite, and we've verified up to 16x acceleration in the Flex Editor project.
Technical Future Prospects
With Rolldown adoption, we've dramatically improved build time challenges. Our next step involves exploring AST-based editor generalization. While currently using Svelte-based AST, we're considering migration to Vapor's AST/IR base using inclusion-vapor and expansion to other products.
Conclusion
This article detailed our journey migrating from Rollup to Rolldown, dramatically improving server-side build times and enhancing product UX.
Despite being in beta, Rolldown's core functionality is nearly complete and ready for production use. JavaScript/TypeScript projects can especially benefit from significant build time reductions, improving DX and accelerating CI/CD deployment cycles.
I hope this article inspires you to consider Rolldown for your own projects.
If you're interested in working on projects while contributing to OSS, please check out our careers page.
v4.35.0 ↩︎
v1.0.0-beta.32 ↩︎
v0.4.4 ↩︎
v0.12.1, using minify feature ↩︎
Minifier provided by the oxc project ↩︎
This performance measurement compares only SWC and Rolldown's minifier under Rolldown. As shown in various minification benchmarks, performance varies depending on the code being minified. ↩︎