Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Building Modern CLI Applications in Go

You're reading from   Building Modern CLI Applications in Go Develop next-level CLIs to improve user experience, increase platform usage, and maximize production

Arrow left icon
Product type Paperback
Published in Mar 2023
Publisher Packt
ISBN-13 9781804611654
Length 406 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Marian Montagnino Marian Montagnino
Author Profile Icon Marian Montagnino
Marian Montagnino
Arrow right icon
View More author details
Toc

Table of Contents (21) Chapters Close

Preface 1. Part 1: Getting Started with a Solid Foundation
2. Chapter 1: Understanding CLI Standards FREE CHAPTER 3. Chapter 2: Structuring Go Code for CLI Applications 4. Chapter 3: Building an Audio Metadata CLI 5. Chapter 4: Popular Frameworks for Building CLIs 6. Part 2: The Ins and Outs of a CLI
7. Chapter 5: Defining the Command-Line Process 8. Chapter 6: Calling External Processes and Handling Errors and Timeouts 9. Chapter 7: Developing for Different Platforms 10. Part 3: Interactivity and Empathic Driven Design
11. Chapter 8: Building for Humans versus Machines 12. Chapter 9: The Empathic Side of Development 13. Chapter 10: Interactivity with Prompts and Terminal Dashboards 14. Part 4: Building and Distributing for Different Platforms
15. Chapter 11: Custom Builds and Testing CLI Commands 16. Chapter 12: Cross-Compilation across Different Platforms 17. Chapter 13: Using Containers for Distribution 18. Chapter 14: Publishing Your Go Binary as a Homebrew Formula with GoReleaser 19. Index 20. Other Books You May Enjoy

The guidelines

These guidelines have been formulated since the first CLI and have continued to evolve through the many years of developer and user experience. Following these guidelines will increase your chances of CLI success; however, there may be times when you decide to go your own way and follow an anti-pattern. There could be many reasons to choose an unconventional route. Remember that these are just guidelines and there are no hard and fast rules.

For life, and building CLIs, to be fun, we must allow a little chaos and the freedom necessary to be creative.

Name

The name of the CLI holds significant weight as the name may convey symbolic ideas beyond the initial intention. People do not like to think more than necessary, so it’s best to choose a name that is simple, memorable, and easy to pronounce. It’s amazing how many CLI program names have been chosen so arbitrarily without much thought.

There have been studies that support the linguistic Heisenberg principle: labeling a concept changes how people perceive it.

Hence, keep it short and easy to type. Use entirely lowercase variables in the name and only use dashes when absolutely necessary.

Some of my favorite application names are clear in the way that they plainly describe the application’s purpose in a creative manner. For example, Homebrew is a package manager for installing applications on macOS. A brew is a concoction of various ingredients, like a recipe, or in this particular case, a formula, to describe how to install an application. Another great example is imagemagick, a command-line application that lets you read, process, or create images magically! Truly, as Arthur C. Clark writes, “Any sufficiently advanced technology is indistinguishable from magic.” Other internal commands we are familiar with are mkdir, for make directory, rm, for remove, and mv, for move. Their popularity is partially a result of the transparent nature of their names, rendering them nearly unforgettable.

Help and documentation

One of the tenets of the UNIX philosophy is transparency, possible mainly through the help and documentation present within the CLI. For new users of the CLI that are in discovery mode, the help and documentation are one of the first sections they will visit. There are a few guidelines to make the help and documentation more easily accessible to the user.

Help

It is a good practice to display help by default when just the command name is entered or with either the -h or –help flag. When you display the help text, make sure it’s formatted and concise with the most frequently used arguments and flag options at the top. Offer usage examples, and if a user misuses the CLI, the program can guess what the user tried to attempt, providing suggestions and next steps.

Documentation

Provide either man pages or terminal-based or web-based documentation, which can provide additional examples of usage. These types of documentation may be linked from the help page as an extension of the resources for gaining an understanding of how the CLI works.

Support

Oftentimes, users will have suggestions or questions on how to use the CLI. Providing a support path for feedback and questions will allow users to give the CLI designer a new perspective on the usage of their CLI. When collecting analytics, be transparent and don’t collect users’ address, phone, or usage data without consent.

Input

There are several ways a CLI retrieves input, but mainly through arguments, flags, and subcommands. There is a general preference for using flags over arguments and making the default the right thing for most users.

Flags

The guideline for flags is that ideally, there exists a full-length version for all flags. For example, -h has --help. Only use , a single dash, or shorthand notation for commonly used flags and use standard names where there is one.

The following is a list of some standard flags that already exist:

Flag

Usage

-a, --all

All

-d, –debug

Debug

-f, --force

Force

--json

Display JSON output

-h, --help

Help

--no-input

Disable prompt and interactivity

-o, --output

Output file

-p, --port

Port

-q, --quiet

Quiet mode

-u, --user

User

--version

Version

-v

Version or verbose

-d

Verbose

Table 1.1: Standard flags

Arguments

Multiple arguments are fine for simple actions taken on several files. For example, the rm command runs against more than one file. Although, if there exist two or more arguments for different things, you might need to rethink the structure of your command and choose a flag option over an additional argument.

Subcommands

