What's new in Go, 1.24 → 1.27
Four releases, two years of changes, and most of them you can ignore. These are the three per release that change how you write Go day to day, each with code you can paste and run. Newest first.
Go 1.27 · August 2026
Methods get their own type parameters, and JSON gets a v2.
01Generic methods
A method can now declare type parameters of its own, not just use its receiver's. Helpers that had to be package-level functions can live on the type. The standard library already uses it:
(*rand.Rand).Ninmath/rand/v2. Interface methods still can't be generic.stack.go1type Stack[T any] struct{ items []T }23func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }45// Map declares its own type parameter U.6func (s *Stack[T]) Map[U any](f func(T) U) []U {7 out := make([]U, 0, len(s.items))8 for _, v := range s.items {9 out = append(out, f(v))10 }11 return out12}1314var s Stack[int]15s.Push(1)16s.Push(2)17fmt.Printf("%q\n", s.Map(strconv.Itoa)) // ["1" "2"]02encoding/json/v2 is a real package
The JSON rewrite that was behind
GOEXPERIMENT=jsonv2in 1.25 is now a standard package, with stricter defaults. Two that change real programs: a nil slice encodes as[]instead ofnull, and object keys match struct fields case-sensitively. The same code under v1 prints"tags":nulland fillsNamewith"x".json.go1import "encoding/json/v2"23type Item struct {4 Name string `json:"name"`5 Tags []string `json:"tags"`6}78b, _ := json.Marshal(Item{Name: "heap"})9fmt.Println(string(b)) // {"name":"heap","tags":[]}1011var it Item12_ = json.Unmarshal([]byte(`{"Name":"x"}`), &it)13fmt.Printf("%q\n", it.Name) // "" ("Name" no longer matches "name")03uuid in the standard library
A new
uuidpackage generates and parses RFC 9562 UUIDs, so one of the most common third-party imports is gone.uuid.New()is the general-purpose choice;uuid.NewV7()gives time-ordered IDs, which index well as database keys.id.go1import "uuid"23a, b := uuid.NewV7(), uuid.NewV7()4fmt.Println(len(a.String()), a.String() < b.String()) // 36 true: v7 sorts by time
Full list: Go 1.27 release notes.
Go 1.26 · February 2026
Smaller syntax wins, and a new garbage collector by default.
01new(expr)
newnow takes an expression as well as a type, so a pointer to a value is one call. The everyday case: optional fields as pointers, which used to need a temporary variable or a helper likefunc ptr[T any](v T) *T.person.go1type Person struct {2 Name string `json:"name"`3 Age *int `json:"age,omitempty"`4}56p := Person{Name: "Ada", Age: new(36)}7b, _ := json.Marshal(p)8fmt.Println(string(b)) // {"name":"Ada","age":36}02errors.AsType
A generic, type-safe
errors.As: no target variable declared up front, no pointer-to-a-pointer, and the match is scoped to theif.open.go1_, err := os.Open("/no/such/file")23// before:4// var pe *fs.PathError5// if errors.As(err, &pe) { ... }67if pe, ok := errors.AsType[*fs.PathError](err); ok {8 fmt.Println("failed path:", pe.Path) // failed path: /no/such/file9}03Green Tea GC is on by default
The garbage collector that was an experiment in 1.25 is now the default. It marks and scans small objects with better memory locality, and the release notes expect 10–40% less GC overhead in programs that collect a lot. Nothing to change in your code; opt out with
GOEXPERIMENT=nogreenteagcif you measure a regression.
Full list: Go 1.26 release notes.
Go 1.25 · August 2025
Concurrency gets easier to write and to test.
01sync.WaitGroup.Go
wg.Go(f)doesAdd(1), starts the goroutine and callsDonefor you, which removes the classic bug of callingAddinside the goroutine. Combined with per-iteration loop variables (Go 1.22), the closure below is safe as written.squares.go1var wg sync.WaitGroup2results := make([]int, 3)3for i := range 3 {4 wg.Go(func() { results[i] = i * i })5}6wg.Wait()7fmt.Println(results) // [0 1 4]02testing/synctest
Test time-dependent concurrent code without real waiting. Inside
synctest.Test, goroutines run in a bubble with a fake clock that jumps forward whenever every goroutine is blocked. The test below sleeps for ten minutes and finishes in milliseconds.expiry_test.go1func TestExpiry(t *testing.T) {2 synctest.Test(t, func(t *testing.T) {3 start := time.Now()4 time.Sleep(10 * time.Minute) // the fake clock advances instantly5 if got := time.Since(start); got != 10*time.Minute {6 t.Fatalf("elapsed %v", got)7 }8 })9}03Container-aware GOMAXPROCS
On Linux,
GOMAXPROCSnow defaults to the lower of the machine's CPUs and the container's cgroup CPU limit, and updates if the limit changes. A service limited to 2 CPUs on a 64-core node no longer runs 64 threads' worth of parallelism into throttling. No code change.
Full list: Go 1.25 release notes.
Go 1.24 · February 2025
A new map, generic aliases, and honest benchmarks.
01Maps are Swiss Tables
The built-in
mapwas reimplemented as a Swiss Table: slots in groups of eight, each with a one-byte control word. A lookup checks a whole group's tags in one operation and compares keys only where a tag matches. Same API, faster maps; step through a lookup:One map lookup in a Swiss Tablem["gopher"]→ hash →H1 = group 1H2 = 0x5agroup 0- 0x12rust
- empty·
- 0x40zig
- empty·
- empty·
- 0x07c
- empty·
- empty·
group 1- 0x5agolang
- 0x21java
- empty·
- 0x33kotlin
- 0x5agopher
- empty·
- 0x0cocaml
- deleted·
1 / 4Hash the key once. hash("gopher") is split in two: the high bits (H1) choose a group, the low 7 bits (H2) are a tag.
Illustrative values. Before Go 1.24, a map was buckets of 8 with overflow chains, and a lookup compared the tophash of each slot one at a time. 02Generic type aliases
A type alias can take type parameters, like a defined type. Useful for short names over generic types without creating a new, distinct type.
set.go1type Set[T comparable] = map[T]struct{}23seen := Set[string]{}4for _, w := range []string{"go", "rust", "go"} {5 seen[w] = struct{}{}6}7fmt.Println(len(seen)) // 203testing.B.Loop
for b.Loop()replacesfor i := 0; i < b.N; i++. Setup before the loop runs once and is not timed, and the compiler can't optimise away the work inside it, which made the old form easy to get wrong.sum_test.go1func BenchmarkSum(b *testing.B) {2 xs := make([]int, 1000) // setup: runs once, not timed3 for b.Loop() {4 sum(xs)5 }6}
Full list: Go 1.24 release notes.
Learning DSA in current Go
Reach For teaches data structures, algorithms and system design for interviews in modern Go: every code example in the course is compiled and run against Go 1.27 before it ships. Start with the free modules, or see why the course uses Go.