2026-04-15 · 5 min
Bun Shell vs Node child_process — kapan pakai yang mana
Bun 1.0+ ship dengan $ Shell API yang membuat shell scripting di JavaScript jauh lebih bersih:
import { $ } from 'bun';
await $`ls -la`;
await $`echo "hello" > greeting.txt`;
const output = await $`cat greeting.txt`.text();
Compare dengan Node child_process equivalent:
import { exec } from 'child_process';
import { promisify } from 'util';
const execP = promisify(exec);
await execP('ls -la');
const { stdout } = await execP('cat greeting.txt');
Bun Shell jauh lebih ringkas. Tetapi ada beberapa scenario di mana Node child_process tetap pilihan terbaik. Ini breakdown-nya.
Pakai Bun Shell ketika…
1. Script lokal yang Anda jalankan sendiri — task automation, build scripts, CI tooling. Syntax bersih, error handling built-in (await $ throws kalau exit non-zero).
2. Anda butuh stream stdout secara real-time — Bun Shell support await $….lines() yang yields per-line, jauh lebih mudah daripada Node stream parsing.
3. Multi-command pipelines — Bun Shell support glob expansion, env variable interpolation, dan pipe dengan syntax yang lebih intuitif:
await $`find . -name "*.ts" | wc -l`;
4. Anda comfortable dengan Bun runtime — Bun Shell tidak akan portable ke pure Node project. Jadi pertimbangkan dependency lock.
Pakai Node child_process ketika…
1. Code Anda harus run di Node atau Deno juga — child_process di Node, mirip API di Deno (lewat compat). Bun Shell tidak portable.
2. Anda perlu fine control atas stdin/stdout/stderr — beberapa use case (interactive prompt, custom protocol over pipes) lebih mudah dengan spawn() API yang lebih granular daripada Bun Shell.
3. Long-running child process dengan event-driven communication — Bun Shell optimized untuk one-shot commands. Untuk daemon, worker pool, atau supervised process yang butuh restart, spawn() dengan event listeners lebih appropriate.
4. Cross-platform shell semantic — Node child_process dengan shell: false punya predictable behavior across OS. Bun Shell mengasumsikan POSIX-ish behavior; di Windows native bisa surprise.
Performance comparison
Saya benchmark sederhana — execute echo "test" 1000 kali di loop:
| Method | Total time | Per-op |
|---|---|---|
Bun Shell $ | 2.1s | 2.1ms |
| Bun spawn (Bun-native) | 1.4s | 1.4ms |
Node exec (promisified) | 4.8s | 4.8ms |
Node spawn | 2.7s | 2.7ms |
Bun Shell adalah middle ground — lebih cepat dari Node exec, tapi 50% slower dari Bun native spawn. Untuk script yang call sub-process puluhan kali, prefer Bun native API atau Node spawn.
Real-world example
Saya migrate satu deploy script saya dari Node child_process ke Bun Shell awal tahun ini:
Before (Node, ~25 lines):
import { exec } from 'child_process';
import { promisify } from 'util';
const execP = promisify(exec);
async function deploy() {
const { stdout: branch } = await execP('git branch --show-current');
if (branch.trim() !== 'main') throw new Error('not on main');
await execP('git pull');
await execP('bun run build');
await execP('wrangler deploy');
}
After (Bun, ~10 lines):
import { $ } from 'bun';
async function deploy() {
const branch = (await $`git branch --show-current`.text()).trim();
if (branch !== 'main') throw new Error('not on main');
await $`git pull && bun run build && wrangler deploy`;
}
Lebih readable, less imports, error handling lebih built-in. Tetapi untuk script ini sustainable karena cuma jalan di lokal saya dengan Bun installed.
Verdict
Untuk most script local automation: Bun Shell. Untuk production code yang harus portable: Node child_process. Trade-off antara DX dan compatibility.
Benchmark dilakukan di MacBook Air M2 (16GB), macOS 14. Per-op timing dapat berbeda di setup berbeda.