The guidelines for subcommands are that they remain consistent and unambiguous. Be consistent with the structure of subcommands; either noun-verb or verb-noun order works, but noun-verb is much more common. Sometimes, a program offers ambiguous subcommands, such as apt update versus apt upgrade, which causes many, including myself, confusion. Try to avoid this!

Validate the user’s input early, and if it’s invalid, fail early before anything bad happens. Later in this book, we will guide you through using Cobra, a popular and highly recommended command-line parser for Go, to validate user input.

Output

Because CLIs are built for humans and machines, we need to consider that output must be easily consumed by both. I will break down guidelines for both stdout and stderr streams for both humans and machines. Standard output, stdout, is the default file descriptor where a process can write output, and standard error, stderr, is the default file descriptor where a process can write error messages:

  • stdout

A guideline for standard output for humans is to make the responses clear, brief, and comprehensible for the user. Utilize ASCII art, symbols, emojis, and color to improve information density. Finally, consider simple machine-readable output where usability is not impacted.

A guideline for standard output for machines is to extract any extraneous substance from the above response so that it is simply formatted machine-readable text to be piped into another command. When simple machine-readable text is not output by default, utilize the -q flag to suppress non-essential output and --plain to display machine-readable text. Disable color with the --no-color option, by setting the NO_COLOR environment variable, or a custom MYAPP_NO_COLOR environment variable specific to your program.

Additionally, don’t use animations in stdout because it’s not an interactive terminal.

  • stderr

Things can go wrong during the execution of a command, but it doesn’t have to feel like a catastrophic event. Sometimes loud full stack traces are the response to a command failure, and that can make the heart skip a beat. Catch errors and gracefully respond to the user with rewritten error messages that can offer a clear understanding of what happened and suggestions for the next steps. Make sure there’s no irrelevant or noisy output, considering we want it to be easy to understand the error. Also, provide users with additional debug and traceback information and an option to submit bugs. Non-error messages should not go to stderr, and debug and warning messages should go to stdout instead.

Note

As for general guidelines for CLI output, return a zero exit code on success and a non-zero code that the machine can interpret as not just a failure but even a particular type of failure on which to take further action.

Configuration

Users may configure their CLI by using flags, environment variables, and files to determine how specifically to invoke the command and stabilize it across different users and environments:

  • Flags and environment variables

By using flags or environment variables, users may configure how to run a command.

Examples include the following:

  • A specified level of debug output
  • Dry run commands

Alternatively, they can be used to configure between different environments.

Examples include the following:

  • Providing a non-default path to files required for the program to execute
  • Specifying the type of output (text versus JSON)
  • Specifying an HTTP proxy server to route requests through

When using environment variables in configuration, set names appropriately, using a combination of all uppercase text, numbers, and underscores, and take the time to ensure you are not using the name of an already-existing environment variable.

  • XDG Spec

Configure stability across multiple environments by following the XDG Spec (X Desktop Group, freedesktop.org), which specifies the location for base directories where configuration files may be located. This spec is supported by many popular tools, such as Yarn, Emacs, and tmux, to name a few.

Security

Do not store secrets and passwords in environment variables or pass them in via an argument or flag. Instead, store them in a file and use the --password-file argument to allow the secret to be passed in discretely.

Open source community

Once your CLI is complete and ready to be distributed, there are several guidelines to follow. If possible, distribute it within a single binary targeted to a user’s specific platform and architecture. If the user no longer wants or needs your program, make sure it’s easy to uninstall too!

Since you’ll be writing your CLI in Go, it would be great to encourage contributions to your program. You may offer a contribution guideline doc that guides users toward commit syntax, code quality, required tests, and other standards. You could also choose to allow users to extend the CLI by writing plugins that can work with your CLI, break up functionality across more modular components, and increase composability.

Software lifespan and robustness

To ensure your CLI will continue to work well in the future, there are a few guidelines to follow specific to robustness to make sure your program has a long lifespan:

  • Future-proofing

When you make any changes to your CLI, over time, it’s best to make these changes additive, but if not, warn your users of the change. Changing human-readable output is usually fine, but it’s best to keep the machine-readable output stable. Consider how external dependencies may cut short your program’s lifespan and think of ways to make your application stable amidst external dependency failures.

  • Robustness

For a CLI to achieve maximum robustness, the CLI must be designed with full transparency. The program needs to feel responsible for the user; so, show progress if something takes a long time and don’t let the program hang. Make programs timeout when they are taking a long time. When the user inputs a command, validate the usage immediately, giving clear feedback when there’s apparent misuse. When there’s a failure due to some transient reason, exit the program immediately upon failure or interruption. When the program is invoked again, it should pick up immediately where it left off.

  • Empathy

Adding some thoughtful detail to the command-line design will create a pleasant experience for the user. CLIs should be fun to use! Even when things go wrong, with a supportive design, the users can feel encouraged on their pathway to successfully using the CLI. The modern CLI philosophy and guidelines reflect a level of empathy toward humans already and, thank goodness, we’ve come a long way from the initial command-line tools and will continue to do better.

You have been reading a chapter from
Building Modern CLI Applications in Go
Published in: Mar 2023
Publisher: Packt
ISBN-13: 9781804611654
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image