logpipe started as a weekend script to stop copy-pasting log processing commands. A year later, a few hundred people use it. Here's what I learned.
Go is the right language for CLIs
Not because of performance — for most CLI tools that doesn't matter. Because of the binary. One file, zero dependencies, works on any platform. No "install Node first", no virtualenv, no version managers. You ship a binary and it runs.
goreleaser handles cross-compilation and GitHub release artifacts. Setup once, never think about it again.
cobra vs hand-rolled
I started with cobra. It's the standard choice, handles subcommands, generates help text, has a huge ecosystem. But for a tool with a few commands, it felt like a lot of setup for boilerplate I didn't need.
I switched to urfave/cli. Lighter, more idiomatic, easier to test. The right tradeoff for logpipe's scope.
If I were building something complex — multiple nested subcommands, plugin system, config management — cobra is still the answer.
Config file headaches
The hardest part wasn't the logging logic. It was config discovery: where does the config file live? What's the merge order between config file, env vars, and CLI flags?
The answer I landed on (and I think it's the right one):
- CLI flags win, always
- Env vars second
- Config file last
Document it clearly. Users get confused when flags don't override config.
Testing CLI tools
Two things that made testing easier:
-
Keep business logic in packages, not in
mainor command handlers. The command handler just parses args and calls the function. The function is tested normally. -
For integration tests, build the binary and shell out. It's slower but tests the real thing. I run these in CI, not in development.
The maintenance reality
Open source at small scale is mostly issue triage. Most issues are: user misunderstood the config, user hit an edge case in config parsing, user wants a feature that exists but is undocumented.
Good docs reduce issues more than good code does. I spent a weekend rewriting the README and cut new issues by half.