🗓 02 November 2020
Reverse engineering CLI design
Understanding good design for the command-line
General wisdom says anyone using the command-line tool is a power user. I don’t believe that’s true. And even if it were, it’s no excuse for a poor developer experience. Creating a useful and widely-adopted developer tool requires consideration for its design.
Skip the context and tell me what makes a good CLI design
I work on Flyway, an OSS command-line tool for managing database migrations. I’m a Product Manager, but I come from product design. I’m always looking for ways to improve the design and experience of my product.
Last week I caught @jamonholmgren’s Tweet about a nice CLI design update to Ignite, a tool for spinning up React Native apps.
I'm rebuilding Ignite CLI, and this is when I wish my terminal design skills were better.
— Jamon Holmgren (@jamonholmgren) October 21, 2020
It's not bad. Definitely better than the old Ignite help screen. I just think I can do a lot better. pic.twitter.com/nUHPrgfTDb
I thought the design looked strong. When I asked about design guides or resources used during development, Jamon’s answer was none, and that “there’s basically nothing out there”, which matches my experience1.
The fact that there’s little help out there for designing non-GUI tools is a real shame.
So instead of continuing to search for the guide to the perfect CLI design, I’ll try to write it. Today I’m starting by reverse-engineering Jamon’s excellent design work 👆, then building a list of design principles from it for you to follow.
Why go to the effort? Can’t you just copy this design? Yes and no. Most people can look at something and intuitively appreciate a good design. I can look at a painting of a horse and try to replicate it, but I’ll get better results from learning how to draw one from first principles.
Reverse engineering a good design doesn’t just identify what’s good, but should help explain why it is good. Knowing this will help you replicate the outcome of the design for your product needs, and not just the tool you’re looking at verbatim.
A reverse engineered guide to CLI design
How to use this guide
I’m going to structure this ‘reverse engineering of design’ by user need, phrased as a question a user would ask themselves, followed by an illustration, describing an example from the Ignite CLI.
When you’re using the guide, practice putting yourself in the shoes of your users and asking the same questions. The right way to answer each for your product might be different.
One last thing, since I’m focussing on the CLI design for the moment, I’ve skipped ahead of the whole experience of finding the product in the first place, and indeed installing it. If this guide proves helpful, then let me know and I’ll follow up with some discovery and installation design guides.
Let’s go…
A. “Is this thing on?”
What we’re looking at in the screenshot above is a result of someone running the help command ignite --h
. Providing the help option is command-line tool design 101, but it serves an interesting use case for a good developer experience (beyond the help itself):
When I run -h
as a user, I’m getting two things: confirmation that the tool is installed correctly (without doing something destructive), and secondly, some idea of what to do next.
As a user I can also get the first effect – confirmation of install – by running -v
– the command-line option that tells me what version of the tool I’m running – but encouraging users to run -h
as the first step after install also provides hints as to what to do next, and without switching context and reading docs.
👉 Remember to reserve -h
and -v
for “help” and “version”, and not to repurpose them as commands specific to your tool.
B. “Am I in the right place?”
Ideally they’ve starting from your website and docs, but more likely they read about it on a blog describing a solution to the problem they just Googled.
I think Ignite does a really good job here of confirming I’m using the right thing. If the blog I read says “Install ignite” and the output of -h
proudly proclaims “🔥 Ignite 🔥”, then I know I’m on the right path to solving my issue.
👉 Not all CLIs are created equal and not every CLIs will render emoji - something Windows is terrible at in general, and eerily reminiscent of browser/CSS standard compatibility (IE6 and rounded corners, anyone?).
C. “Do I understand what this does?”
This might sound similar to the question above, but it’s purpose is different and indicative of the difference between GUI and CLI. A GUI helps the user build a mental model of the system using a visual representation. CLI doesn’t have that benefit, but requires the same outcome.
Ignite achieves this with the information that “Ignite is a CLI that helps you spin up a new React Native app using a battle-tested tech stack”.
We can illustrate the mental model being built with this sentence by looking at the etymology of a couple of the phrases used:
- Spinup: (computing) The process of a disk drive spinning up.
- Tech stack In computing. A standard set of software components commonly used together on a system.
👉 I don’t think it’s feasible to rely on the command-line to impose a mental model on your user, particularly in more complex scenarios. At this point it’s useful to defer to project documentation, with illustrations - something we do in Flyway.
D. “Do I understand the implications of my actions?”
This question is about user trust. The CLI is often considered a more powerful interface because its assumption is that you know what you’re doing. But that also makes it harder to gain trust - do I really want to copy-paste that sudo
command I found on the internet without understanding what it will do?
Ignite achieves this by listing all the commands, with a description of their purpose and an example of its use, in a table. The design of the commands table in Ignite is good because it:
- Lists commands in structured format that’s easy to read as a human.
- Uses contrasting to differentiate command from description
👉 Whenever your command-line returns an output, think about the format of that content. Pure json – ie. a long string of text – might be machine readable, but it less easy for a human to parse. If a human needs to parse the output, make sure you structure it.
E. “Now what?”
Understanding a handful of commands is just the beginning. Individual differences in technical environments and requirements make it near impossible to cater for every eventuality in the tool. That’s where human help comes in - either through documentation or through asynchronous conversation.
Ignite does a great job of not just linking out to its documentation, but goes one step further by creating somewhere for its user community to go, and to support itself via their Slack community.
👉 The user “community” is a really powerful ecosystem for a lot of products, but especially so for developer tools. People get attached to their tools and like the affirmation of their good choices from like-minded people. Perhaps a better reason to create and support a community is for the opportunities to spot and improve on usability issues.
Summary
This is an all too brief review of one command-line tool. It’s not intended to be a comprehensive guide to CLI design, but a first step towards a more complete guide to designing for the CLI, which you can register your interest in getting an early look at below:
Footnotes
-
The information that is out there is disparate, and is either too esoteric to be practical, or is practical but lacks substance ↩
Get notified of new posts
© Jonathan Roberts 2020
I occasionally update articles to fix typos, improve readability or modify content when new information is available to me: view revisions for this article