Exit
Keep cleanup in a run function; report errors from main and exit nonzero after cleanup completes.
os.Exit terminates a process immediately with a status code. Deferred functions do not run, so call it only at the program boundary after cleanup decisions are complete.
A run function can return errors while its deferred cleanup still runs. main reports the error and chooses the process exit code.
package main
import (
"errors"
"fmt"
"os"
)
func run() error {
if len(os.Args) < 2 {
return errors.New("usage: program <name>")
}
fmt.Println("hello,", os.Args[1])
return nil
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}In production
Use zero for success and a non-zero status for failure so shells and CI can detect errors. Avoid log.Fatal inside a running service because it also calls os.Exit and skips deferred cleanup. Keep exit decisions in main or another top-level command boundary.
Enjoyed this? Get more software craft delivered to your inbox.