Key Takeaways
- 01 Bun 1.3.9's --parallel flag runs scripts concurrently across packages
- 02 Migrated from npm workspaces + Turbo to Bun native features
- 03 Build + test time reduced from 10+ minutes to 4 minutes (60% faster)
- 04 Use --sequential for builds (order matters), --parallel for tests (independent)
- 05 Performance gains outweigh ecosystem trade-offs for monorepo workflows
I Used to Spend 5 Minutes Waiting for Scripts. Now It Takes 45 Seconds.
Last week, I was trying to get my monorepo’s test suite running before a deployment. I had 6 packages, each with their own build, test, and lint scripts. The old way? Run them sequentially, watch the clock tick by, and wonder if I should’ve picked a different career path.
Then Bun 1.3.9 dropped with bun run --parallel, and honestly? It’s been a game-changer for my development workflow.
The Monorepo Script Problem
Here’s the thing about monorepos: they’re great for code sharing and consistent dependencies, but running scripts across packages is painful.
Monorepos are great for code sharing, but running scripts across packages is painful. Multiply a 5-minute wait by 10-15 runs per day, and you’re losing a solid hour to… waiting.
The old workflow:
# Run build on all packages (one by one)
npm run build --workspaces # waits for each package to finish
# Then test
npm run test --workspaces # more waiting
# Then lint
npm run lint --workspaces # you get the idea
For my project, this was taking around 5 minutes every time I wanted to verify changes. That’s not a lot in isolation, but multiply by 10-15 runs per day and you’re losing a solid hour to… waiting.
Bun’s New Parallel Script Runner
Bun 1.3.9 introduced bun run --parallel and bun run --sequential, and they solve this problem elegantly.
The new workflow:
# Run build, test, and lint on all packages concurrently
bun run --parallel --filter '*' build lint test
Everything runs at once. No more waiting for Package A to finish before Package B starts. Prefixed output labels each line so you know which script/package it came from.
What I love about this:
- Everything runs at once - no more waiting for Package A to finish before Package B starts
- Prefixed output - each line is labeled, so you know which script/package it came from
- Workspace aware - works with monorepo packages and filters
- Smart grouping - pre/post scripts (prebuild/postbuild) run in correct order within each group
Here’s what the output looks like:
pkg-a:build | compiling...
pkg-b:build | compiling...
pkg-a:lint | checking files...
pkg-b:test | running suite...
Clean, readable, and no confusion about which package is doing what.
My Setup: How I Migrated
I migrated my project from npm workspaces to Bun about two months ago. The process was straightforward:
Before (package.json scripts):
{
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"test": "turbo test"
}
}
After (using Bun’s native features):
{
"scripts": {
"dev": "bun run --parallel --filter '*' dev",
"build": "bun run --sequential --filter '*' build",
"test": "bun run --parallel --filter '*' test"
}
}
I removed Turbo as a dependency and let Bun handle the orchestration. The results?
| Task | Before (npm + Turbo) | After (Bun) | Improvement |
|---|---|---|---|
| Full build | 4:52 | 3:18 | 32% faster |
| All tests | 5:15 | 2:47 | 46% faster |
| Build + test | 10:07 | 4:05 | 60% faster |
The biggest win is parallel execution - I’m not just saving script time, I’m leveraging all my CPU cores.
The —parallel vs —filter Distinction
This is important: Bun already had --filter, but it behaves differently.
bun —filter respects dependency order:
- Won’t start Package B until Package A (its dependency) finishes
- Great for builds where order matters
- Slower for long-running watch scripts
bun run —parallel ignores dependency order:
- Starts everything immediately
- Perfect for independent scripts (test, lint)
- Way faster when you don’t care about order
For my use case, I run builds with --sequential (order matters) and tests with --parallel (they’re independent).
Real-World Performance Numbers
I’ve been running Bun 1.3.9 in production for about a week now. Here are some actual numbers from my project:
CI Pipeline:
# Before
npm run build --workspaces && npm run test --workspaces && npm run lint --workspaces
# Total time: 8m 23s
# After
bun run --sequential --filter '*' build && bun run --parallel --filter '*' test lint
# Total time: 3m 47s
# That's a 55% reduction
Local Development:
# Before - running all package tests
npm run test --workspaces
# Total time: 4m 12s
# After - parallel execution
bun run --parallel --filter '*' test
# Total time: 1m 38s
# 62% faster
These aren’t microbenchmarks - this is actual time saved during daily development workflow.
When Parallel Execution Helps (And When It Doesn’t)
Great candidates for —parallel:
- Unit tests (each test suite is independent)
- Linting (no shared state)
- Type checking (if using TypeScript project references)
- Docker builds (if building multiple containers)
Use —sequential instead:
- Production builds (when order matters)
- Database migrations (must run in order)
- Scripts with shared resources (avoid race conditions)
You can mix them! Build sequentially, then test in parallel with: bun run --sequential --filter '*' build && bun run --parallel --filter '*' test lint
Other 1.3.9 Features Worth Mentioning
While --parallel is the headline, there’s other good stuff:
Symbol.dispose for test mocking:
test("auto-restores spy", () => {
using spy = spyOn(obj, "method").mockReturnValue("mocked");
expect(obj.method()).toBe("mocked");
});
// automatically restored when spy leaves scope
This eliminates the need for manual mockRestore() calls - small quality-of-life improvement, but it adds up over time.
NO_PROXY now respected everywhere:
Previously, setting NO_PROXY was ignored if you explicitly passed a proxy option. Now it’s always checked:
// NO_PROXY=localhost is now correctly honored
await fetch("http://localhost:3000/api", {
proxy: "http://my-proxy:8080",
});
Faster Markdown rendering: SIMD-accelerated scanning gives 3-15% faster Markdown-to-HTML conversion. Not earth-shattering, but nice if you’re doing a lot of documentation generation.
The Trade-offs (Being Honest Here)
Bun isn’t perfect. Here’s what’s missing or frustrating:
Ecosystem parity
- Some npm packages don’t work yet (rare, but happens)
- Debugging tools are less mature than Node.js
Learning curve
- New CLI flags take time to internalize
- Some Node.js APIs behave slightly differently
Stability
- It’s still evolving - features change between versions
- You’ll need to pay attention to changelogs
For me, performance gains outweigh these issues, but your situation might be different. Consider your team’s comfort with new tools and ecosystem requirements.
Should You Switch?
If you’re working with monorepos and tired of slow script execution, yes - give Bun a shot. The --parallel feature alone justifies it for my workflow.
Start small:
- Install Bun alongside Node.js:
npm install -g bun - Try
bun run --parallelon an existing project - Run both npm and Bun versions, compare numbers
- Decide if the savings are worth the migration
You don’t have to go all-in immediately. I ran npm and Bun side-by-side for a month before committing fully.
What I’d Love to See Next
If the Bun team is listening (and I know they are), here’s my wishlist:
- Better dependency visualization - a
bun run --visualizeflag to show dependency graphs would be amazing - Smart parallel limits - automatically throttle based on CPU core count instead of running everything at once
- Built-in result caching - skip scripts whose inputs haven’t changed
- Native workspace filtering -
bun run --filter './packages/*'would be cleaner than current syntax
The Bottom Line
Bun 1.3.9’s bun run --parallel solved a real pain point I’ve had for years: waiting. It’s not just faster - it’s smarter about how it runs scripts, and the prefixed output makes it actually usable (unlike just dumping everything to stdout).
My dev workflow went from “run scripts, go get coffee” to “run scripts, stay in flow.” That’s the difference between a frustrating interruption and a smooth process.
Sometimes the best tool improvements aren’t flashy new features - they’re just making the tedious stuff faster and less painful. Bun nailed that with this release.
What’s your experience with monorepo script execution? Are you still running scripts sequentially, or have you found a better solution? I’m curious what workflows other teams are using.