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.
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.
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_environmentA 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 ;;
esacDownstream 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.shVerification 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.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.