Improve the UX of CLI tools with version update warnings
CLI tools are like any other user facing software: they need to provide a good user experience. UX is about more than having a great UI. For instance, software evolves, and users must be given the tools to upgrade, either by using some kind of auto update feature or by using a distribution channel with versioning support. The latter approach relies on the user asking if something is outdated and that’s not the best UX. Tools should be proactive and warn the user about new versions.
Several well-known tools like npm or yarn follow this pattern. If you use npm I’m sure you’ve seen a message like this one:
╭─────────────────────────────────────╮ │ Update available 5.5.1 → 6.0.0 │
│ Run npm i -g npm to update │ ╰─────────────────────────────────────╯
It’s a nice touch when you distribute your CLI tools using a package manager.
A version checker for npm packages
Let’s add version checking to a hello
CLI command (I assume you know how to build a command line tool in node). To detect new versions we need to query the npm registry. If the local version of the package is outdated, a message in the console will invite the user to update to the latest version.
We start with a simple executable script. It just imports the hello
function from a lib
module and call it (it’s easier to test the logic this way).
The hello
function will use a versionCheck
utility before doing its thing.
To look for new versions:
- We use package-json to fetch the package metadata from the registry. This library also deals gracefully with
npmrc
config files and that’s a plus! - To compare versions we use semver, the semantic version parser used by npm itself.
Notice that we use the latest
dist-tag
in getLatestVersion
, but your mileage may vary. It should be adapted to your package versioning policy (skip or use non stable channels, etc.).
Improvement: periodic checks
If you try the code above you’ll probably notice a lag on each run depending on your connection speed. We are issuing a network request to the npm registry on each execution. That’s overkill. Nobody releases packages that often.
To remove the lag we just need to check for updates periodically. We can store the last time we checked and wait for a defined period of time to pass before attempting a new check.
In this example we store the last check time as a timestamp in a file under the OS’s temp folder.
Improvement: synchronous API
CLI tools tend to be synchronous. It’s not nice to force the use of async
functions just to await
for the version check. We can fix that by adding a versionCheck.sync()
method. We use DeAsync.js. It allows us to wait for async code without blocking the entire event loop.
Wrapping up
With a few lines of code we can improve the UX of our CLI tools. We’ve seen the Javascript/npm version but the same approach can be used with other languages/package managers, like Ruby and RubyGems, Python and pip, etc.