Currently available · INDIA
← Writing
Languages·Aug 7, 2026·5 min read

Why Google Invented Go Instead of Improving C++

A case study of the problems that created Go — compile time, dependency hell, memory safety, concurrency, and why Go intentionally left features out. Interactive timeline and scenario lab.

0

Why Google Invented Go Instead of Improving C++

In 2007, Robert Griesemer, Rob Pike, and Ken Thompson started designing a language at Google. The question wasn't “how do we make C++ cooler?” It was:

> What language would we invent if we optimized for Google-scale engineering — compile speed, dependency clarity, and code that thousands of people can read?

This post is a case study, not a feature checklist. Work through the timeline, open the “intentionally omitted” explorer, and pressure-test when Go actually fits.

Have you written production Go?

---

Start here · The case study timeline

Google wasn't failing because engineers were “bad at C++.” They were hitting organizational physics: huge codebases, slow builds, complex deps, and concurrency that punished mistakes.

Case study · Why Google built Go

Step through the problems that made “just improve C++” the wrong answer

2007 · Google

The scale problem

Millions of lines of C++ and Java. Builds took minutes to hours. Waiting on compile blocked entire teams.

At Google's size, language design is also an operations problem — slow feedback loops cost more than clever syntax saves.

Treat compile latency as a product metric. Languages that stay fast under huge dependency graphs win org-wide adoption.

---

The five pressures (map)

PressureSymptom at scaleGo's bet
Compile speedMinutes–hours of waitingFast package builds
Dependency hellCascading rebuilds, unclear ownershipExplicit imports, boring modules
Memory safetyCostly bugs in C++GC + simple rules
ConcurrencyThreads/locks or callback spaghettiGoroutines + channels
ClevernessDialects, hero codeSmall language, uniform style
DimensionC++ (typical large codebase)Go design bet
Compile / feedbackCan explode with headers & templatesFast, package-oriented builds
DependenciesComplex build graphs, include cascadesExplicit imports; boring module story
MemoryManual control; high footgun densityGC + simple pointer rules
ConcurrencyThreads, locks, async frameworksGoroutines + channels (CSP)
Feature surfaceHuge language; many dialectsSmall language; uniform style
Optimized forMaximum control & expressivenessReading & maintaining at org scale

Not “C++ bad, Go good” — different optimization targets. C++ still wins where you need zero-cost abstractions and fine-grained control.

Go can feel like it is “missing features” at first. That is often the point. Language design is also governance: fewer knobs means fewer incompatible dialects across teams.

---

The features Go refused

Most language tours start with a feature list. That is the wrong entrance for Go.

Go is easier to understand if you ask what it *refused* to include — inheritance, exceptions, operator overloading, macros, and (at first) generics — and sit with the reason for each refusal. The common thread is not austerity for its own sake. It is optimizing for reading strangers' code under a deadline, not for writing the cleverest expression.

Explore each choice below. Pick one topic at a time; read it like a short design memo.

1 of 5

Inheritance

What people wanted. Reuse behavior by extending base classes the way C++ and Java encourage.

Why Go said no. Deep hierarchies get brittle. Shared mutable state in parents is hard to reason about when hundreds of people touch the same tree.

What you do instead. Compose structs and depend on small interfaces. You embed when you need fields; you satisfy an interface just by having the methods.

Prefer has-a over is-a. Start from the behavior you need, then name an interface for it. Implicit interfaces decouple packages without a shared base type — strong for API boundaries, weaker for “find every subclass” navigation.

Composition still needs clear ownership. Over-embedding can surprise readers as much as inheritance did.

You can steal this habit even if you never ship Go: when you evaluate a tool, ask what it left out on purpose, and what that buys a team at scale.

---

Mini case studies

1 · Compilation as a product feature

Situation: A C++ change touches a widely included header. Half the monorepo rebuilds. Reviewers context-switch for coffee.

Go's answer: Compilation speed is a design constraint. Packages form a clear DAG. You feel it as short feedback loops.

Takeaway: Feedback latency is a first-class engineering cost — language choice affects it.

2 · Dependency hell

Situation: Build files encode folklore. Transitive versions fight. Onboarding means learning the build system before the domain.

Go's answer: Make imports obvious; keep the dependency story dull. Dull is a feature when hundreds of teams share a repo.

3 · Concurrency for multicore servers

Situation: Network services need lots of concurrent I/O. Thread pools and shared mutable state are footguns.

Go's answer: Cheap goroutines + channels (CSP-inspired). Concurrent style can look almost sequential — still requires discipline with sharing and cancellation (context).

go
func fetchAll(ctx context.Context, urls []string) []Result {
    ch := make(chan Result, len(urls))
    for _, u := range urls {
        u := u
        go func() {
            ch <- fetchOne(ctx, u)
        }()
    }
    out := make([]Result, 0, len(urls))
    for range urls {
        out = append(out, <-ch)
    }
    return out
}

Readable concurrency was a design goal — not an add-on library.

4 · Simplicity over cleverness

Situation: Expert C++ can be beautiful and unreadable. Average C++ in a huge org drifts into incompatible styles.

Go's answer: Fewer ways to say the same thing. gofmt ends style debates. The language refuses some power to protect the median engineer.

---

Scenario lab · Should you use Go?

Go isn't a religion. Match constraints.

Scenario lab · When Go fits

Pick a situation. The point isn't hype — it's matching constraints.

Many small services, lots of networking, mixed experience on the team.

Strong Go fit

Fast builds, simple deployment (static binary), solid stdlib net/http, easy concurrency for I/O.

Operational simplicity compounds: one binary, predictable performance envelope, easy onboarding.

---

Things worth trying

  1. Read through the omitted-features topics — practice questioning “missing” features.
  2. Write a tiny HTTP handler and a fan-out goroutine example.
  3. Trace an error up the stack until explicit failure feels normal.
  4. Sketch one class hierarchy you know as interfaces + composition instead.
  5. In your next design doc, start from the scale problem: compile latency, dialect sprawl, or concurrency model — not from a feature wishlist.
  6. Prefer strangler extraction over “rewrite in Go because it is simpler.”

---

Key takeaways

  1. Go was invented because improving C++ wouldn't fix Google's compile, dependency, and uniformity problems.
  2. Design goals: fast builds, clear deps, safe-enough memory, approachable concurrency, small language.
  3. Feature omission is intentional — ask why not inheritance, exceptions, macros, overloading, early generics.
  4. Generics arriving later proves the point: sequence the complexity; don't ship the whole kitchen on day one.
  5. Go wins for many services, CLIs, and I/O-heavy systems — not every domain.
  6. The transferable skill: evaluate tools by scale pressures, not feature bingo.

---

Go did not win by being the most powerful language on paper. It won by making the expensive parts of large-team engineering — waiting on builds, decoding strangers' code, and coordinating concurrency — a little less expensive. Whether you adopt Go or not, that design lesson travels: optimize for the cost your organization actually pays.

/ Stay in the loop

Get the next one in
your inbox.

New essays and field notes sent only when there's something worth sending. No tracking, no spam, easy unsubscribe.

Join · No spam · One-click unsubscribe