This is from vps-lxc, my LXC container manager. The rule: register a cleanup function when a step creates something that may need undoing. A step that can partially succeed needs its handler in place before starting. Registration prepends:
# prepend exception handler
vps_register_handle_fatal () {
VPS_FATAL=("${1}" "${VPS_FATAL[@]}")
}Prepending is the whole ordering discipline. The fatal path walks the array front to back, so the newest handler runs first—a LIFO unwind with no index arithmetic. And the unwind fires for more than explicit errors, because both interrupt signals route into the same fatal path:
trap 'vps_exception ${LINENO} fatal "caught SIGTERM"' TERM
trap 'vps_exception ${LINENO} fatal "caught SIGINT"' INTCtrl-C halfway through a build is a partial-operation failure like any other, and it gets the same rollback. The dispatcher itself—the vps_exception machinery that collects a stack trace along the way—is quoted in Containers in 2010.
The order matters in practice, not just in principle. mktpl registers vps_mktpl_fatal, whose inverse is deleting the half-built template. The Arch upgrade profile then bind-mounts /dev, /sys, and /proc into the template’s rootfs—but only after registering a second handler whose inverse is unmounting them. If pacman fails inside that window, the stack is two deep and unwinds newest-first: unmount the host’s /dev out of the rootfs, then delete the rootfs. Run those in the other order and the cleanup is the disaster.
Success pops the handler, and the removal is its own bash trick:
# remove exception handler
vps_unregister_handle_fatal () {
# this trick will remove a handler from the stack
# we start from the back (%) as this will significantly
# reduce the possiblitly of a partial match. the array
# is then repacked (WITHOUT using quotes, else the missing
# item would be included in the pack)
VPS_FATAL=(${VPS_FATAL[@]%${1}})
}${array[@]%word} strips word as a suffix from every element. An exact match becomes the empty string, and because the repack is deliberately unquoted, the empty word vanishes from the new array. The comment explains both choices: suffix matching from the back to reduce partial-match odds, and the missing quotes as load-bearing.
The gotcha is inherited from that trick: it strips a suffix from every element, so a handler name that is the tail of another handler’s name would corrupt the survivor, and names must be single words. The codebase’s vps_<command>_fatal convention kept it out of trouble, but it is a convention, not a guarantee.
I announced vps-lxc on lxc-users. Years later the same idea came back with pop-by-count deregistration in atexit for bash.