Why Go’s Error Handling, Defer, and Nil Design Feel Like Bad Ideas

The article critiques Go’s language design by highlighting unnecessary patterns such as overly broad error‑variable scopes, confusing nil semantics, cumbersome defer usage, hidden memory costs, and a lack of clear ownership, illustrating each point with concrete code examples and comparing alternatives in other languages.

21CTO
21CTO
21CTO
Why Go’s Error Handling, Defer, and Nil Design Feel Like Bad Ideas

Incorrect variable scope and forced errors

The author shows how Go forces developers to write repetitive error‑handling boilerplate, arguing that limiting a variable’s scope to just a few lines would improve readability. Example:

if err := foo(); err != nil {
  return err
}

Later code demonstrates how the same err variable can be unintentionally reused across multiple calls, leading to bugs and confusion.

bar, err := foo()
if err != nil { return err }
if err = foo2(); err != nil { return err }
// …

The author questions why the language does not allow tighter scoping.

Two types of nil

A small program illustrates surprising equality results when comparing nil interfaces and pointers, emphasizing that Go’s handling of nil can be misleading.

package main
import "fmt"

type linterface{}

type S struct{}

func main() {
  var il linterface{}
  var s *S
  fmt.Println(s, il)
  fmt.Println(s == nil, il == nil, s == il) // true, true, false
  il = S{}
  fmt.Println(s, il)
  fmt.Println(s == nil, il == nil, s == il) // true, false, true
}

The confusion stems from Go’s lack of thoughtful design around nil.

It’s not portable

Adding conditional compilation comments at the top of files is criticized as a poor practice that hampers portability; the author likens it to “locking yourself in a room and never testing assumptions against reality.”

append without clear ownership

Examples show how appending to a slice via a subslice can produce unexpected results because the underlying array is shared:

package main
import "fmt"

func foo(a []string) {
  a = append(a, "NIGHTMARE")
}

func main() {
  a := []string{"hello", "world", "!"}
  foo(a[:1])
  fmt.Println(a) // [hello NIGHTMARE !]
}

A second example demonstrates that the same operation can leave the original slice unchanged, highlighting the unintuitive ownership model.

package main
import "fmt"

func foo(a []string) {
  a = append(a, "BACON", "THIS", "SHOULD", "WORK")
}

func main() {
  a := []string{"hello", "world", "!"}
  foo(a[:1])
  fmt.Println(a) // [hello world !]
}

defer is stupid

The author argues that Go’s defer mechanism forces developers to consult documentation for each resource, leading to error‑prone patterns such as double‑closing files or forgetting to defer when needed.

foo, err := myResource()
if err != nil { return err }
defer foo.Close()

Similar boiler‑plate appears when writing to files, making code verbose and fragile.

Standard library swallows exceptions

Go claims to have no exceptions, yet the standard library often hides errors, forcing developers to write “exception‑safe” code without actual exception support. This leads to awkward patterns like unlocking mutexes with defer and worrying about panics leaving resources locked.

func (f *Foo) foo() {
  f.mutex.Lock()
  defer f.mutex.Unlock()
  f.bar()
}

Sometimes not UTF‑8

When arbitrary binary data is placed into a Go string, the runtime accepts it silently, which can cause data loss for non‑UTF‑8 filenames or content, a problem the author has experienced in backups.

Memory usage

Although memory is cheap, the author cares about it because services run on cloud instances where RAM is billed. Go’s garbage collector may retain more memory than expected; manual triggering with runtime.GC() is discouraged by the standard library, yet sometimes necessary.

runtime.GC()

The author rewrote parts of a project in another language because newer Go versions consumed increasingly more memory.

Too many unnecessary things

The article concludes that many of Go’s design choices feel like “stupid” shortcuts that were not thoughtfully considered, comparing them to outdated scientific methods that ignore real‑world testing.

Author: 手扶托拉斯基 Reference: https://blog.habets.se/2022/08/Java-a-fractal-of-bad-experiments.html

Related reading:

Go、Python、Rust:我们该学习哪一款?

JetBrains 正在开发更高抽象的编程语言

十个最受欢迎的本地语言大模型

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Error Handlinglanguage designnildefer
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

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.