24 comments

[ 2.5 ms ] story [ 66.7 ms ] thread
I prefer node with commander. It doesn't get any easier or more featured than that. You can even wrap it with an API to make it graphical.
The newer generation of tech people the more they tend to be impaired when it comes to using command line interfaces.

When I started these were a norm and I learned to be proficient well before I started to use graphical applications.

While I can easily move around any graphical application I do circles around other developers I work with when it comes to tasks like operating on files, moving them between servers, etc. even when those people use their best tools they know and have been using for a long time.

Coupled with ability to type fast (yes, I had touch typing on an electric typewriter at school), command line interfaces tend to scale much better with complexity of the operation. Change to a subfolder and maybe a mouse-driven app is going to be faster. Change five levels down, then three levels up, then repeat five times with small variations doing some operations, and mouse user has no chance to keep up.

Also there is only so many things you can click on (ie the surface of the screen) so you have to click on things to uncover other options to click on, and so on. All this is slow because typing is much faster than pointing and clicking. I type 5 to 10 characters per second, easily, and that I think is not possible for a mouse-driven application and touch interfaces are even worse.

Commands basically open an infinite amount of possibilities with very few keystrokes as long as you have experience composing them. On my machine I press ctrl+n and start typing name of the file and I am instantly brought to that file. With a mouse I would first have to navigate to some kind of commander or explorer-like application, start going through folders (assuming I remember where the file is) or maybe click on some kind of search and then browse through results. That takes time and I am usually under a two seconds to reach most files on my system.

My theory is that people who started learning with newer technology that does not afford so much in terms of composability never learned to solve a problem this way. Also the new technology tends to change a lot faster than in the past, making it obsolete sooner which causes people to overlook advantages of learning their tools really well.

Amusingly, my family just bought a new car. Naturally, it comes with a lot of interesting features, and each feature has multiple settings. Adjusting those settings is done by pushing buttons in sequence, just like how you programmed the date and time into your first VCR.

All of the time I'm spending with these settings, I'm thinking to myself: Just give me an RS232 port, so I can plug in a dumb terminal and set everything up using a CLI.

But not all is lost. I've noticed that more and more web pages that give instructions on setting up computer hardware and software start with: "Press Windows-R and type CMD, now enter the following commands." And I don't search for programs any more. I press the Windows button and enter the program name from the keyboard.

Strong disagree.

Any system worth keeping around should be a library (or family of libraries) which have a CLI fronting it that serves to pass parameters to it just as you might any other way (such as through some other program or REST API or GUI). Agree the CLI "shell" around the system should not have separate logic, but should report back logic faults from within the library.

> Agree the CLI "shell" around the system should not have separate logic

that was the main point.

> should report back logic faults from within the library

And I suggest validating parameters passed to functions.

So what's this you disagree with?

I share @ezekiel68 feeling. Your solution to replace CLI parameters with code is a terrible idea...

1) CLI support is an additional logic in your program that makes no real work

CLI is a user interface, it does real work as giving user way to interact with your program. It's an interface.

2) Error (exception) handling with CLI is very poor. Another layer of (bad faulty) code is required to make it possible.

Like with any other code. That's why you should to test it (like any other code).

3) Scaling/extending is not as easy compared to programming language APIs (see example in the end)

It's much easier. At least to my wife, who isn't programmer, but can use shell.

4) CLIs are detached from essential code, which in most cases is disadvantage.

What cases?

5) Forcing users to use CLI means: stay away from my code, you’d better not work with it.

Why having CLI would have to force the user to not use the library (if it is intended to be a library, not a stupid tool)

To sum up:

API != User Interface. API = Programming Interface.

Two different goals, two different sets of requirements. You can't just reduce one to another.

> API != User Interface. API = Programming Interface.

That's where we should start, but the other way around. Why do you keep trying to replace programming interfaces with bash calls?

Any example when calling cli compared to proposed alternative makes any difference to your spouse?

Mostly because user interface is meant to expose the common tasks to the user vs API which is supposed to give a low level control. curl is a good example, as a tool is fairly accessible, but as the library it gives control on all the aspects of connection, requiring much more knowledge/skill from the programmer. Not every user has this kind of knowledge, but still may want to use curl to do quick HTTP requests.

