Error handling is an essential part of every Go program, but let’s face it — checking if err != nil over and over again can get tedious. While Go’s simplicity is a virtue, repetitive boilerplate code is not.
But what if, in some cases, you didn’t have to? What if exiting early was actually the right choice?
In this article, I’ll introduce gobail, a small Go library I wrote to make early exits simpler and cleaner. If you’ve ever been frustrated by repeated if err != nil blocks or struggled with testing os.Exit, this approach might save you time and reduce clutter.
When Should You Exit Early?
Exiting might seem like a last resort, but sometimes it’s the right call. Here’s when it makes sense:
- Nothing else can be done — The error is critical, and recovery isn’t practical.
- No cleanup is required — There’s nothing to close, no files to sync, and no network calls to complete.
- Restarting is simpler — If your app runs under systemd, a container, or another process manager, exiting may be the fastest way to "recover"—the system restarts you.
If one or more of these conditions apply, exiting early can make your code simpler and more maintainable.
The Traditional Approach to Exiting in Go
Here’s the familiar Go pattern:
err := myFunc()
if err != nil {
fmt.Printf("doing my thing: %v", err)
os.Exit(1)
}This works fine for small cases, but it has a few limitations:
- Repetition — Every error-prone call requires an if err != nil block.
- Testing problems — os.Exit ends your program, including your tests.
If you want to make os.Exit testable, you might introduce a custom exit function, like this:
var customExit = os.Exit
func myFunc() {
err := someOtherFunc()
if err != nil {
fmt.Printf("doing my thing: %v", err)
customExit(1)
}
}
This approach works, but it's verbose and easy to get wrong. Can we do better?
How gobail Makes Exiting Cleaner
With gobail, you don’t need custom exit functions, and you can avoid all those repeated if err != nil blocks. Here’s how the previous example looks using gobail:
func myFunc() {
gobail.Run(someOtherFunc()).
OrExit("doing my thing")
}Cleaner, isn’t it? No repeated if statements, no os.Exit(1) calls—just clear, readable logic.
Handling Single and Multi-Value Returns
Not all Go functions return a single error. Some return (value, error) pairs, and gobail supports both.
Single-value returns:
value := gobail.Return(someFuncThatReturnsError()).
OrExit("something went wrong")Multi-value returns:
value1, value2 := gobail.Return2(return2ValuesAndError()).
OrExitMsg("something went wrong: %v")Both examples clean up error handling, and with gobail, you can add meaningful context to error messages.
Exiting vs. Panicking
Exiting isn’t always the right move. Sometimes, you want the program to panic instead, especially if you’re debugging. gobail supports this too.
Instead of exiting, you can have the program panic like this:
gobail.Run(someOtherFunc()).OrPanic("something went wrong")This gives you the context of a panic while still keeping your main logic clean. It’s especially useful for debugging and development, while you might prefer OrExit production.
Why Use gobail?
- Cleaner code — Say goodbye to repetitive if err != nil blocks.
- Better testing — Linters catch unhandled errors, so you can ensure all error paths are properly addressed.
- Proven reliability — Fully tested and coverage-tracked, gobail is safe to use in production.
A Deeper Look
When you remove repetitive if err != nil logic, your functions become cleaner and easier to follow. gobail encourages you to decide up front which errors are critical enough to exit on and which ones should be handled.
From a testing perspective, you don’t need to mock os.Exit or somehow intercept exit paths. Linters can catch any unhandled errors in your code and prompt you to consider using gobail.
Finally, gobail is fully tested. This gives you confidence that, whether you’re exiting or panicking, gobail will behave predictably.