Our product is called Patch. It ships patches. The CLI, obviously, should be patch:
brew install patch-release/tap/patch
patch init
patch releaseBeautiful. Memorable. Already taken — by Larry Wall, in 1985.
patch(1) is one of the foundational Unix tools: it applies diffs to files, it predates Git by two decades (git apply is its spiritual descendant), and build systems all over the world invoke it by bare name every day. macOS ships it at /usr/bin/patch — and here's the detail that makes the story better: that binary reports patch 2.0-12u11-Apple, the BSD lineage descended from Larry Wall's own code. Not GNU patch (that's a separate fork, which Homebrew deliberately ships under the name gpatch). Lose the name fight on a Mac and you are losing it to the 1985 original, still shipping.
We knew all this, in the way you "know" trivia. What we hadn't internalized is what it means to share a name with it. We shipped 1.0 under the bare name anyway. The collision fully sank in about two days post-launch — as far as we know, before any user was bitten by it — and the rename landed in the next release. Here's what we were facing.
What happens when two commands share a name (PATH-order roulette)
When two executables share a name, the winner is decided by PATH order — per machine, per shell, per how the user happens to have configured Homebrew. Which meant our beautiful CLI had two failure modes, and both were worse than a crash:
We lose the roulette. /usr/bin comes first; the user types patch init and gets the system patch — Wall's lineage — sitting silently, reading stdin, waiting for a diff that will never arrive. No error. (If you have ever typed patch, watched the terminal hang with no output, and googled "patch command not doing anything" — that stdin wait is what you were looking at.) A hung terminal as the first onboarding experience, for a developer tool whose entire pitch is "this takes five minutes."
We win the roulette. Homebrew's bin precedes /usr/bin (it usually does), our binary shadows the system one, and now some Makefile or autoconf script three directories away runs patch -p1 < fix.diff and gets our CLI politely explaining that -p1 is not a recognized subcommand. We have broken a 40-year-old contract on the user's machine, and the bug report goes to whichever project's build failed — not to us.
Homebrew's own rules point the same direction: formulas duplicating macOS-provided tools must be keg-only (keg_only) — installed but not linked onto PATH — which avoids the breakage by making the install do nothing visible. That's why GNU patch lives in homebrew-core as gpatch: core hit this exact name decades of collisions ago and chose the rename. For an onboarding-critical CLI, a tool that installs and then isn't on PATH is its own catastrophe — so the precedent was clear either way.
Renaming a shipped CLI: patch became patchcli
brew install patch-release/tap/patchcli
patchcli initThe product is still Patch; the command stopped claiming a name that wasn't ours. The bill, since we caught it days rather than months in: rename the binary and the tap formula, sweep the docs, the quickstart, and every code snippet on the marketing site, and re-verify the install flow on a clean machine. An afternoon — plus the awkward fact that a couple of point releases had already shipped under the old name. If you installed one of those: brew uninstall patch then brew install patch-release/tap/patchcli — deleting a formula from a tap doesn't unlink the binary already on your machine, which is its own little footgun we now get to document forever. Wait a year instead of a week and this section becomes a migration guide, a deprecation period, and a forwarding shim.
(The "keep the cute name and document PATH ordering" option was never real. An install step that depends on users re-ordering their PATH fails silently on every machine you don't control. We already knew this intimately: our build pipeline once reported "0 modules emitted" because the wrong Swift toolchain shadowed the right one. PATH problems don't error; they gaslight.)
How to check if a CLI name is already taken
To check whether a CLI name is taken, run four commands — on your machine, before the name appears in a single README: command -v and type -a (what wins locally, including shell builtins), man (does your OS ship a tool by that name), and brew info/brew search (the Homebrew namespace). Then check a manpage index like manpages.debian.org, because your Mac is not every Unix.
command -v <name> # taken on YOUR machine?
type -a <name> # every shadow: builtins, aliases, all PATH hits
man <name> # does your OS ship a manpage for it?
brew info <name> # taken in homebrew-core?
brew search <name> # ...or anything confusingly near it?Then the judgment calls:
Command names that are already taken
| Name | Taken by | How it bites |
|---|---|---|
patch | /usr/bin/patch, since 1985 | hangs silently reading stdin |
test | shell builtin | the builtin wins — no PATH order involved |
make | POSIX build tool | every Makefile on earth |
time | shell keyword + /usr/bin/time | the keyword wins |
link, install | coreutils/BSD | scripts call them with flags |
The product name and the command name don't have to match. Nobody is confused that Cargo drives Rust. A suffix (
patchcli), a prefix, or a different word entirely all beat a collision.If you must squat near a real name, check what scripts do. The question isn't "will humans be confused" — humans adapt. It's "does any Makefile on earth call this name with flags my tool doesn't understand."
We spent months on the genuinely hard naming problems — the product, the company, the protocol — and let the easiest one, the literal string typed into a terminal, ride along by default because it felt obvious. The obvious name had a forty-year-old prior claim.
The CLI in question ships OTA code updates for native Swift iOS apps — patchrelease.com. Related: Compiling Swift to WebAssembly