Chapter 06Lesson 04~75 minutes

Designing Usage, Help, and Validation

Parsing only tells you what tokens the user supplied. A usable CLI must also explain its grammar, validate values, report errors on the correct stream, document defaults, and make safety behavior predictable.

BeginnerCLI designHands-on lab

Learning objectives

By the end of this lesson

  • Separate usage from full help.
  • Layer syntax, semantic, and prerequisite validation.
  • Write actionable diagnostics.
  • Keep help and errors on appropriate streams.
  • Design trustworthy dry-run and production safeguards.

1. Usage and help serve different purposes

SurfacePurposeTypical destination
UsageConcise invocation synopsisUseful after malformed input
HelpOptions, operands, defaults, examplesIntentional user documentation
ErrorCause plus corrective directionNormally stderr
usage() {
  printf 'usage: %s [-n] [-e ENV] [-r N] SERVICE\n' "$0"
}

help_text() {
  usage
  cat <<'EOF'

Plan or execute a service deployment.

Options:
  -n        dry run
  -e ENV    dev, staging, or prod
  -r N      replicas
  -h        help
EOF
}

2. Intentional help belongs on stdout

if [[ $want_help == true ]]; then
  help_text
  exit 0
fi

if [[ -z $service ]]; then
  printf 'error: SERVICE is required\n' >&2
  usage >&2
  exit 64
fi

Automation can then distinguish successful help output from a failed invocation.

3. Validate in layers

Validation pipeline
flowchart TD
  A["parse tokens"] --> B["required values"]
  B --> C["syntax/type"]
  C --> D["semantic policy"]
  D --> E["dependencies"]
  E --> F["mutation"]

Layered validation produces precise diagnostics and keeps dangerous work behind read-only checks.

4. Validate representation before arithmetic

if [[ ! $replicas =~ ^[0-9]+$ ]]; then
  printf 'error: replicas must be decimal digits\n' >&2
  exit 64
fi

replicas=$((10#$replicas))

if (( replicas < 1 || replicas > 100 )); then
  printf 'error: replicas must be 1..100\n' >&2
  exit 65
fi

5. Semantic policy is separate from parsing

case $environment in
  dev|staging|prod) ;;
  *) printf 'error: unsupported environment\n' >&2; exit 65 ;;
esac

if [[ $environment == prod && $tag == latest ]]; then
  printf 'error: production requires immutable tag\n' >&2
  exit 65
fi

6. Check dependencies before state changes

require_command() {
  local name=$1
  command -v "$name" >/dev/null 2>&1 || {
    printf 'error: missing dependency: %s\n' "$name" >&2
    return 69
  }
}

Only require a dependency when the selected command actually needs it; --help should not fail because deployment tooling is absent.

7. Error messages should tell users what to fix

# Weak:
printf 'invalid input\n' >&2

# Better:
printf 'error: --replicas expects 1..100; received %q\n' \
  "$replicas" >&2

8. Keep exit-code contracts small and stable

# Example convention:
# 0  success
# 64 invocation error
# 65 invalid domain value / policy rejection
# 69 unavailable dependency
# 1  operational failure

A descriptive stderr message is usually more useful than dozens of granular numeric statuses.

9. Dry-run must actually avoid mutation

run_or_plan() {
  if [[ $dry_run == true ]]; then
    printf 'DRY-RUN:'
    printf ' %q' "$@"
    printf '\n'
  else
    "$@"
  fi
}
Safety contract

Do not call a mode dry-run if it still changes remote state, writes files unexpectedly, or triggers irreversible API calls.

10. Production safety cannot rely only on prompts

if [[ $environment == prod && $approved != yes ]]; then
  printf 'error: production approval missing\n' >&2
  exit 65
fi

CI has no reliable interactive user. Enforce production policy through explicit configuration, approvals, or external authorization.

11. Help examples clarify grammar

Examples:
  deployctl -e staging api
  deployctl -n -e prod -r 3 payments
  deployctl -h

Examples should remain synchronized with the implementation.

12. Hands-on lab: polished help and validation

mkdir -p "$HOME/devops-academy/bash/chapter06/lesson04"
cd "$HOME/devops-academy/bash/chapter06/lesson04"

cat > deploy-help.sh <<'EOF'
#!/usr/bin/env bash

usage() { printf 'usage: %s [-n] [-e ENV] [-r N] SERVICE\n' "$0"; }

help_text() {
  usage
  cat <<'HELP'
Options:
  -n        dry run
  -e ENV    dev, staging, prod (default: staging)
  -r N      replicas 1..100 (default: 2)
  -h        help
HELP
}

dry_run=false
environment=staging
replicas=2

while getopts ':hne:r:' option; do
  case $option in
    h) help_text; exit 0 ;;
    n) dry_run=true ;;
    e) environment=$OPTARG ;;
    r) replicas=$OPTARG ;;
    :) printf 'error: -%s needs a value\n' "$OPTARG" >&2; usage >&2; exit 64 ;;
    \?) printf 'error: unknown option -%s\n' "$OPTARG" >&2; usage >&2; exit 64 ;;
  esac
done
shift "$((OPTIND - 1))"

service=${1:-}
[[ -n $service ]] || { printf 'error: SERVICE required\n' >&2; usage >&2; exit 64; }
case $environment in dev|staging|prod) ;; *) exit 65 ;; esac
[[ $replicas =~ ^[0-9]+$ ]] || exit 64
replicas=$((10#$replicas))
(( replicas >= 1 && replicas <= 100 )) || exit 65

printf 'PLAN service=%s env=%s replicas=%d dry_run=%s\n' \
  "$service" "$environment" "$replicas" "$dry_run"
EOF

bash deploy-help.sh -h
bash deploy-help.sh -n -e prod -r 3 api
bash deploy-help.sh -r nope api || true

Verification checklist

13. Knowledge check

Question 1. Where should intentional help normally go?

Question 2. What is semantic validation?

Question 3. What guarantee should dry-run make?

Question 4. Why not rely only on confirmation prompts for production?

14. Summary

A predictable CLI documents its grammar, validates in layers, writes intentional help to stdout and errors to stderr, and makes safety features real behavioral guarantees.

15. Further reading

  • POSIX Utility Syntax Guidelines.
  • GNU Coding Standards — command-line interfaces.
  • GNU Bash Reference Manual — getopts and exit status.
  • Command Line Interface Guidelines (clig.dev).
Next lesson

Building Predictable DevOps Command-Line Tools

Continue Chapter 6 by refining the command-line interface and its validation contract.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.