skip to content

The Compatibility Promise

Go 1 promises working programs keep compiling, but never covered map iteration order, gofmt output, command-line tools or security fixes. Interviewers ask where that line falls.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

4

What does the Go 1 compatibility promise guarantee when you move to a newer Go toolchain?

level: juniorimportance: should knowfreq 38%

answer

  1. written policy, not folklore
  2. it binds exactly two things
  3. source level, so you rebuild
  4. the library grows, never shrinks
  5. spec plus documented stdlib API

basics

~20 s

Go 1 promises that source building and running correctly under one Go 1.x release keeps building and running under later ones. It binds the language specification and the documented standard-library API, so upgrading is normally just a recompile.

solid answer

~40 s

The Go 1 compatibility document says a program written to the Go 1 specification, using the documented standard-library API, should keep compiling and running correctly with every later Go 1.x toolchain. It is a *source-level* promise: Go has no stable ABI, so you rebuild from source rather than relink old object files. That is why the standard library only ever grows — `slices` and `maps` arrived in Go 1.21, `math/rand/v2` in Go 1.22, `encoding/json/v2` in Go 1.27 — instead of existing packages changing shape. Documented API is never deleted; obsolete API keeps working and gains a `Deprecated:` paragraph. The promise is deliberately narrow, though: it covers the spec and the documented API, not tool behaviour, not performance, and not behaviour the specification leaves unspecified.

go deeper

for a junior

Be ready to state it in one sentence: source that builds and runs correctly under one Go 1.x release should still build and run under later ones, after a recompile.

for a middle

Explain what the promise binds — the language specification and the documented standard-library API — and why that forces additive evolution such as math/rand/v2 instead of changing the old package.

for a senior

Show how you use it: upgrades are routine and low-ceremony, so the real work is auditing the narrow set of things the promise never covered before you roll a fleet forward.

for a principal

Own the argument to the business. The promise is why staying on a current toolchain is a rebuild rather than a project, and why freezing versions for years costs more than it saves.

## What the document actually says Go ships a short policy document, usually called the Go 1 compatibility promise, that commits to this: a program written to the Go 1 language specification, using the documented API of the standard library, will continue to compile and run correctly, unchanged, with later Go 1.x releases. Two nouns carry the whole thing — the **language specification** and the **documented standard-library API**. Anything outside those two is not promised, and the document says so explicitly rather than leaving it to be discovered. Notice what "Go 1" means here. It is not a version you install; it is the name of the compatibility epoch. Go 1.21, Go 1.25 and Go 1.27 are all "Go 1" for this purpose. There is no planned breaking successor release: the stated strategy is to keep evolving inside Go 1 by adding, never by redefining. ## It is a promise about source, not about binaries Go has no stable ABI. Compiled packages and binaries are tied to the exact toolchain that produced them, and you cannot link an object file built by one release into a program built by another. So the promise is: take your source, install a newer toolchain, rebuild, and the program still works. An upgrade is a recompile plus a test run, not a migration project. This is the practical reason Go upgrades are cheap, and it is the point candidates most often get backwards — "my old binary keeps working" is true only in the trivial sense that a statically linked binary keeps running; it is not what the promise is about. ## Why the standard library only grows If documented API can never break, there is exactly one way to fix a standard-library design mistake: add a replacement beside it and leave the original in place. That is visible all over the tree: - `slices` and `maps` (Go 1.21) added generic helpers rather than changing anything in `sort`. - `math/rand/v2` (Go 1.22) is a new import path with a cleaner API, because `math/rand` itself could not be changed. - `encoding/json/v2` (Go 1.27) shipped alongside `encoding/json` rather than redefining it. - Old API such as `io/ioutil` still exists — now as thin wrappers over `os` and `io` — carrying a `Deprecated:` paragraph in its documentation. A `Deprecated:` marker under this policy means "no longer recommended, will not be improved". It does not mean "scheduled for removal", because removal is precisely what the promise forbids. ## What "correctly" does and does not cover The promise is about programs that were **correct** to begin with. It does not promise the same performance, the same memory footprint, the same goroutine interleaving, the same garbage-collection timing, or the same output for behaviour the specification declares unspecified. Go 1.24 replaced the map implementation with Swiss Tables and Go 1.26 turned on the Green Tea collector by default; both changed how programs behave in time and memory, and neither touched a documented API, so neither is a compatibility break. The document also lists explicit carve-outs — security fixes, fixes to bugs in the compiler, runtime or libraries, specification errors, use of `unsafe`, and the tooling around the language. Tools were never in scope at all: `gofmt`'s output has changed, `go test` runs vet checks it did not use to run, and `go tool doc` was removed in Go 1.26. ## Why this is worth understanding The promise is the reason a Go service that nobody touched for three years usually rebuilds on the current toolchain with no edits, which in turn is the reason staying current on security patches is cheap. The cost is paid by the language: Go carries its old mistakes forever, and the standard library accumulates API nobody should use any more. That is a trade the Go team made deliberately, and being able to state both sides of it — cheap upgrades, permanent cruft — is what an interviewer is listening for.

  • Does the promise mean an old compiled Go binary keeps working with a new toolchain?
    That is not what it covers. Go has no stable ABI, so object files and packages built by one release cannot be linked by another. The promise is about source: rebuild the same source with the newer toolchain and the program should still compile and behave correctly. An existing statically linked binary keeps running, but that is unrelated to the policy.
  • If documented standard-library API is never removed, how does Go fix an API it regrets?
    By adding a replacement beside it. A new import path is a new package, so existing callers are untouched: `math/rand/v2` arrived in Go 1.22 next to `math/rand`, and `encoding/json/v2` shipped in Go 1.27 next to `encoding/json`. The old package stays, usually with a `Deprecated:` paragraph, and keeps working indefinitely.
  • Is a breaking Go 2 coming that resets this?
    The stated direction is no. Go keeps evolving inside the Go 1 compatibility epoch by adding — generics, new packages, new builtins — in ways that existing code does not have to react to. Planning as though a breaking major release will arrive and force a rewrite is not a bet the language's history supports.

saying these in an interview costs you the question

  • Claims old compiled binaries link against a new toolchain
  • Thinks the promise covers performance and memory use
  • Says deprecated standard-library functions are removed later
  • Believes gofmt output and go vet checks are frozen too
  • Treats it as a vague intention rather than a written policy
open as a page

What does the Go 1 compatibility promise explicitly leave itself free to change?

level: middleimportance: should knowfreq 45%

basics

~20 s

The promise excludes unspecified behaviour such as map iteration order and error message text, plus security fixes, bug fixes, tool behaviour and performance. Additive API changes are allowed too, and adding a struct field breaks unkeyed composite literals.

open as a page

A Go service untouched for years is rebuilt on a modern toolchain and one test now fails: how do you triage it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Assume the code relied on something Go never promised. Read the compatibility notes for each skipped release and classify the failure: unspecified behaviour, a fixed bug, a newly run vet check, or changed tool output. Then fix the dependency, not the toolchain.

open as a page

In the Go standard library, what does a Deprecated: marker on a function change for callers?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

Nothing at build time. A Deprecated: paragraph is a documentation convention: the code still compiles, still runs and still gets security fixes. Under the Go 1 promise it is never removed, and the replacement ships alongside it.

open as a page