Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go 1.27

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Go 1.27

Go 1.27 is a release where the parts that matter most are not the language features. We will start with the three changes most likely to reach a service already in production: `go test` now runs the `stdversion` vet check by default, `encoding/json/v2` graduated from experiment so the classic `encoding/json` runs on the new implementation with nothing to import, and two quieter behaviour changes — time channels are now always unbuffered, and `http.Response.Body` drains itself on close.
Then the parts worth adopting deliberately: `uuid` in the standard library, the `goroutineleak` profile graduating alongside goroutine labels that now appear in crash dumps and `SIGQUIT` tracebacks, `httptest.NewTestServer` running without real TCP ports, and the new `go fix` modernizers for mechanical migrations. Performance comes last — size-specialized allocation makes small allocations up to 30% faster, worth roughly 1% overall and 60 KB of extra binary — followed by the language changes themselves: generic methods, promoted-field selectors in struct literals, and wider function type inference.

By attending the event I agree that photographs and video recordings from this event may be used by Sky Czech Republic for employer branding and promotional purposes across company websites, career pages, social media, third-party profiles, and other communication materials.

Avatar for Ladislav Prskavec

Ladislav Prskavec

September 23, 2026

More Decks by Ladislav Prskavec

Other Decks in Technology

Transcript

  1. Timeline - 10.2.2026 - Go 1.26 is released - 21.5.2026

    - Introducing the pkg.go.dev API - 7.2026 - Go 1.27 RC1 - 19.8.2026 - Go 1.27 is released - 1.9.2026 - Go 1.27.1 is released - 16.9.2026 - Size-specialized memory allocation - 23.9.2026 - our meetup ;-) 2 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  2. Language changes - generic methods - methods can declare type

    parameters - struct literal keys can be any field selector - generalized function type inference 4 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  3. Generic methods [1/3] The problem in 1.26: a method cannot

    introduce a new type parameter, so anything that maps T -> U had to escape to package scope. // Go 1.26 type Stack[T any] struct{ items []T } func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) } // U cannot live on the method, so it lives on the package func MapStack[T, U any](s *Stack[T], f func(T) U) *Stack[U] { out := &Stack[U]{} for _, v := range s.items { out.Push(f(v)) } return out } ▶ Run on the Go Playground 5 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  4. Generic methods [2/3] // Go 1.27 func (s *Stack[T]) Map[U

    any](f func(T) U) *Stack[U] { out := &Stack[U]{} for _, v := range s.items { out.Push(f(v)) } return out } names := ids.Map(func(id int) string { return strconv.Itoa(id) }) ▶ Run on the Go Playground - the standard library already uses it: (*rand.Rand).N[Int](Int) Int in math/rand/v2 - Caveat: interface methods still cannot declare type parameters - Caveat: a generic method cannot implement an interface method 6 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  5. Generic methods [3/3] Stack.Map only ever went int -> string

    once. The real payoff is calling the same method with a different U each time - a Result[T] chain does that in one expression. type Result[T any] struct { value T err error } func Ok[T any](v T) Result[T] { return Result[T]{value: v} } func (r Result[T]) Unwrap() (T, error) { return r.value, r.err } func (r Result[T]) Map[U any](f func(T) U) Result[U] { if r.err != nil { return Result[U]{err: r.err} } return Ok(f(r.value)) } label := Ok(42). Map(func(age int) bool { return age >= 18 }). Map(func(adult bool) string { if adult { return "adult" } return "minor" }) // int -> bool -> string: three instantiations of Map, one method ▶ Run on the Go Playground 7 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  6. Struct literal keys Promoted fields from an embedded struct are

    now valid keys. type Base struct{ ID int } type User struct { Base Name string } // Go 1.26 - you have to spell out the embedded type u := User{Base: Base{ID: 1}, Name: "abtris"} // Go 1.27 u := User{ID: 1, Name: "abtris"} 8 golang/go#9859 - open since 2015 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  7. Generalized function type inference Inference now also works when you

    assign or convert a generic function. func Map[T, U any](s []T, f func(T) U) []U { /* ... */ } // Go 1.26 - explicit instantiation required var f func([]int, func(int) string) []string = Map[int, string] // Go 1.27 - inferred from the target type var f func([]int, func(int) string) []string = Map 9 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  8. encoding/json/v2 The headline standard library change. - new encoding/json/v2 -

    options-based API - new encoding/json/jsontext - token/value level, the syntax layer - encoding/json v1 is now backed by the v2 implementation - unmarshal is significantly faster, marshal at parity - escape hatch: GOEXPERIMENT=nojsonv2 10 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  9. json: options instead of tricks // Go 1.26 - unknown

    fields need a Decoder dec := json.NewDecoder(r) dec.DisallowUnknownFields() if err := dec.Decode(&u); err != nil { /* ... */ } // Go 1.27 import json "encoding/json/v2" err := json.UnmarshalRead(r, &u, json.RejectUnknownMembers(true)) - Marshal, MarshalWrite, MarshalEncode - Unmarshal, UnmarshalRead, UnmarshalDecode - all of them take ...Options 11 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  10. json: options you will actually use json.Deterministic(true) // maps sorted

    by key json.RejectUnknownMembers(true) // strict decoding json.MatchCaseInsensitiveNames(false) json.FormatNilSliceAsNull(true) // nil slice -> null, not [] json.StringifyNumbers(true) jsontext.WithIndent(" 12 ") // formatting lives in jsontext Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  11. json: omitzero vs omitempty type Event struct { Name string

    `json:"name"` At time.Time `json:"at,omitzero"` // uses IsZero() Tags []string `json:"tags,omitempty"` // omitted when it encodes as [] } - v1 omitempty never worked for structs - time.Time{} always got serialized - stricter defaults in v2: invalid UTF-8 rejected, duplicate object names rejected 13 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  12. New package: uuid No more github.com/google/uuid in go.mod for the

    simple case. import "uuid" id := uuid.New() id = uuid.NewV7() // v4 today // 48-bit timestamp, sorts by creation time id, err := uuid.Parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6") fmt.Println(id.String(), id.Compare(uuid.Nil())) - UUID is [16]byte, so == and map keys just work - implements TextMarshaler / TextUnmarshaler / TextAppender 14 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  13. Runtime: goroutine leak profile Experimental in 1.26, generally available in

    1.27. import _ "net/http/pprof" go tool pprof http://localhost:6060/debug/pprof/goroutineleak - reports goroutines blocked on a primitive that cannot be unblocked - detection rides on the GC: if the channel/mutex is unreachable from any runnable goroutine, the waiter is dead - blind spot: primitives reachable through globals or locals of runnable goroutines 15 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  14. What it catches func leak() { ch := make(chan int)

    // never written to, never escapes go func() { <-ch // blocked forever }() } - goroutine profile: shows the goroutine, you decide whether it is a leak - goroutineleak profile: the runtime proved nobody can wake it up 16 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  15. Runtime: allocation is cheaper - the runtime generates a mallocgc

    variant per span class, for objects < 80 bytes - 20-30% faster on those allocations, ~1% on allocationheavy programs overall - opt out: GOEXPERIMENT=nosizespecializedmalloc (going away in 1.28) 17 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  16. Why it is faster // one generated function per (size

    class, pointers?) pair func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Pointer - size class is known at compile time - no runtime lookup - zeroing becomes a fixed-size store, not a memclr call - helpers inlined by hand (well, by a generator) - 80 bytes is the cutoff: more specializations would push your code out of the instruction cache 18 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  17. Runtime: labels in tracebacks pprof.Do(ctx, pprof.Labels("tenant", tenantID), func(ctx context.Context) {

    handle(ctx) }) - for modules on go 1.27, panics and tracebacks now print runtime/pprof goroutine labels - turn off with GODEBUG=tracebacklabels=0 19 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  18. Tooling: go fix modernizers go fix ./... - new: atomictypes,

    embedlit, slicesbackward, unsafefuncs - removed: fmtappendf (stylistic complaints) - renamed: waitgroup -> waitgroupgo // slicesbackward for i := len(s) - 1; i >= 0; i-- { use(s[i]) } // becomes for _, v := range slices.Backward(s) { use(v) } 20 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  19. Tooling: go doc go doc example.com/[email protected] go doc -ex bytes

    go doc bytes.ExampleBuffer 21 # read docs for a version you do not depend on # list executable examples # print the example source Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  20. Tooling: go test, go mod go test ./... # now

    runs the stdversion vet check by default - stdversion flags standard library symbols newer than your go directive - go test -json lines carry an "OutputType" field: error, errorcontinue, frame - go mod tidy on go 1.27+ merges duplicate require blocks into two: direct + indirect - go tool trace -http=:6060 now binds localhost only 22 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  21. Small things that remove code // bytes, strings base, ext,

    ok := strings.CutLast(name, ".") // replaces: i := strings.LastIndex(name, "."); if i >= 0 { ... } // net/url u2 := u.Clone() v2 := values.Clone() // deep copy, no more re-parsing u.String() // testing/synctest synctest.Sleep(time.Second) // = time.Sleep + synctest.Wait // math/big - QuoRem and DivMod gave you two rounding modes, now there are four q, r := new(big.Int), new(big.Int) q.Divide(x, y, r, big.Floor) // Trunc, Floor, Round, Ceil 23 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  22. net/http - HTTP/1 Response.Body auto-drains unread content on close, up

    to a limit, so the connection can be reused // Go 1.26 - the idiom everyone forgets defer resp.Body.Close() io.Copy(io.Discard, resp.Body) // <- needed for keep-alive // Go 1.27 defer resp.Body.Close() - HTTP/2 server honours RFC 9218 client priorities (Server.DisableClientPriority to opt out) - Server.MaxHeaderValueCount caps the number of header values - httptest.NewTestServer - in-memory server for testing/synctest 24 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  23. Crypto: post-quantum - new crypto/mldsa - ML-DSA signatures (FIPS 204)

    - crypto/x509 parses and verifies ML-DSA keys and signatures - crypto/tls supports MLDSA44, MLDSA65, MLDSA87 in TLS 1.3 - MLKEM1024 key exchange via Config.CurvePreferences - SystemCertPool now respects SSL_CERT_FILE / SSL_CERT_DIR on Windows and macOS - tls.Config.Rand deprecated - use testing/ cryptotest.SetGlobalRandom 25 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  24. Before you upgrade - macOS 13 Ventura minimum - older

    versions dropped - bzr support removed from the go command - asynctimerchan GODEBUG gone - time channels are always unbuffered - removed GODEBUGs: tlsunsafeekm, tlsrsakex, tls3des, tls10server, x509keypairleaf, gotypesalias - compress/flate output bytes may differ (better speed) - check golden files - Unicode 15 -> 17 - linux/ppc64 now builds ELFv2, with cgo and PIE 26 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  25. Experimental: simd GOEXPERIMENT=simd go build ./... - simd - portable,

    vector-size-agnostic: Int8s, Float32s, ... - simd/archsimd - architecture specific, now with arm64 Neon and wasm 128-bit - proposal #78902 27 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24
  26. References - go.dev/doc/go1.27 - Go 1.27 is released - the

    announcement post - encoding/json/v2 docs - Migrating to v2 - go.dev/issue/77273 - generic methods - go.dev/issue/9859 - struct literal keys - Size-specialized memory allocation in Go - Michael Matloob 28 Ladislav Prskavec (Everpure) - @[email protected] - Go Meetup Prague #24