Why You Should Hate ‘else’ in Go: Dave Cheney’s Surprising Coding Rules

The article distills Dave Cheney’s provocative Go coding guidelines—abandoning else, using anonymous structs, simplifying conditionals, and isolating main logic—showing how these counter‑intuitive habits reduce cognitive load, improve testability, and lead to clearer, more maintainable backend code.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Why You Should Hate ‘else’ in Go: Dave Cheney’s Surprising Coding Rules

1. “If is bad, else is worse”

Cheney recommends initializing a variable with a safe default and overriding it only in production, thereby eliminating the need for an else branch. This prevents the variable from ever being nil after refactoring.

var thing *Thing
if os.Getenv("ENV") == "production" {
    thing = NewRealThing()
} else {
    thing = NewMockThing()
}

A safer version gives the variable a default value first:

thing := NewMockThing() // default safe value
if os.Getenv("ENV") == "production" {
    thing = NewRealThing() // override only in production
}

Encapsulating the logic in a helper function makes the code reusable and unit‑testable:

func newThing(env string) *Thing {
    if env == "production" {
        return NewRealThing()
    }
    return NewMockThing()
}

// usage
thing := newThing(os.Getenv("ENV"))

For multiple environment branches, a switch statement is preferred over chained if/else for clarity.

2. Sometimes the best name is no name

When decoding a one‑off JSON payload, use an anonymous struct directly inside the handler instead of defining a named type that lives only for a few lines.

func MyHandler(w http.ResponseWriter, r *http.Request) {
    var payload struct {
        Name  string `json:"name"`
        Value int    `json:"value"`
    }
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        // handle error
        return
    }
    // validate payload and build domain object...
}

The anonymous struct never escapes the function, keeping the package namespace clean and reducing cognitive overhead.

3. Write conditionals for your brain, not just the compiler

Prefer if status > 399 over if status >= 400. The > operator requires a single mental check (“greater than 399?”) whereas >= forces the reader to consider two cases (greater than 400 or equal to 400), increasing short‑term memory load.

4. main.run pattern: isolate application logic

Fundamentally, if something is hard to discuss, it’s hard to use.
func main() {
    if err := run(os.Stdout, os.Args); err != nil {
        fmt.Fprintf(os.Stderr, "%s
", err)
        os.Exit(1)
    }
}

// All application logic lives here.
func run(stdout io.Writer, args []string) error {
    // parse flags, set up dependencies, run the app...
    // return an error if something goes wrong
    return nil
}

The run function receives all dependencies explicitly and returns an error, making the core logic a plain, testable Go function without hidden global state.

Conclusion

Avoiding else, using anonymous structs, simplifying conditionals, and moving work out of main into a dedicated run function together reduce cognitive load and produce clearer, more maintainable Go code.

References

GopherCon Europe: https://www.youtube.com/watch?v=RZe8ojn7goo

software engineeringGobest practicescode quality
Radish, Keep Going!
Written by

Radish, Keep Going!

Personal sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.