Chances are, you have written some command line programs in Python. This is quite probable even if you normally code
in some other language. And if you have, it is not unlikely that you needed to parse the argv of your program at one
point or another.

There are plenty of options here, both in the standard library as well as among third party packages. One does stand out,
however, and it’s mostly for how it is often overused. I’m talking about Click here.

If you wanted to use it in your next Python program, I hereby urge you to reconsider.

What’s the fuss?

The somewhat bizarrely named Click library is described as a “package for creating beautiful
command line interfaces”. Its main trick is the ability to create subcommands by adorning Python functions with the
@click.command() decorator1. It then makes them coalesce into an argument parser, equipped with the necessary
dispatching logic.

This idea isn’t new, of course. Prior art goes back at least seven years to the now-abandoned
opster package. Click, however, was the first one of its kind to garner noticeable
popularity, which is easily attributed to whom it’s been authored by.

So while my arguments against using this kind of CLI framework would apply to any package implementing the paradigm,
it just happens that Click is currently its most prominent example. Purely for the sake of convenience, I will therefore
refer to it as if it was interchangeable with the whole concept. Because why not? Whatever you may say about
the library’s name, it’s hard to imagine a more concise moniker than a simple Click.

What’s wrong, then, with the way Click handles command line interfaces?

CLI: Little Interfaces

It’s how it encourages to treat them as an accidental afterthought rather than a deliberate design decision.

For applications invoked repeatedly from a terminal, their command line arguments and flags are the primary means of
user interaction2. It is how users communicate their intent to perform an action; provide the neccessary input data
to carry it throgh; decide how they want to receive the output; and control many other aspects of the programs execution.
Absent graphical components and widgets, the command line is virtually the only way to interact with a terminal program.

In other words, it is the UI.

And how important the UI is for any application? It seems to be important enough that entire fields of study are devoted
to reducing friction of human-computer interaction. In many projects, the emphasis on user interface design is on par
with that of actual software engineering.
Like everything, of course, it is susceptible to trends and fads (such as the recent “mobile/responsive everything!”
craze). But its significance remains undiminished. Quite the opposite: in the age of ubiquitous computing,
user interfaces are probably more important than ever.

Yes, this includes CLI. One of the main reasons we turn to the command line are speed and efficacy. Common tasks must
utilize short and convenient syntax that is quick to integrate into user’s muscle memory. Others should not only be
possible, but discoverable and accessible without going through reams of man pages.

Any terminal program intended for frequent use by humans should therefore strive to excel in those two qualities.
But except for the simplest of cases, it won’t happen by itself.
Designing an efficient CLI for any non-trivial application is a challenging and demanding task.

It doesn’t click

With Click, however, we’re encouraged to just wing it.

Click tells us to slap some decorators on our top-level functions and call it a day. Sure, you can dig deep enough
and uncover the underlying layers of abstraction that may eventually allow you do things for which argparse has
a first-class support.

Indeed, the whole idea of subdiving your program into several distinct is already suspect, for it appears at odds
with the fundamental Unix philosophy of
doing one thing well. While it is
occasionally justified, it shouldn’t be the first thing that comes to your mind. But that’s completely at odds with the
Click’s approach, where not ending up with multiple distinct commands is something you have to consciously avoid.

…though it sometimes might

So, what am I suggesting you use instead libraries such as Click?… Nothing outrageous, really.

If you care about your command line interface, consider just using
the argparse module. Yes, it will force you to create parser objects,
add arguments & flags to it, and in general pay some attention to the whole business. When it comes to UI, it’s always
good to make it an explicit concern, maybe even sufficient to warrant
its own module.

Alternatively, the docopt library provides another take on the UI-first approach
to CLI, though it is more limited in its capabilities3.

Finally, I’m not advocating to ditch Click in all scenarios. There’s plenty of situations when we’re interested in
getting anyCLI up and running, and not so much in making the most efficient and intuitive interface possible. The prime
example is any kind of automation scripts that are ancillary to some bigger project, like manage.py is in Django4.
The Python ecosystem doesn’t really have dedicated task runners that are as featureful as Grunt
or Gulp, and that makes Click a viable and compelling option5.

But for standalone programs whose CLI is the main interface? Yeah, not really.

Environment variables and config files deserve a honorary mention, of course. But those are usually derivatives of
the command line arguments, containing e.g. the default values for flags. ↩

Click’s own documentation actually describes quite nicely
how theirs and docopt’s philosophies differ in a way that’s consistent with this article. ↩

Incidentally, this appears to be a major motivation behind creating Click in the first place:
to support web applications built upon on the Flask framework, and possibly obviate the need
for extensions such as Flask-Script. ↩

This saying, there are some task runners which offer similar experience, like Invoke. ↩