A simpler building block for Go CLIs¶
Originally published on Medium. Cross posted here for posterity.
Nothing technical here, just an opinion on an alternative CLI (command line interface) framework for Go developers.
If you searched "Go + CLI + framework" + any of the following:
- urfave/cli
- spf13/cobra
- mitchellh/cli (fairly lightweight, also very good)
- alecthomas/kingpin
- and friends..
Then you’ve landed in the right place, but probably not why you think. The goal is to surface a much simpler package:
Do we really need yet another CLI framework?
Yes.
Most folks building CLIs do not require complicated solutions. Furthermore the act of composing (defining) a CLI is often conflated with orthogonal concerns, such as state management, tab completion, prompts, colors, etc.
There are a lot of blog posts and videos that suggest one of the aforementioned packages. For the record, I’ve used most of them and have nothing but respect ❤️ for the maintainers and projects. They evolved in their own way and for different reasons.
Some have undergone re-writes resulting in less intuitive functionality, others suffer feature creep and bloated API footprints. I’m not going to focus on any one package, but I suggest the reader check the links above and look for themselves.
Think about your project, does it require all features and functionality to be exposed through a single CLI framework? Can the concerns of CLI composition be separated from terminal colors?
As the title suggests, simple building blocks are powerful. If we’re being completely honest,
one could get away with a switch
statement and map
at the core of the CLI. But since you're
here, and likely looking for something more structured, I'd suggest starting with a lightweight
solution such as ffcli
. You'll be pleasantly surprised how far a simple package that does the bare
minimum (well) will get you:
- composing a command tree
- parsing flags
- matching commands to functions
- propagating a context
Everything beyond this is noise. Orthogonal and often necessary noise that has a place, just outside the core CLI framework.
Prompts? see manifoldco/promptui. Colors? see fatih/color. Fancier components? see charmbracelet/bubbletea or charmbracelet/bubbles.
And this is why ffcli
resonated with me, to echo the author:
intuitive and productive
.. the long-term goal of supporting commandline applications that are substantially easier to > understand and maintain.
For more info see the rationale section of the README.
I’ve witnessed it a few times. Projects adopt a framework and struggle to maintain it. Day one is easy: read the docs, code the thing, and everything is awesome™. Inevitably upstream frameworks evolve, the docs get longer, the API footprint grows, lots of new features are added and the whole thing requires more reasoning about.
But wait, the same could happen to ffcli
? Maybe, but I don’t think so. The current maintainer
strives to build "letter openers, not Swiss Army knives" that solve well-defined problems. Remember,
we’re strictly talking about a framework for composing the core of a CLI.
To wrap this up, minimal frameworks are much easier to pick up, reason about and consistently use over time. The lack of additional features is a feature in-itself. Why? Because "other" features can be introduced at any moment and are orthogonal in nature.
The entire API for ffcli
can fit in a single screenshot.
Official examples can be found here. I hope to find time to dive a bit deeper and show how it could be used to compose a CLI such as pressly/goose.
The lawn mower effect.
Think of an old gas-powered lawn mower. It hasn’t been used in a while, needs maintenance you don’t remember and starts with a specific angle / force applied to a string. The next few hours are spent on the lawn mower instead of the task: cutting grass.
Don’t let tools get in the way.