Bash Scripting — Writing Clean and Readable Code

2 minute read

Introduction

You’ve already learned how to write Bash scripts that work. Now it’s time to write scripts that are easy to read, easy to maintain, and easy to hand off to your teammates (or to your future self six months from now).

Clean code isn’t about being fancy — it’s about being kind to the next person who opens your script. That person might be you, debugging at 2 AM during an outage. Trust me, you’ll thank yourself.

Why Clean Code Matters

Problem Dirty Script Clean Script
Debugging “What does x even hold?” Variables tell you exactly what they store
Onboarding New team member is lost Code reads like documentation
Maintenance One change breaks everything Modular functions, easy to update
Reviews PR takes hours to review Reviewer understands intent in minutes

What You’ll Learn in This Chapter

This chapter is split into focused lessons. Work through them in order:

# Topic What You’ll Learn
1 Naming Conventions How to name variables, functions, and scripts clearly
2 Commenting Best Practices When and how to write useful comments
3 Script Header Block A standard header template every script should have
4 Function Organization Structuring scripts with functions and the main() pattern
5 ShellCheck Compliance Catching bugs before they bite with static analysis
6 Practice Exercises Hands-on exercises to cement everything

The Golden Rules

Before you dive in, keep these five rules pinned to your wall:

  1. Name things clearly — If you need a comment to explain what a variable holds, rename it.
  2. Comment the why, not the what — The code shows what it does; explain why it does it.
  3. Every script gets a header — Author, purpose, usage — no exceptions.
  4. Functions over monoliths — Break logic into small, named functions.
  5. Run ShellCheck on every script — Treat warnings as errors.

A Before-and-After Example

Before — “It works, don’t touch it” style:

#!/bin/bash
d=$(date +%Y%m%d)
f=/tmp/bk_$d.tar.gz
tar czf $f /var/www
if [ $? -eq 0 ]; then
echo "ok"
else
echo "fail"
fi

After — Clean and readable:

#!/usr/bin/env bash
set -euo pipefail

# ---------------------------------------------------------------------------
# backup_website.sh — Create a compressed backup of the web root
# Author: ops-team | Last updated: 2025-06-01
# ---------------------------------------------------------------------------

readonly BACKUP_DIR="/tmp"
readonly SOURCE_DIR="/var/www"
readonly DATE_STAMP
DATE_STAMP="$(date +%Y%m%d)"
readonly BACKUP_FILE="${BACKUP_DIR}/website_backup_${DATE_STAMP}.tar.gz"

create_backup() {
    echo "Creating backup of ${SOURCE_DIR}..."
    tar czf "${BACKUP_FILE}" "${SOURCE_DIR}"
}

main() {
    create_backup
    echo "Backup complete: ${BACKUP_FILE}"
}

main "$@"

Both scripts do the same thing. Which one would you rather debug at 2 AM?

Let’s Get Started

Head to the first lesson: Naming Conventions →


In this chapter you’ll transform your scripts from “it works” to “it’s a pleasure to maintain.”

Updated: