Command-line interfaces

Command-line tools (also referred to as “CLI tools”, for “Command-Line Interface tools” or simply “CLIs”) are productivity tools that Shopify developers and third party developers interact with every day.

A well crafted CLI feels familiar, and helps developers perform tasks efficiently. Sweating these details is what makes a CLI trustworthy.

When developers use a CLI, they’re often performing repetitive tasks, which is why the most critical aspect of a CLI is speed. A focus on this principle should result in faster perceived or actual performance.

Some of these guidelines can also be used when building chatbots and Slack commands that focus on productivity and execution speed, in tandem with the conversational experiences guidelines.

The Shopify App CLI in action, demonstrating a multi-step project creation process.

Using a CLI

Users should be familiar with the following mechanics of a CLI:

  • Typing in commands to execute tasks
  • There’s a persistent help command that will educate them on specifics of each command/subcommand
  • ctrl + C exits any running task
  • Navigating and manipulating the file system by using commands such as cd and mkdir

Anatomy of a CLI

CLIs most commonly include 3 components: a top-level command, commands (sometimes with subcommands), and options. This pattern is known as “multi-command CLI”.

  1. top-level command
  2. command
    1. command name
    2. command argument
  3. options (sometimes called “flags”)
    1. option name
    2. option argument

For example:

$ shopify create node --name=myApp
  ^       ^      ^      ^    ^
  1       2.1    2.2    3.1  3.2

Learn more: The syntax of a CLI command.

These are valid and equivalent ways to pass an argument:

# Equal sign
$ shopify create node --name=myApp
# No equal sign
$ shopify create node --name myApp

Components of a well-designed command

When creating a new command or subcommand there are a few things to keep in mind.

The most efficient way to execute a command

Most of the time a command should be self-contained and should not require additional input from the user. For example when running shopify populate products the command will execute without additional inputs required from the user even though the command could ask for things such as product name and price.

When creating a new command or subcommand, consider how much information is absolutely necessary for the command to execute autonomously. If a command always requires arguments or additional information, adding smart defaults could help make inputting the command faster.

Add arguments for key overrides

Arguments allow the user to override a command’s default execution. For example, when a user runs shopify populate products, a sensible default would be to create 5 products. However, if the user wanted to generate more than 5 products, they could use an argument to override the default behavior, like so: shopify populate products --count=20.

When creating a new command or subcommand, consider setting smart defaults, and how to best expose arguments that allow the user to override them.

Always add to help

Help is the primary way a user will find more information about how to use a command. This is a pattern built into the CLI mental model and should not be overlooked.

When adding a new command or subcommand, always make sure it’s documented in the help pages. The standard pattern for running the help command is shopify help [command], as documented in the naming conventions section for the help command.

Other surface areas to consider

When a command or subcommand creates new files or adds lines of code to existing files, consider adding comments or content in said files to describe how to use the feature. For example, insert a link to documentation material where the user can learn more, and include usage examples.

Where possible, provide the user with the context that will help them use the feature or file in their work in the most effective way.


Naming conventions

Single words, lowercase or kebab-case

  • Command and option names should always be single, lowercase words without spaces, hyphens, underscores, or other word delimiters
  • If there’s no obvious way to avoid having multiple words, separate with kebab-case

Do

$ shopify create node --name=myApp
# When using multiple words is unavoidable,
# use kebab-case notation:
$ shopify --long-argument

Don’t

$ shopify --longArgument
$ shopify --long_argument
# Use long command names (unless unavoidable)
$ shopify create-node-app

Aliases

Aliases are shortened versions of commonly-used commands and options.

For example:

$ shopify serve
# Can be aliased to:
$ shopify s

Options that are aliased will be equivalent in these cases:

$ shopify serve --an-option
$ shopify serve --a
$ shopify serve -a

Do

Create aliases for commonly-used commands and options.

$ shopify serve
$ shopify s

Don’t

Create aliases for rarely-used commands and options, especially when it may introduce ambiguity.

# Rarely used: --name=option
$ shopify create node -n myApp
# Ambiguous: does `c` stand for `create`, or `config`?
$ shopify c

Option aliases (example: -b for --branch) should be avoided, but are useful when they provide significant time savings or follow an existing convention that’d be expected by the user.

Option aliases can be lowercase or uppercase letters, where uppercase letters can either have the inverse meaning of their lowercase equivalent (like git log, where --paginate is aliased as -p, and --no-paginate aliased as -P), or to signify “force-running” an operation (like git checkout and its -b and -B options).


Follow existing conventions

CLIs such as git, npm, or rails have popularized conventions that CLIs must consider in order to feel familiar to new users.

Here are a few examples of commands, options, and aliases that must follow existing conventions:

Version number

Users need to know what version of the CLI is currently running so they can load the corresponding documentation, or mention the version number when reporting bugs or issues.

$ shopify version
1.5.0
# Must be aliased to (by convention):
$ shopify --version
1.5.0

Note: if your CLI supports -v to return the version, make sure -v also works as an alias for --verbose when appropriate.

Help

Users expect to see helpful documentation when using these commands and options:

$ shopify help
# Must be aliased to (by convention):
$ shopify -h
$ shopify --h
$ shopify --help

# These syntaxes are equivalent:
$ shopify help <command>
$ shopify --help <command>
$ shopify -h <command>

If a command requires subcommands or options, CLIs may provide usage guidelines when the user runs the bare command:

$ my-cli config
Usage:
  my-cli config set <key> <value> [--global]
  my-cli config get [<key>]
  my-cli config list [--json]

