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.
Using a CLI
Users should be familiar with the following mechanics of a CLI:
- Typing in commands to execute tasks
- There’s a persistent
helpcommand 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
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”.
- top-level command
- command name
- command argument
- options (sometimes called “flags”)
- option name
- option argument
$ 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
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 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.
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
$ shopify create node --name=myApp # When using multiple words is unavoidable, # use kebab-case notation: $ shopify --long-argument
shopify --longArgument shopify --long_argument Use long command names (unless unavoidable) shopify create-node-app
Aliases are shortened versions of commonly-used commands and options.
$ 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
Create aliases for commonly-used commands and options.
$ shopify serve $ shopify s
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:
--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
--no-paginate aliased as
-P), or to signify “force-running” an operation (like
git checkout and its
Follow existing conventions
CLIs such as
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:
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.
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 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.
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,
$ 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
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
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
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.
Use mixed case between descriptions, or omit a description.
--dev save package to your `devDependencies` --disable-pnp Disable the Plug'n'Play installation. --verbose
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.
# 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).
|Text without brackets or braces||Items you must type as shown.|
||Placeholder for which you must supply a value.|
||Set of required items. You must choose one.|
||Separator for mutually exclusive items. You must choose one.|
||Items that can be repeated and used multiple times.|
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,
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
Hide all STDOUT information if the process ran successfully (unless the user asked for verbose output), and show all output if the process failed.
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
Prompt the user if you need to run something external that will modify their system to complete the installation.
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
Error out of a process if you know the rest of the task will fail.
Continue to try successfully completing the task if one crucial task failed.
Output one idea per line
Error: you are not in an app project folder Tip: use shopify create project to create a new app project
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.
Use smart defaults to execute the task as fast as possible, and include arguments to let the user override the defaults.
Defer multiple options or inputs to after the command is executing.
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
These guidelines were in part inspired by content from these sources:
- The content strategy principles we followed to build the Shopify App CLI, on the Shopify UX blog
shopifyCLI general and design principles
- Command Line Interface Guidelines
- Google’s command-line documentation guidelines
- Heroku’s CLI style guide
- Atlassian’s 10 design principles for delightful CLIs, by Natalie Johnson
- Heroku’s 12 Factor CLI Apps, by Jeff Dickey
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.