Why Go’s Simple Design Sparks Both Praise and Criticism

An in‑depth look at Go’s origins, design goals, and the trade‑offs that provoke both admiration for its simplicity in large‑scale server development and criticism over its limited generics, file‑system API, error handling, and FFI capabilities.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Why Go’s Simple Design Sparks Both Praise and Criticism
Haha, no generics — an old programmer proverb.

In many ways, 2009 planted the seed for my future career. I was 13, scoring my first soccer goal with a powerful shot into the top‑left corner, only to miss the scout’s attention. While dreaming of Wembley, the Go language was born.

Go quickly attracted a large following for its simplicity, its optimization for web services, and handy tools like gofmt. Yet, like any language, it has a downside: some complain it is too simple, relegated to “clunky” REST APIs, and criticize its overly enthusiastic tooling.

Over the past 15 years, many have voiced harsh criticism of Go. Two articles stand out: “I Want to Escape the Wild Journey of Mr. Golang” and “The Lies We Tell Ourselves to Keep Using Go” by fastthanlime. The latter bluntly states:

So they (Go Core Team) didn’t design the language. It just “appeared”.

What Exactly Is Design?

To me, design is a plan or specification that achieves a goal. For example, the BBC news site aims to inform users of globally relevant events; it writes articles and orders them by relevance, because a missile heading toward me is far more important than a cat on a tree.

Thus, judging a design’s quality depends on how well it meets its intended objectives.

Origin of the Go Language

Go was created at Google by veterans such as Russ Cox, Rob Pike, and Ken Thompson. At the time, Google’s internal codebase relied heavily on Java and C++. The designers found these languages performant but cumbersome: slow compilers, picky tools, and outdated designs. Meanwhile, cloud computing—massively parallel servers—was becoming widespread.

They decided to build a language tailored for large‑scale computation and collaborative development. Rob Pike explained in “Go at Google”:

Hardware scales massively, and so does software. Millions of lines of code run on servers mostly written in C++, with the rest in Java and Python, involving thousands of engineers.

In another remark, Pike modestly noted that their programmers are Google engineers, not researchers, and that a highly sophisticated language would be beyond their needs.

Important note: If you are designing, avoid belittling or condescending toward your audience.

The reasonable design goal emerges: the language should make writing and maintaining large concurrent server code easy, even for thousands of developers with varying skill levels.

Criticisms of Go

Let’s examine common complaints and evaluate them against Go’s design goals.

File System API

Go’s file‑system API is often criticized for being Unix‑centric. Windows lacks Unix‑style permissions, so Go can only return superficial permission data. Path handling is simple: a path is a string —just a byte slice with no checks (the upcoming Go 1.24’s os.ROOT will improve this).

Other languages are stricter. In Rust, fetching a file’s modification time may return None. Zig’s file metadata varies by OS.

From a design perspective, this bias is understandable: Go was built for Google’s Linux‑based servers, so a Unix‑focused file API is a sensible choice for a server‑oriented language.

No Operator or Function Overloading

Unlike Java, Go allows only a single definition for a function or method. Operators are baked into the compiler and cannot be overloaded. For example, adding a Duration to a Time requires the Add method, not a generic Add(0, 0, 2) call; you must use AddDate instead.

Some view this as inelegant, but it promotes clarity: you know exactly where a function is defined, and any operator you see works on built‑in types, never on exotic overloads.

Cumbersome Error Handling

Modern languages favor brevity, so many programmers dislike Go’s verbose if err != nil pattern.

However, this choice is deliberate:

Although Go’s error checking is more verbose, the explicit design makes control flow crystal‑clear.

Explicit error handling improves readability; while exception‑based languages can write faster code, they often hide control flow deep inside the stack.

Go is frequently criticized for avoiding exceptions, with some accusing it of “going backward.” Others question why Go ignores decades of type‑system research.

Rob Pike dismisses such arrogance, stating that Go was created to solve Google’s software‑development pain points, making it a solid choice for large projects even if it isn’t a groundbreaking research language.

Go aims to solve the difficulties Google faces in software development, so while it isn’t a pioneering research language, it excels at large‑scale software projects.

Explicit error values have become a trend; Go, Rust, and Zig all adopt this approach. Swift, even with exceptions, requires functions to declare which may fail.

Pitiful FFI Capabilities

FFI (Foreign Function Interface) allows a language to call functions written in another language.

Go’s compatibility with other languages is limited. To call C functions (e.g., SQLite), you must use CGO, which introduces performance overhead. CGO isn’t pure Go and adds cost because goroutine stacks must conform to C expectations.

Go’s own compiler, linker, and debugger are custom, making interoperability harder.

Nevertheless, this trade‑off aligns with Go’s design goal: server software must support concurrency, so goroutines are prioritized over simple inter‑process calls.

These decisions give Go tooling an edge—its compiler is fast, and the debugger understands goroutines and all built‑in types.

So Is Go Great?

That’s subjective. Personally, I enjoy Go; the code I write is usually easy to read and understand. It forces me to write practical code rather than abstract fluff, and I’ve successfully taught it to many recent graduates.

However, I don’t ignore its flaws. Once a client’s error went unnoticed because we didn’t check it, leading to a costly investigation. Enabling a linter would have prevented it. Go’s historic lack of generics makes generic data structures cumbersome. Frequent Windows‑specific error reports make me wonder if Go gives a false sense of security.

In the end, these issues stem from intentional trade‑offs. You may dislike Go, find it unsuitable for a particular scenario, or even hate it, but you shouldn’t claim the language is fundamentally badly designed.

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.

GoProgramming LanguageDesign
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.