Provide commonly-used examples when they can help the user complete their tasks faster. Only display examples that are safe to run, and won’t perform an irreversible or destructive action: users who copy and paste examples may not realize their impact when they’re still unfamiliar with the CLI.

Verbosity levels

When things are going well, CLIs should get out of the user’s way and output information as concise as possible. However, some developers will want to understand the underlying details of the program that’s running in the background, for example to diagnose performance issues, or observe incoming and outgoing traffic.

Give users the ability to know detailed information on running processes by using the --verbose (and optionally, --very-verbose) options:

$ shopify serve --verbose
$ shopify serve -v
$ shopify serve --very-verbose
$ shopify serve -vv
  • Keep the noise to a minimum

  • Allow users to show detailed information using the --verbose (and optionally, --very-verbose) options

  • Show every bit of information that comes from external or internal tasks such as network calls, as it obscures important and actionable information away from the user


Syntax

Descriptions, help, and additional content

CLI descriptions should be:

  • Focused on information that is most important to users
  • Concise and scannable:
    • Use simple, clear language that can be read at a glance
    • Keep description content to a short sentence that’s just a few words in length
    • Avoid using punctuation such as periods, commas, or semicolons
    • Avoid using exclamation marks—good copy already makes enough of a statement without an exclamation mark
    • Write in sentence case

Do

Use sentence case across all descriptions.

--dev                Save package to your `devDependencies`
--disable-pnp        Disable the Plug'n'Play installation
--verbose            Output verbose messages on internal operations

Follow the Polaris help documentation guidelines.

Don’t

Use mixed case between descriptions, or omit a description.

--dev                save package to your `devDependencies`
--disable-pnp        Disable the Plug'n'Play installation.
--verbose

Equal signs

When an option has a value, use = sign between options and arguments, both in command-line help and documentation (--option=<placeholder-value>). It signifies that the option and its value stand as a single unit.

Do

--option=<placeholder-value>

Don’t

# This syntax should work, but not be surfaced in help or documentation
--option <placeholder-value>

The following table describes the notation used to indicate command-line syntax (as per Microsoft’s command-line syntax key).

Notation Description
Text without brackets or braces Items you must type as shown.
<Text inside angle brackets> Placeholder for which you must supply a value.
[Text inside square brackets] Optional items.
{Text inside braces} Set of required items. You must choose one.
Vertical bar | Separator for mutually exclusive items. You must choose one.
Ellipsis Items that can be repeated and used multiple times.

Do

cli --option=<required-argument>
cli --option[=<optional-argument>]
cli [optional-command]
cli command [optional-command|another-optional-command]

When your CLI operates alongside an existing ecosystem or framework, follow conventions familiar to your users (for example, the Rails CLI uses uppercase to denote command and option arguments: rails generate GENERATOR, --environment=ENVIRONMENT).

In some rare cases, you may also need to tweak the syntax for the documentation to read well. Use your best judgment and be consistent across these occurrences.


High-level UX considerations

High-level UX considerations to help make decisions when creating a CLI.

Interacting with a CLI is a conversational experience

Using a CLI can be hard to get right the first time. Whether it’s because they mistyped a command, or because their local development environment is broken, users learn how to use the CLI through trial-and-error, which means that to find how to best perform a task, they’ll likely go through a number of unsuccessful takes.

Be mindful of this multi-step experience by treating the CLI like a conversational interaction. Give users the right to fail, and guide them along the way.

Communicate every state like success, waiting, and error, even if it’s redundant

  • Communicate task success even if the external process has also communicated success

  • Always wrap up what just happened with a success or error message

  • Assume that success is understood because the process finished

Suppress output when the process is successful, and show output when the process has an error

Do

Hide all STDOUT information if the process ran successfully (unless the user asked for verbose output), and show all output if the process failed.

Don’t

Show every line or state if the process success or error state has not been determined yet.

Error out if a task needs to make a system change and tell the user how to complete the installation

Do

Prompt the user if you need to run something external that will modify their system to complete the installation.

Don’t

Automatically install anything that modifies the system. Prompt them that it’s needed and ask them if they would like the CLI to install it.

If a process that fails early will affect the rest of the task, terminate it as soon as possible and tell the user why

Do

Error out of a process if you know the rest of the task will fail.

Don’t

Continue to try successfully completing the task if one crucial task failed.

Output one idea per line

Do

Error: you are not in an app project folder
Tip: use shopify create project to create a new app project

Don’t

Error: you are not in an app project folder, use shopify create project to create a new app project.

Ask for all the information needed to execute the task automatically

To enable users to automate tasks, make sure any information requested by the CLI in a prompt can also be populated through an equivalent command or option.

Do

Use smart defaults to execute the task as fast as possible, and include arguments to let the user override the defaults.

Don’t

Defer multiple options or inputs to after the command is executing.

Layout

Breaking content into columns can make the content easier to scan. For example, options and their descriptions can be broken into two columns.

$ cli help install

Usage: cli install [options]

Cli install is used to install all dependencies for a project.

Options:

  --no-default-rc      Prevent Cli from automatically detecting clirc files
  --enable-pnp         Enable the Plug'n'Play installation
  --disable-pnp        Disable the Plug'n'Play installation

Resources


Public CLIs by Shopify

  • Shopify App CLI, helps you build Shopify apps faster. It quickly scaffolds Node.js and Ruby on Rails apps, and automates many common development tasks.
  • Theme Kit, that you can use to build Shopify themes.
  • Theme Check, helps you follow Shopify Themes & Liquid best practices by analyzing the Liquid & JSON inside your theme.
  • CLI UI, a small framework for generating nice command-line user interfaces.