User != Programmer

Also, "bash calls" are cool - shell is a great tool to compose many diverse tools written in different programming languages. If all the tools were just configurable via writing code in their implementation language: 1) you would have to know all the programming languages 2) you would have to somehow input the code, parameters are just shorter than code

> "Any system worth keeping around should be a library (or family of libraries)..."

I'm interested to read more about this topic.

I maintain about 8-10 Python Click CLI scripts (a la 'Practical Business Python') which are basically clones of 2-3 original scripts. These have worked really well for me (developing them) and for the handful of colleagues who I share them with.

This summer I started a project to put a Flask front end on a few of the CLI scripts.

Reading the OP I started to imagine the decorator-on-function from `typer` might be an interesting way to maintain functions for the Flask App to call and still preserve the CLI functionality.

Then again, and I'm a neophyte here, I assume a 'library' formalization would give me both options as well...

This... isn't persuasive. I'm genuinely struggling to find a deeper critique than that.

Perhaps if there were examples of all the confident statements? But they're just terse declarative sentences, all I could say is "nuh uh!"

The "better" examples using inline code made my head spin.
It looks new - but is it any more complicated if after all that's what your call maps to?
> How do you test CLI?

I do not understand this. Has the author used diff or stdin/stdout redirection? Testing CLI is trivial in comparison to testing a GUI or another kind of interface; it is all text and so can easily be compared against a correct result. CLIs are not meant for every task, but one of their core advantages is ease of automation and testing.

It is easy only if you write CLI utility (or somewhat very light-weight).

Imagine you write ASR system (from CLI input to output - week(s) of training, requires several servers). Mind telling me more about using diff/stdin/stdout in this case?

Well, forget ASR, take any ML system.

ok, I understand now my question got misinterpreted, I'll rewrite that.

CLI parsing (mapping to real calls) may have bugs. How do you test this?

Both options suggested eliminate this possibility completely.

> one of their core advantages is ease of automation and testing.

Any example when suggested options are any worse in this sense?

I guess our conceptions of CLIs are different. I wasn't thinking of developing complex textual formats that need to be parsed as input to a CLI program, I was thinking more like simple flags like `--do-operation=value` as opposed to a button in a GUI doing the same thing. If you have a function you want to call named foo() it should be pretty easy to just look for a `--foo` flag and run it. The problem becomes trickier if you have to parse some inputs from text. However, most CLIs I've seen for third party services use mostly simple flags, each of which corresponds to some simple operation. They may take an arg or two (e.g. `cli-client --foo 34 --title "Hello"`). If there is a more or less one-to-one mapping between functions and CLI flags, then it should be pretty easy to test. Testing a full-on parser for complex CLI commands is definitely harder, though.
> then it should be pretty easy to test.

If it is that simple, you can just devote this to package like typer. That's what I point to - don't write it.

I see people somehow read GUI along the lines, while I never even considered that as an option. Added a note and please read completely before commenting

I think this is trying to say you shouldn't build CLI parsing logic into your core functionality, and this makes sense.

Core functionality should be in functions, classes, etc. and a CLI utility can be separately written or synthesized that imports that library, and anything wanting to implement a different interface can do the same.

Isn't that the way it's normally done? Real world example is libcurl and curl command.

The premise is not worth criticizing. I wonder what his motivation was for writing this nonsense?
I read the core argument as:

Don't write a shell CLI prematurely - a script in whatever language you're using can often be just as good a interface. Maintaining a shell CLI is work.

If you write a shell CLI don't aim for full flexibility - a script already fulfill that role.

I'm a big fan of shell CLIs (command --option=1), but I think this way of thinking has merit. I would consider a script a subset of CLIs though, using a python REPL with preloaded modules is certainly a type of CLI, no? Code is a sequence of commands.

That being said, as the author mentions, new libaries make it really easy to expose some functions to a shell CLI. If the task mesh well with piping, exposing some primitive to the shell is probably worth it.