I'm a Frontend Engineer Who'd Never Touched Go. I Shipped a Real Go CLI Tool Anyway — Here's What That Actually Looked Like

Every time Node.js ships a new LTS or Current release, upgrading means the same five manual steps: look up the new version, install it through whatever version manager you use, snapshot your global npm packages, reinstall them on the new version, and clean up the old one. I got tired of doing this by hand, so I built nodeup — a CLI that collapses all of it into one command: nodeup upgrade.
Here's the part that's more interesting than the tool itself: I'm a Senior Frontend Engineer. React, TypeScript, Next.js — that's my whole career. I had never written a line of Go before this project. I built it anyway, using AI tools heavily to learn the language and write the code, because a tool that manages your Node installs can't depend on Node itself to run.
What "AI-assisted" actually meant day to day
It didn't mean typing "build me a Node version manager" and shipping whatever came out. It meant constantly making calls an AI can't make for you: how should the package boundaries in internal/ be drawn so the CLI layer doesn't leak into the detection logic? Which of the eight version managers (fnm, nvm, Volta, asdf, mise, n, nodenv, nvm-windows) actually needed special-casing, and which could share a common interface? When the AI suggested something that compiled fine but wasn't idiomatic Go, how would I even know, since I didn't have the instinct for what "idiomatic" looks like yet?
That last one is the real answer to "isn't this just vibe coding": the AI can write plausible-looking Go all day. It can't tell you whether the plausible-looking Go is actually good, and neither could I, at first. I closed that gap by asking it to explain why a pattern was idiomatic, not just apply it, and by leaning hard on golangci-lint with staticcheck, gocritic, and errcheck enabled — tools that catch a lot of what a human reviewer would catch, even when the human reviewer (me) didn't know Go well enough to catch it themselves.
Treating it like a real release, not a script
The part I'm genuinely proud of isn't the Go code — it's what surrounds it. Full CI across 5 platform builds (linux/darwin × amd64/arm64, windows/amd64), tests running on Ubuntu, macOS, and Windows separately, Conventional Commits enforced by commitlint, and a GoReleaser pipeline where a single git tag pushes a release to a Homebrew tap, a Scoop bucket, and npm (via OIDC trusted publishing) simultaneously. None of that happens by accepting AI suggestions — it's the kind of infrastructure decision-making that's identical whether you're writing Go, TypeScript, or anything else, and it's arguably the more transferable skill here.
Where it's honestly still weak
I use fnm + Homebrew on macOS daily, so that path is well-tested. nvm-windows, asdf, and mise have had far less real-world exercise than I'd like before calling this fully solid — if you're on one of those and something breaks, I'd genuinely appreciate a bug report over at the repo (nodeup).
Why write this up
Partly because I think the discourse around AI-assisted code is stuck between "it's all slop" and "just ship it," and neither matches what building this actually involved. And partly because I don't know many people who've documented a first systems-language project done this way — there's a whole companion guide in the repo (LEARN_GO_AND_NODEUP.md) teaching Go through the actual codebase, for anyone starting from exactly where I did.
If you're a Node developer, I'd love for you to try nodeup. If you're a Go developer, I'd love your honest opinion on where the seams show.
One more thing: I'm currently open to senior frontend/full-stack remote roles. If the way I approach shipping something end-to-end, outside my comfort zone, is useful to your team — I'd love to talk.