Chapter 20Lesson 01~105 minutes

Script Architecture, Modules, and Configuration Layers

The final chapter moves from individual techniques to complete production design. Large Bash tools become manageable when architecture, configuration, modules, and process-level error policy are explicit.

AdvancedProduction design & capstoneHands-on lab

Learning objectives

By the end of this lesson

  • Structure orchestration around a main function.
  • Create sourceable modules without accidental side effects.
  • Define configuration precedence.
  • Normalize configuration early.
  • Keep hidden mutable state small.

1. Production scripts need architecture

A production Bash tool benefits from explicit layers: entrypoint, configuration, validation, reusable modules, orchestration, tool adapters, observability, and cleanup.

Production Bash architecture
flowchart LR
  E["entrypoint"] --> C["configuration"]
  C --> V["validation"]
  V --> O["orchestration"]
  O --> A["tool adapters"]
  O --> R["results and logs"]
  O --> X["cleanup"]

2. Put orchestration in main

main() {
  parse_args "$@" || return $?
  load_config || return $?
  validate_config || return $?
  preflight || return $?
  run_release || return $?
}

main "$@"

A main function creates one visible control-flow entrypoint and makes testing easier.

3. Source modules that define functions, not side effects

SCRIPT_DIR=$(
  cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &&
  pwd -P
) || exit 1

source "$SCRIPT_DIR/lib/logging.sh"
source "$SCRIPT_DIR/lib/config.sh"

A sourced module should normally define reusable behavior and avoid mutating production merely because it was loaded.

4. Define configuration precedence

environment=${DEFAULT_ENVIRONMENT:-staging}
[[ -n ${CONFIG_ENVIRONMENT:-} ]] && environment=$CONFIG_ENVIRONMENT
[[ -n ${DEPLOY_ENV:-} ]] && environment=$DEPLOY_ENV
[[ -n ${cli_environment:-} ]] && environment=$cli_environment

A documented order such as defaults → config → environment → CLI makes behavior predictable.

5. Normalize once, then rely on invariants

dry_run=${dry_run:-false}
replicas=${replicas:-2}

case $environment in
  dev|staging|prod) ;;
  *) printf 'invalid environment\n' >&2; return 65 ;;
esac

Downstream functions should receive validated values rather than repeatedly handling unset or malformed inputs.

6. Keep global mutable state small

Use local variables and explicit parameters where practical. Hidden global state makes tests, reuse, and recovery harder.

7. Use a repository layout that mirrors the architecture

scripts/
  release.sh
  lib/
    logging.sh
    config.sh
    git.sh
    deploy.sh
  test/
    release.bats
    fixtures/

8. Hands-on lab: modular release skeleton

mkdir -p "$HOME/devops-academy/bash/chapter20/lesson01/lib"
cd "$HOME/devops-academy/bash/chapter20/lesson01"

cat > lib/logging.sh <<'EOF'
log_info() {
  printf 'level=INFO msg=%q\n' "$*" >&2
}
EOF

cat > lib/config.sh <<'EOF'
load_config() {
  ENVIRONMENT=${DEPLOY_ENV:-staging}
}

validate_config() {
  case $ENVIRONMENT in
    dev|staging|prod) return 0 ;;
    *) return 65 ;;
  esac
}
EOF

cat > release.sh <<'EOF'
#!/usr/bin/env bash
set -u
set -o pipefail

SCRIPT_DIR=$(
  cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &&
  pwd -P
) || exit 1

source "$SCRIPT_DIR/lib/logging.sh"
source "$SCRIPT_DIR/lib/config.sh"

main() {
  load_config
  validate_config || return $?
  log_info "release environment=$ENVIRONMENT"
}

main "$@"
EOF

chmod u+x release.sh
DEPLOY_ENV=staging ./release.sh

Verification checklist

9. Knowledge check

Question 1. Why use a main function?

Question 2. What should sourceable modules avoid?

Question 3. Why define configuration precedence?

Question 4. Why minimize global state?

10. Summary

Production Bash architecture separates entrypoint, configuration, validation, modules, orchestration, and cleanup. Normalize inputs early and keep module behavior predictable.

11. Further reading

  • GNU Bash Reference Manual — functions and sourcing.
  • ShellCheck documentation — sourced files.
  • Bats documentation for sourceable modules.
  • Software modularity principles applied to shell tooling.
Next

Observability, Metrics, Logs, and Machine-Readable Output

Continue the final chapter by combining production Bash patterns into a complete operational design.

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.