DEV Community

Wilson Xu
Wilson Xu

Posted on

The Developer Experience Checklist for CLI Tools

The Developer Experience Checklist for CLI Tools

You shipped a CLI tool. It works. But does it feel good to use? Developer experience (DX) is what separates tools people install once and forget from tools they recommend to their team.

This checklist covers everything that makes a CLI tool professional — from first install to daily use. Each item is a concrete, implementable pattern. No vague advice.

Installation Experience

1. Zero-config first run

The tool should do something useful immediately after install, without creating config files or reading documentation:

npm install -g mytool
mytool scan .  # Just works
Enter fullscreen mode Exit fullscreen mode

2. Helpful --help output

Structure help like a conversation, not a man page:

mytool - Scan your project for common issues

Usage:
  mytool scan [path]       Scan a directory for issues
  mytool fix [path]        Auto-fix what can be fixed
  mytool init              Create a config file

Options:
  -f, --format <fmt>       Output format: text, json, table (default: text)
  -t, --threshold <n>      Fail if score below threshold
  --no-color               Disable colored output
  -v, --verbose            Show detailed information
  -q, --quiet              Only show errors

Examples:
  mytool scan              Scan current directory
  mytool scan src/ --json  Scan src/ and output JSON
  mytool fix --threshold 80  Fix issues, fail if score < 80
Enter fullscreen mode Exit fullscreen mode

Key details: examples section, grouped options, default values shown inline.

3. Version output follows convention

mytool --version
# mytool/1.2.3 node/20.11.0 darwin-arm64
Enter fullscreen mode Exit fullscreen mode

Include the Node.js version and platform — it makes bug reports useful.

program.version(
  `${pkg.name}/${pkg.version} node/${process.version} ${process.platform}-${process.arch}`
);
Enter fullscreen mode Exit fullscreen mode

Output Experience

4. Color that communicates, not decorates

// Good: color conveys meaning
console.log(chalk.green('✓ 42 files passed'));
console.log(chalk.yellow('⚠ 3 warnings'));
console.log(chalk.red('✗ 1 error'));

// Bad: color as decoration
console.log(chalk.blue.bold.underline('=== Results ==='));
console.log(chalk.magenta('Files:') + chalk.cyan(' 42'));
Enter fullscreen mode Exit fullscreen mode

5. Respect NO_COLOR

import chalk from 'chalk';

// chalk v5+ respects NO_COLOR automatically
// But also handle --no-color flag
if (options.noColor || process.env.NO_COLOR) {
  chalk.level = 0;
}
Enter fullscreen mode Exit fullscreen mode

6. Progress for anything over 2 seconds

import ora from 'ora';

const spinner = ora('Scanning files...').start();
// ... work ...
spinner.succeed(`Scanned ${count} files in ${elapsed}ms`);
Enter fullscreen mode Exit fullscreen mode

7. Summary at the end

Always end with a clear summary:

  ✓ 142 files scanned
  ⚠ 3 warnings
  ✗ 1 error (see above)

  Score: 94/100
  Run `mytool fix` to auto-fix 2 of 3 warnings
Enter fullscreen mode Exit fullscreen mode

The last line should tell the user what to do next.

Error Experience

8. Every error suggests a fix

// Bad
throw new Error('ENOENT: no such file');

// Good
throw new CliError('Config file not found: .mytoolrc.json', {
  suggestion: 'Run `mytool init` to create one, or specify a path with --config',
});
Enter fullscreen mode Exit fullscreen mode

9. Distinguish user errors from bugs

try {
  await runTool(args);
} catch (error) {
  if (error instanceof CliError) {
    // User error: clean message, no stack trace
    console.error(chalk.red(`Error: ${error.message}`));
    if (error.suggestion) {
      console.error(chalk.yellow(`  ${error.suggestion}`));
    }
    process.exit(error.code);
  }

  // Bug: show stack trace and ask for report
  console.error(chalk.red('Unexpected error — this is a bug'));
  console.error(error.stack);
  console.error(chalk.gray('\nPlease report: https://github.com/you/tool/issues'));
  process.exit(99);
}
Enter fullscreen mode Exit fullscreen mode

Automation Experience

10. --json for everything

Every command that produces output should support --json:

if (options.json) {
  console.log(JSON.stringify(results, null, 2));
} else {
  printFormattedReport(results);
}
Enter fullscreen mode Exit fullscreen mode

11. Non-zero exit codes

Code Meaning
0 Success
1 Tool found issues
2 Invalid input/config
3 Network error
99 Unexpected error (bug)

12. Stderr for diagnostics, stdout for data

process.stderr.write('Scanning...\n');  // Progress
process.stdout.write(JSON.stringify(results));  // Data
Enter fullscreen mode Exit fullscreen mode

13. Config file support

// .mytoolrc.json
{
  "threshold": 85,
  "ignore": ["vendor/", "dist/"],
  "format": "table"
}
Enter fullscreen mode Exit fullscreen mode

14. Stdin support

cat files.txt | mytool scan --stdin
docker logs app | mytool filter --level error
Enter fullscreen mode Exit fullscreen mode

Performance Experience

15. Fast startup

Users notice if your tool takes over 500ms just to show --help. Minimize top-level imports:

// Bad: imports everything on startup
import lighthouse from 'lighthouse';
import * as chromeLauncher from 'chrome-launcher';

// Good: lazy import only when needed
program.command('audit').action(async () => {
  const { default: lighthouse } = await import('lighthouse');
  const chromeLauncher = await import('chrome-launcher');
  // ...
});
Enter fullscreen mode Exit fullscreen mode

16. Show timing

const start = performance.now();
const results = await runScan(path);
const elapsed = Math.round(performance.now() - start);

console.log(chalk.gray(`  Completed in ${elapsed}ms`));
Enter fullscreen mode Exit fullscreen mode

Update Experience

17. Update notifications

import updateNotifier from 'update-notifier';

const notifier = updateNotifier({ pkg });
notifier.notify(); // Shows message if update available
Enter fullscreen mode Exit fullscreen mode

This prints a non-intrusive notice: "Update available: 1.2.3 → 1.3.0. Run npm i -g mytool to update."

The Complete Checklist

  • [ ] Works without config on first run
  • [ ] --help has examples section
  • [ ] --version shows platform info
  • [ ] Colors convey meaning (green=good, red=bad, yellow=warning)
  • [ ] Respects NO_COLOR environment variable
  • [ ] Progress spinner for operations > 2s
  • [ ] Summary with "what to do next" at the end
  • [ ] Errors include fix suggestions
  • [ ] User errors vs bugs have different output
  • [ ] --json flag for machine-readable output
  • [ ] Non-zero exit codes for failures
  • [ ] Stderr for diagnostics, stdout for data
  • [ ] Config file support (.toolrc.json)
  • [ ] Stdin piping support
  • [ ] Fast startup (< 500ms for --help)
  • [ ] Timing shown for operations
  • [ ] Update notifications

Score yourself: 13+ is excellent, 10-12 is good, under 10 means your users are probably frustrated.


Wilson Xu has published 10+ npm CLI tools, each designed with these DX principles. Find them at npm and follow at dev.to/chengyixu.

Top comments (0)