The three shapes for a shell shortcut differ on two axes: reachability (which shells can find it) and logic (whether it takes arguments or branches).

An alias is pure text substitution on the first word of a command. No arguments, no control flow. It lives in the interactive rc file (.zshrc), and zsh disables alias expansion in non-interactive mode anyway, so zsh -c, scripts, and tools that spawn a non-interactive shell never see it. Use it only for a static rename you invoke by hand.

A function is a named block that takes $@, branches, and runs in the current shell process (so it can cd or set env vars). It has the same reachability limit as an alias unless sourced: interactive-only by default. Use it when you need logic but must mutate the calling shell.

A script is an executable file with a shebang on $PATH, run in its own subprocess. Because $PATH lookup is universal, every shell finds it. Use it when anything non-interactive must call it.

The deciding question is reachability: if a non-interactive shell (CI, a cron job, an agent’s tool) must run it, it has to be a script, regardless of how simple the body is. This is the failure mode behind an hm = "home-manager switch --flake ..." alias that works when typed but reports “command not found” from a non-interactive bash tool. Related: aliases and functions are loaded at shell init, where Subshells in Shell Init Are Both Slow and Fragile.