When writing a command line utility used by others – end users, colleague developers, etc. – one has often to make a compromise between two goals. One the one hand, the tool should guide the end user through the process to facilitate the actual task. This is often achieved with interactive dialogs. On the other hand, the edit-compile-run cycle should be as short as possible to speed up development. By definition, the tool should not be interactive to not let the programmer answer the same questions again and again and slow down development.
In the following, I want to outline a “design pattern” which can reconcile both requirements. Although it always seemed somewhat obvious to me, I haven’t seen in a lot in the wild: My proposed solution is to have a command line argument for every interactive dialog defining some input parameter.
Let me first show an example to get our minds on track and then give further advice on the implementation of such a system.
The above example is in Perl but you can obviously apply this pattern in any language.
The core concept is implemented in the
It checks whether a certain command line argument (the
key) has been provided and just returns its value if that is the case.
Otherwise, it will ask the user to input the specific parameter and store the value in the
opts hash before returning it.
The sample functions
askAge make use of
ask() by providing the expected command line argument name, a question to be asked and a default value.
This setup allows to provide all, none or some arguments on the command line.
Every missing bit will be asked.
askName() twice will not cause the question to be asked twice.
This is handy if you need to have access to certain input parameters at different locations in the code.
The end user will get to see all the questions she needs to answer without having to worry about forgetting anything. The programmer can always run the program with tons of arguments and does not need any interactivity at all.
The concept can be adapted to various circumstances.
Just as an idea, in the past I have implemented further helper functions like
askYesNo() (which would expect either
Another thing which can be very handy is a
Often the end user does not need to input anything but should acknowledge a certain fact (ex: “Bear in mind that X is only valid if Y.”).
The program should pause and print some message but should obviously not halt during testing.
A simple approach using the above setup in Perl could be:
The developer would add
--no-verify; the end user has to acknowledge every message by pressing
I love to have automated tests for my software so in production, I further expanded the presented framework to allow unit testing as follows.
ask() function should do one of the following (in the order of priority):
- Return a non-empty argument specified by the caller (unit testing scenario).
- Return the value specified on the command line (development scenario).
- Ask & return the value specified interactivity (end user scenario).
Another advantage of the program’s possible non-interactivity is that it can be run in a cron job or via a remote shell. More often than not, a tool will not be run by a real person even if it was originally intended only as an interactive end user program. You can just tell your boss you’ve already implemented this feature!
As a side note, in 10 Usability Heuristics for User Interface Design by Jakob Nielsen, the Flexibility and efficiency of use pattern declares: “Accelerators – unseen by the novice user – may often speed up the interaction for the expert user such that the system can cater to both inexperienced and experienced users.” Well, guess what we just did?