# bunkill: making node_modules cleanup 2× faster on Bun

> npkill crawls on a machine full of projects. bunkill re-does it on Bun with parallel scanning, a fuzzy picker and sort-by-size. The speedup came from refusing to walk the same tree twice.

By Himanshu Maurya · 2026-07-04 · CLI · perf · 2 min
Canonical: https://codingstark.com/blog/shipping-bunkill/

`npkill` is the tool I reach for to reclaim disk, but on a laptop with a few hundred repos it takes its time. **bunkill** is my re-take on the same idea, built on Bun, and the goal was simple: same UX, roughly half the wall-clock.

### The speedup wasn't clever, it was structural

The obvious win is concurrency — but the real one was *not re-walking finished trees*. Once a directory contains a `node_modules`, there is nothing useful below it, so the scan stops descending there:

```ts
async function* walk(dir: string): AsyncGenerator<string> {
  const entries = await readdir(dir, { withFileTypes: true });
  const hit = entries.find((e) => e.isDirectory() && e.name === "node_modules");
  if (hit) { yield join(dir, "node_modules"); return; } // prune — don't recurse in
  await Promise.all(
    entries.filter((e) => e.isDirectory() && !e.name.startsWith("."))
           .map((e) => drain(walk(join(dir, e.name)))),
  );
}
```

That single `return` prunes the deepest, densest part of the tree. Pair it with Bun's fast `readdir`/`stat` and a bounded worker pool, and a full `$HOME` scan drops from ~40s to under 20s on my machine.

### The rest is UX

Interactive fuzzy search, sort by size (so the 3 GB `node_modules` you forgot about in 2023 floats to the top), and a dry-run so you can see what you're about to delete. It ships as an npm package — `bunx bunkill` and you never install anything.

The lesson I keep relearning with CLIs: the fastest code is the code you convince yourself not to run.
