What's new in Go 1.27

17th September 2026 · Max Craig

Benefits of Go

Go gopher
Type safety
High performance
Native concurrency
golang/go GitHub repository card

Go Version Adoption

“How many people are using the 2 latest releases of Go?”
Public GitHub Packages
164
1.10
2,100
1.11
23,040
1.12
31,808
1.13
37,888
1.14
34,496
1.15
43,008
1.16
54,976
1.17
63,232
1.18
68,224
1.19
75,008
1.20
132,864
1.21
168,960
1.22
159,232
1.23
170,496
1.24
169,984
1.25
90,368
1.26
11,520
1.27
Go Version
7.6% out of 1.2 million Golang Packages on Github are using version 1.26 or 1.27

Go 1 Compatibility

“It is intended that programs written to the Go 1 specification will continue to compile and run correctly, unchanged, over the lifetime of that specification.”

go.dev/doc/go1compat

1. I can freely upgrade my Go toolchain and use new language features without worrying about unexpected behaviour.
2. I can leave my code exactly as it is once I've written it.

Upgrading Your Toolchain

Performance

Runtime and compiler improvements.

Fewer dependencies

More features in the standard libraries.

Fewer vulnerabilities

Bugs and Security patches.

Developer experience

New language features and tooling.

Go 1.27

Released 19th August
Features
  • Generic Methods
  • Struct Literals
  • JSON
  • UUID
  • SIMD
  • Post Quantum
  • CutLast

Generics

Generic Methods
  • Generics added in Go 1.18 (2022)
  • Go 1.27 adds support for generic methods
  • One method now replaces many type-specific ones
math/rand
Before
func (r *Rand) Int32N(n int32) int32
func (r *Rand) Int64N(n int64) int64
func (r *Rand) IntN(n int) int
// ...repeated per integer type
After
type intType interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint |
    ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

func (r *Rand) N[Int intType](n Int) Int

Generics

Not on Interfaces
  • Interface methods can't declare type parameters
type Foo interface {
    Bar[T any](v T) T // not allowed
}
  • Generic methods can’t satisfy interface methods
type Foo interface {
    Bar(v int) int
}

type FooImpl struct{}

func (FooImpl) Bar[T any](v T) T { return v }

var _ Foo = FooImpl{} // doesn't compile

Struct Literals

Implicit Fields
  • Embedded fields settable without naming the type
  • Less boilerplate when initialising
Types
type Habitat struct {
    Burrow string
}

type Gopher struct {
    Name string
    Habitat
}
Struct literal
// before
g := Gopher{
    Name:    "Gopher",
    Habitat: Habitat{Burrow: "Burrow #42"},
}

// after
g := Gopher{
    Name:   "Gopher",
    Burrow: "Burrow #42",
}

Struct Literals

Reshaping Without Refactoring
Before
type Identity struct { Age int }

type Gopher struct {
    Name string // I want to move this
    Identity
}
pre-1.27
g := Gopher{
    Name: "Gopher",
    Identity: Identity{
        Age: 13,
    },
}
1.27
g := Gopher{Name: "Gopher", Age: 13}
After Name moved into Identity
type Identity struct {
    Age  int
    Name string // Moved
}

type Gopher struct { Identity }
pre-1.27
g := Gopher{
    Identity: Identity{
        Name: "Gopher", // have to move the field everywhere
        Age:  13,
    },
}
1.27 (unchanged)
g := Gopher{Name: "Gopher", Age: 13}

encoding/json v2

  • New encoding/json/v2 package which can be imported separately
  • encoding/json (v1) now runs on the v2 engine internally, unchanged behaviour
  • Marshalling is (up to) roughly 2.6 times faster. Unmarshalling is (up to) roughly 2.5 times faster
  • Stricter error handling is available as part of encoding/json/v2

encoding/json v2

No Automatic Sorting
  • Map keys are no longer sorted when marshalled
  • Re-enable it with the Deterministic option
Before
import "encoding/json"

m := map[string]int{"z": 1, "a": 2}
json.Marshal(m)
// {"a":2,"z":1}
After
import "encoding/json/v2"

m := map[string]int{"z": 1, "a": 2}
json.Marshal(m)
// {"z":1,"a":2} (unsorted)

