At first, when I saw Bun’s benchmarks, I was amazed. HTTP servers ranking at the top of framework performance lists (like Elysia) were very appealing. Installing npm packages much faster? I would easily trade pnpm for it.
When I read the article Bun hype. How we learned nothing from Yarn here in Dev.to, I was a bit outraged. But today, I tend to agree with it.
As I mentioned in this tweet, I ended up getting somewhat frustrated with how many packages unrelated to the runtime—such as libraries for Amazon S3 and SQLite—were included.
From my experience, I felt that there was a lack of investment in developer experience. Aside from the issues I had with Node/TypeScript eight years ago, the following things had never happened to me while using Node in VS Code—at least until now, in version 1.21.
(A little spoiler here: I ended up using Bun and Node together in the end. Check out the last section to see how I defined this.)
Debugging
bun --inspect
did not respect breakpoints unless there was a port listener or some function "hanging" in the runtime. --inspect-brk
and --inspect-wait
didn’t work either.
The launch.json
file described in the official Bun VS Code extension did work, especially when changing the Bun location to node_modules/.bin/bun
. However, this caused serious conflicts when using Volta.sh.
The debugger’s behavior fluctuated intermittently between launch.json
and “Run file,” particularly when switching between Remote/Tunnel and local development.
The Web Debugger also had issues, especially failing to respect promise waits when stepping through await
lines. It would get completely lost.
Even with sourcemaps, transpiled JavaScript code did not correctly map to the TS files in the same monorepo.
Open ports were also left hanging, so I had to create a task in tasks.json
to use as a postDebugTask
in launch.json
to kill processes with open ports.
{
"version": "2.0.0",
"tasks": [
{
"label": "kill port",
"type": "shell",
"command": "lsof -ti:3000 | xargs kill -9",
"problemMatcher": []
}
]
}
Test runner
Bun's test runner seems excellent, but it is still not integrated with VS Code's Test Explorer like Jest and other runners.
Since it doesn’t appear in the Test Explorer, it’s difficult to visualize and run only the tests that failed in the last run within VS Code.
It does have the --filter
option in the CLI, but you would have to manually fetch the failed tests from the last run and specify them manually. Alternatively, you would have to do the same manually with .skip()
.
Besides that, debugging with the test runner doesn’t seem to be an option, especially given the already poor debugging experience.
Additionally, the test runner has some bizarre assertion errors. I have two packages in a monorepo that reference a third package in their package.json
. Even though they are on the same version, the test runner treats this package as if it were different versions. A simple class instance assertion (instanceof
) behaves as if the same class had different constructors, causing the assertion to fail.
I believe this happens due to the way it handles monorepos. When running with the test runner, it likely has two different module resolutions—one looking at node_modules
and another resolving modules within the runtime in memory.
I ended up solving this in a dirty way. Instead of using instanceof
, I compared obj.constructor.toString() === ClassName.toString()
.
Monorepos & multi-root workspaces
I know that monorepos are not very common, except for developers who publish multiple packages. But Bun falls short when it comes to monorepos.
Many times, the build and install processes got confused and caused conflicts with the repository’s packages—something I have never experienced with npm.
Besides the debugging and sourcemap issues I mentioned earlier, these are completely ignored in monorepos.
Multi-root workspaces were also a problem since VS Code does not respect the launch.json
from a single root unless you create a .vscode
folder for the entire workspace. Although this is an issue with VS Code rather than Bun, it still results in a poor experience because Bun users are forced to rely on launch.json
.
Build
(Only for those who actually need to transpile Bun to JS and publish it to an NPM package, for example.)
Although Bun's build process is very fast, it accepts things that tsc
would never allow. It also does not properly respect tsconfig.json
, relying much more on flags passed either via CLI or as arguments in Bun.build()
.
By default, Bun fills the bundle with all external modules from node_modules
, and the external
and packages
flags are quite confusing at first—sometimes even conflicting with each other.
Final definitions
In the end, I ended up formalizing some definitions on where and when to use Bun in the projects I work on.
-
Libs: Use
tsc
to transpile and publish (this removes code from the bundle and maintains compatibility with CJS). -
Debug: Use Node with sourcemaps, as this allows proper debugging of TypeScript in monorepos (and even in linked packages inside
node_modules
) in VS Code. - Servers/Applications: Use Bun because it is faster and more efficient at runtime.
-
Package manager & scripts: Use Bun (although using
pnpm
is still a good option due to symlinks and reduced disk space usage for development).
Top comments (1)
thanks for the article! Keep sharing your knowlege with us!