← Orieb AlzyuotHire me
By Orieb Alzyuot · 17 October 2026 · 9 min read

Why I Wrote Two of My Dev Tools in Go (and Kept the Rest in Node)

I build front ends for a living: Angular, React, Vue, Svelte. Most of my developer tools are Node.js, because that is where my users already are. This month I wrote two of them in Go. I measured both, and some of what I expected did not hold up. This is what I found.

The two tools

Both run on every pull request in my projects.

The concurrency code, as written

Both tools scan files in parallel. This is the real worker pool from i18n-leak, lightly trimmed:

workers := opts.Workers
if workers <= 0 {
    workers = runtime.NumCPU()
}
if workers > len(files) {
    workers = len(files)
}

results := make([][]rules.Finding, len(files))
errs := make([]error, len(files))
jobs := make(chan int)
var wg sync.WaitGroup
for w := 0; w < workers; w++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        for i := range jobs {
            results[i], errs[i] = scanFile(root, files[i], opts.Allow)
        }
    }()
}
for i := range files {
    jobs <- i
}
close(jobs)
wg.Wait()

Each worker writes to its own slot in results, so no lock is needed. The output is sorted by file, then line, then rule afterwards, so the report is the same on every run, whatever the worker count. That determinism is what lets CI diff the report.

I had written a longer, invented version of this in my first draft of this article. I deleted it, because it was not the code that ships.

What I measured

Windows 11, Git Bash, Go 1.27.0, Node 24.21.0. Cold start is the median of 10 runs of --help or node -e 0, which includes process start-up and any shell overhead.

i18n-leak (Go)style-dups (Go)Bare Node
Binary or runtime size4.78 MB (one file)4.69 MB (one file)node.exe 93.6 MB, before any packages
Cold start, median of 10103 ms141 ms149 ms (node -e 0)
Scan, one run2,000 files in 295 ms, 8,500 findings2,400 files (20 MB) in 4,805 ms, 96,000 findingsnot measured

Three things stand out:

Cold start is not a Go win on this machine. Both Go tools start in about the same time as an empty Node script. On Windows, process start-up dominates. My draft claimed Node took 300 ms and Go 5 ms. That was never measured, and it is wrong. I removed it.

The real difference is what you have to install. The Go tools are one file each, about 4.7 MB, with no runtime and no node_modules. The machine that runs them needs nothing else. Node is 93.6 MB before any packages. For a backend team that does not run JavaScript, that difference is the whole argument.

The style-dups timing is mostly output. Of its 4.8 seconds, a large part is writing a 25.8 MB JSON report and 96,000 text lines. I did not separate the two, so I cannot say how fast the scan alone is. I have not compared either tool against a Node version on the same input, so I can’t claim a speed ratio.

Tests and what I did not check

Where Node is still the right answer

I did not rewrite the other tools, and I would not:

What Go was good at

What I missed: union types, optional chaining, and the ergonomics of Array methods. Loops are verbose. For a tool that is mostly string work, I still reach for Node first.

My rule now

Before starting a tool, I ask two questions:

  1. Who runs it, and what do they already have installed? A front-end team has Node, so Node is fine. A mixed team, CI images I do not control, or a pre-commit hook on a machine without a JavaScript toolchain is a case for Go.
  2. Is the hard part the web platform, or files and text? DOM, CSS rendering and screenshots are Node. Walking trees, lexing text and parallel file work are Go.

If both answers point the same way, the choice is made. If they disagree, I default to Node, because my users are front-end engineers and shipping matters more than cleverness.


Also by me

Tags: Go · Node.js · Developer Tools · CLI · Performance