json.Marshal(m, json.Deterministic(true))
// {"a":2,"z":1} (opt back in)

encoding/json v2

Stricter Defaults
  • Invalid UTF-8 now errors instead of being replaced
  • Duplicate object keys now error
  • Unmatched fields are ignored unless you opt in to RejectUnknownMembers
Rejected by encoding/json/v2
// duplicate key
{
    "Name": "a",
    "Name": "b"
}

// invalid UTF-8
{
    "Name": "\xff\xfe"
}
Ignored, not rejected
// wrong case
{
    "name": "a"
}
// opt in: json.RejectUnknownMembers(true)

encoding/json v2

Nil Slices & Maps
  • nil slices/maps used to marshal to null
  • v2 marshals them as empty [] / {} instead
  • Matches what most JSON consumers expect
Before
import "encoding/json"

type T struct {
    Items []int
    Meta  map[string]int
}

json.Marshal(T{})
// {"Items":null,"Meta":null}
After
import "encoding/json/v2"

type T struct {
    Items []int
    Meta  map[string]int
}

json.Marshal(T{})
// {"Items":[],"Meta":{}}

UUID

New stdlib Package
  • New stdlib uuid package (RFC 9562)
  • V4 and V7 supported
  • v1 / v3 / v5 and namespaced UUIDs are not
V4 — random
id := uuid.NewV4()
// 3f29a1d2-6b7e-4c1a-9f3d-8e2b7a4c5d6f
V7 — time-ordered
id := uuid.NewV7()
// 018f4d2a-7b3c-7e21-9f4a-1234567890ab
Example
id := uuid.NewV4()

id = uuid.NewV7()

id, err := uuid.Parse(s)

id.String()

UUID

Replacing google/uuid
google/uuid GitHub repository card
  • Used by 477k+ GitHub repositories today
google/uuid → stdlib uuid
V4
uuid.New()
uuid.NewV4()
V7
uuid.NewV7()
uuid.NewV7()
Compare
bytes.Compare(u[:], v[:])
u.Compare(v)

SIMD

Single Instruction, Multiple Data
  • SIMD is data-level parallelism, not thread-level like goroutines
  • One instruction executes across many data elements at once
  • Uses built-in hardware instructions to perform vector operations
Diagram of SIMD: a single instruction pool feeding multiple processing units, each operating on its own slice of a shared data pool

SIMD

  • New experimental package for hardware SIMD instructions
  • An abstraction over simd/archsimd (Go 1.26)
  • No need to define vector shape or target architecture
  • Enable with GOEXPERIMENT=simd
archsimd
var a, b archsimd.Int32x8 // AVX2, 8 lanes
a = archsimd.LoadInt32x8(s1) // load 8 int32s
b = archsimd.LoadInt32x8(s2) // load 8 int32s
a.Add(b).Store(dst) // add + store
simd
a := simd.LoadInt32s(s1) // width inferred
b := simd.LoadInt32s(s2)
a.Add(b).Store(dst) // add + store

Post-Quantum Signatures

  • New crypto/mldsa package implementing ML-DSA
  • crypto/x509, crypto/tls also implement ML-DSA
  • TLS 1.3 auth, CA certificate signing
Example
priv, _ := mldsa.GenerateKey(mldsa.MLDSA65())
pub := priv.PublicKey()

opts := &mldsa.Options{}
sig, _ := priv.Sign(nil, message, opts)
err := mldsa.Verify(pub, message, sig, opts)
Industry deadline: classical algorithms deprecated by 2030
Source: NIST IR 8547

CutLast

Changes
  • New CutLast in bytes and strings
  • Splits around the last occurrence of a separator
Before
i := bytes.LastIndex(s, sep)
before, after := s[:i], s[i+len(sep):]
After
before, after, found := bytes.CutLast(s, sep)
Use Cases
File extension
before, after, _ := bytes.CutLast(s, []byte("."))
// "archive.tar.gz" ->
// before="archive.tar", after="gz"
Path segment
dir, file, _ := bytes.CutLast(s, []byte("/"))
// "/var/log/app.log" ->
// dir="/var/log", file="app.log"

What Wasn't Covered

  • Goroutine leak profiling
  • Faster memory allocation
  • Go tool, link, fix changes

See the complete release notes

https://go.dev/doc/go1.27