Functions are defined in Bash with name() { commands; } or with the keyword form function name { commands; }. The body of a function executes in the same shell environment as the caller — there is no automatic local scope — so variables assigned inside a function leak into the surrounding scope unless they are declared with local foo=bar, which hides any global of the same name for the duration of the function. Arguments are passed as positional parameters $1, \(2, ..., exactly as in a script, and they are forwarded correctly with "\)@". From Bash 4.3 onward, arrays may be passed by reference using declare -n refname=array. A function exits with return N (where N defaults to the status of the last command), in contrast to exit N, which terminates the entire script — a subtle but important distinction.
Two related mechanisms for modularization are source script.sh (or its POSIX alias . script.sh) and the getopts builtin. source executes a file in the current shell, so any variable assignments or directory changes made by that file persist in the caller — this is why source is the standard way to load a configuration file of Bash variables. There is no separate include mechanism in Bash: modules are sourced, and there is no harm in doing so because source is precisely identical to the dot operator. getopts, by contrast, parses short command-line options such as -a or -b value from a script's own argument list. The typical idiom is while getopts "ab:c" opt; do case \(opt in ...) esac; done, where the option string declares which letters take arguments (those followed by :). The argument to the current option is in \)OPTARG, and $OPTIND points past the consumed options; the usual cleanup is shift $((OPTIND-1)) after the loop, so that "$@" still contains the remaining positional arguments. Before calling any external tool, the robust check for its presence is command -v cmd >/dev/null 2>&1, not the legacy which.
A subtle issue worth knowing is that strict mode does not protect you from a misleading cd. set -e exits when a later command fails for whatever reason, which can hide a failed cd that was supposed to be the cause. The defensive idiom is cd /path || exit 1 or pushd /path || exit to make the directory change itself visible to error handling. The : builtin is a useful no-op that returns 0 and discards its arguments; it is handy as a placeholder in empty function bodies and is the canonical left-hand side of parameter expansion tricks such as : "${VAR:=default}".