DEV Community

Rost
Rost

Posted on

Go 1.25 release - what's new

#go

Go 1.25, released in August 2025, brings a variety of enhancements across the toolchain, runtime, standard library, and internal compiler behavior—yet preserves Go’s backward-compatibility promise.

Below, we walk through the major changes (and some subtler ones), with sample code to illustrate the impact and usage.


Language Changes

First, on the language side:

  • No user-visible language features are added in Go 1.25. The language remains stable.
  • Internally, the spec’s treatment of “core types” has been simplified: the notion of core types is removed in favor of prose. But this is not a breaking change for Go programs.

So there’s no new syntax to learn, but many improvements under the hood.


Go Tooling

go build / go command

  • The -asan option (AddressSanitizer) now defaults to leak detection on exit. That means if your program (via C code) allocates memory not reachable by either C or Go, the runtime will now report it as an error. You can disable via
  export ASAN_OPTIONS=detect_leaks=0
Enter fullscreen mode Exit fullscreen mode

during runtime.

  • The Go distribution ships fewer precompiled tools. Tools not commonly invoked (i.e. not part of build or test) will be built on-demand via go tool.

  • The new go.mod ignore directive allows specifying directories to be ignored by the go command when matching patterns like ./... or all. Those paths are still included in module zips. Example:

  // in go.mod
  ignore tools/
  ignore scripts/
Enter fullscreen mode Exit fullscreen mode
  • A new go doc -http option starts a local documentation server for the requested object and opens it in the browser. Useful for exploring docs offline.

  • A new go version -m -json prints the embedded runtime/debug.BuildInfo structures in a binary in JSON form.

  • The go command now supports using a subdirectory of a repository as a module root (via <meta name="go-import" … subdir>). This lets you host a module that is not at the root of a VCS repo.

  • The work module pattern now matches all packages in the “work” (previously “main”) modules. This unifies behavior between workspace mode and single-module mode.

  • When go updates the go version line in go.mod / go.work, it no longer injects a toolchain annotation.


go vet Enhancements

Two new analyzers are added:

  1. waitgroup analyzer: catches misuses of sync.WaitGroup.Add (e.g. calling Add(…) after spawning goroutines) which can lead to race-like behavior.
  2. hostport analyzer: flags code that builds network addresses via fmt.Sprintf("%s:%d", host, port), which fails under IPv6 contexts, and suggests using net.JoinHostPort.

Example (hostport):

host := "127.0.0.1"
port := 8080
addr := fmt.Sprintf("%s:%d", host, port)  // vet may warn
conn, err := net.Dial("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
Enter fullscreen mode Exit fullscreen mode

Runtime & GC

Container-aware GOMAXPROCS

  • On Linux, the default GOMAXPROCS now respects cgroup CPU bandwidth limits (if set). If the cgroup limit is lower than the machine’s logical CPUs, Go uses the lower value.
  • On all OSes, the runtime now periodically updates GOMAXPROCS if available CPUs or cgroup constraints change at runtime.
  • These new behaviors are bypassed if the user has explicitly set GOMAXPROCS (via env var or runtime.GOMAXPROCS) or disabled via GODEBUG flags containermaxprocs=0 or updatemaxprocs=0.

This is a significant improvement for containerized workloads, enabling Go apps to adjust to CPU quotas more gracefully.

Experiment: New Garbage Collector (greenteagc)

  • Go 1.25 introduces an experimental GC, selectable via
  GOEXPERIMENT=greenteagc
Enter fullscreen mode Exit fullscreen mode
  • Its design improves locality, parallelism, and scanning of small objects; in some benchmarks it reduces GC overhead by 10–40% in GC-heavy workloads.
  • Because it's experimental, feedback is encouraged.

Trace Flight Recorder

  • The new runtime/trace.FlightRecorder API provides a lightweight in-memory ring buffer that records execution traces. Programs can snapshot the buffer when needed via WriteTo. This enables capturing only the recent events (e.g. around a failure) rather than continuously dumping full traces.

Example sketch:

  import (
      "os"
      "runtime/trace"
  )

  func main() {
      rec, err := trace.NewFlightRecorder(trace.FlightRecorderConfig{})
      if err != nil {
          panic(err)
      }
      defer rec.Close()
      // run your program logic...

      // On some event:
      f, _ := os.Create("trace.out")
      rec.WriteTo(f, "")
  }
Enter fullscreen mode Exit fullscreen mode
  • The captured trace is much smaller and more focused.

Panic output change

If a program panics, recovers, and then repanics, the output no longer prints the panic value twice. Instead:

panic: PANIC [recovered, repanicked]
Enter fullscreen mode Exit fullscreen mode

rather than the old:

panic: PANIC [recovered]
  panic: PANIC
Enter fullscreen mode Exit fullscreen mode

This removes redundant repetition.

VMA names on Linux

On Linux kernels with CONFIG_ANON_VMA_NAME, Go will name anonymous VMAs (virtual memory areas) with context like [anon: Go: heap], improving visibility in OS tools. This can be disabled via GODEBUG=decoratemappings=0.

SetDefaultGOMAXPROCS

A new function runtime.SetDefaultGOMAXPROCS() sets GOMAXPROCS to the runtime’s default (i.e. as if user had not overridden it). This is useful if your program initially disabled the dynamic container-aware behavior but later wants to re-enable it.

Cleanup / finalizer changes

  • Cleanups registered via runtime.AddCleanup(...) now run concurrently and in parallel (instead of serially). This makes heavy cleanup more viable.
  • Enabling GODEBUG=checkfinalizers=1 triggers diagnostics on each GC cycle, reporting finalizer queue lengths and related stats to stderr. Useful for detecting finalizer/backlog issues.

Compiler / Linker Changes

Fix for nil pointer bug

A longstanding bug (introduced in Go 1.21) allowed code like:

f, err := os.Open("nonexistent")
name := f.Name()
if err != nil {
    return
}
println(name)
Enter fullscreen mode Exit fullscreen mode

to run without panic—because the compiler delayed the nil check until after the err check. In Go 1.25 this is fixed: the nil-check is not delayed, so this code will correctly panic.

Best practice: always check errors immediately before dereferencing values, e.g.:

f, err := os.Open("nonexistent")
if err != nil {
    return
}
name := f.Name()
println(name)
Enter fullscreen mode Exit fullscreen mode

DWARF5 by default

  • The compiler/linker now emit DWARF v5 debug information, which reduces binary debug-data size and speeds linking (especially on large binaries).
  • You can revert to older DWARF via GOEXPERIMENT=nodwarf5 at build time (temporary fallback).

Faster slices (stack allocation)

  • In more situations, the compiler will now allocate slice backing storage on the stack (instead of the heap). This can reduce heap allocations and improve performance.
  • Caveat: this change makes incorrect usages of unsafe.Pointer more dangerous (aliasing issues etc.). If needed, you can disable this by passing -gcflags=all=-d=variablemakehash=n or use the bisect tool with -compile=variablemake to isolate problematic allocation points.

Linker -funcalign=N

The linker adds a new -funcalign=N option to control function entry alignment. The default alignment remains platform-specific and unchanged, but this gives advanced users control when needed.


Standard Library Changes & Additions

testing/synctest (stable)

The testing/synctest package, formerly experimental, is now generally available. It supports testing of concurrent code with a “virtual time” model:

  • synctest.Test runs a test function in a “bubble”, where the time package is virtualized (i.e. time jumps only when goroutines are blocked).
  • synctest.Wait waits until all goroutines in the bubble are blocked (i.e. quiesced).

This is helpful for deterministically testing concurrency behaviors. (The older API under GOEXPERIMENT=synctest is still supported but deprecated in Go 1.26).

Example:

package mypkg_test

import (
    "testing"
    "time"

    "testing/synctest"
)

func TestSomethingConcurrent(t *testing.T) {
    synctest.Test(t, func(tb *testing.T) {
        go func() {
            time.Sleep(time.Second)
            tb.Log("done sleeping")
        }()
        synctest.Wait(tb)
        // at this point, the bubble has quiesced — no runnable goroutines
    })
}
Enter fullscreen mode Exit fullscreen mode

encoding/json/v2 (experimental)

  • Go 1.25 ships an experimental JSON implementation (enabled via GOEXPERIMENT=jsonv2).
  • When enabled:

    • A new module encoding/json/v2 is available, with a revised API.
    • The existing encoding/json package uses the new implementation. The Marshaling/Unmarshaling behaviors are intended to be backward-compatible (although error message texts might differ).
    • Additional configuration options for the JSON (un)marshaler are provided.
  • The new implementation shows strong performance gains: decoding is often much faster.

  • Users are encouraged to test their code under GOEXPERIMENT=jsonv2 and report compatibility issues.

Library Refinements (select highlights)

Below are some interesting standard library changes:

  • archive/tar: Writer.AddFS now supports symbolic links (if the underlying fs implements io/fs.ReadLinkFS).
  • encoding/asn1: Unmarshal / UnmarshalWithParams parse T61String and BMPString more strictly/consistently; some malformed encodings previously accepted may now error.
  • crypto:

    • Introduces a new interface MessageSigner (for signers that perform hashing internally) and SignMessage to attempt upgrading Signer to MessageSigner.
    • Signatures under FIPS 140-3 mode are now faster (e.g. Ed25519, RSA) — often four times faster.
    • crypto/rsa: key generation is now 3× faster.
  • crypto/elliptic: Removal of hidden/unexported Inverse and CombinedMult methods on some Curve types.

  • crypto/tls:

    • ConnectionState.CurveID exposes the key-exchange mechanism used.
    • A new callback Config.GetEncryptedClientHelloKeys enables configuring Encrypted Client Hello (ECH) keys in TLS.
    • SHA-1 signatures are disallowed in TLS 1.2 by default (can be re-enabled via GODEBUG=tlssha1=1).
    • TLS endpoints now prefer the highest protocol version supported (not just client preference).
  • crypto/x509:

    • Many functions (e.g. CreateCertificate) now accept the new MessageSigner interface.
    • If a certificate lacks SubjectKeyId, it will be computed via truncated SHA-256 rather than SHA-1 (revertable via GODEBUG=x509sha256skid=0).
  • go/ast, go/types, go/parser, go/token: various new methods and deprecations to improve AST traversal, filtering, and type selection APIs.

  • hash: introduces a new XOF interface (extendable-output functions) and ensures all standard Hash implementations also implement hash.Cloner (i.e. can clone internal state).

  • hash/maphash: the new Hash.Clone method implements the Cloner interface.

  • log/slog: GroupAttrs to group attributes, and Record.Source for source location.

  • mime/multipart: new helper FileContentDisposition for Content-Disposition.

  • net:

    • LookupMX and Resolver.LookupMX now return DNS names that look like valid IP addresses (if the name server returns them). Previously such addresses were discarded.
    • On Windows: ListenMulticastUDP now supports IPv6 addresses.
    • Also on Windows: it’s now possible to convert between os.File and network connections (e.g. FileConn, FileListener, etc.), and vice versa (e.g. TCPConn.File). This is especially useful for working with named pipes.
  • io/fs / filesystem APIs:

    • A new interface ReadLinkFS supports symbolic links in virtual file systems.
    • Root (in fs) now supports Chmod, Chown, Chtimes, Link, MkdirAll, Readlink, RemoveAll, Rename, Symlink, WriteFile, etc.
    • CopyFS supports copying symbolic links when FS supports ReadLinkFS.
  • reflect: new TypeAssert function to convert reflect.Value directly to a Go value of the given type, skipping an intermediate allocation.

  • regexp/syntax: extended support in \p{name} / \P{name} classes for names like Any, ASCII, Assigned, Cn, LC, Unicode category aliases, and case-insensitive name lookups.

  • runtime/pprof: mutex profile for runtime-internal locks now points to the end of the critical section (matching behavior for sync.Mutex). The previous runtimecontentionstacks fallback option is removed.

  • sync: new method WaitGroup.Go simplifies common patterns of spawning goroutines with waiting:

  var wg sync.WaitGroup
  wg.Go(func() {
      // do work
  })
  wg.Wait()
Enter fullscreen mode Exit fullscreen mode

This is equivalent to wg.Add(1); go func(){ …; wg.Done() }() but more concise.

  • testing:

    • T.Attr, B.Attr, F.Attr to attach arbitrary key/value metadata to tests (visible in logs).
    • T.Output, B.Output, F.Output return an io.Writer that writes to the test output (like Log) but without file/line prefixes.
    • AllocsPerRun now panics if tests are run in parallel (since results can become flaky).
  • testing/fstest: MapFS now implements io/fs.ReadLinkFS. TestFS will verify ReadLinkFS support (if implemented). TestFS no longer follows symlinks (to avoid unbounded recursion).

  • unicode: new category aliases map (e.g. "Letter" for "L"), inclusion of Cn (unassigned code points) and LC (cased letters) categories. The C (Other) category now includes Cn.

  • unique package: interned values are now reclaimed more aggressively and efficiently, and a chain of Handles no longer requires multiple GC cycles—collection happens in a single cycle. This reduces memory bloat for high-volume use of unique.


Ports, Platforms, Architecture Updates

  • On macOS: Go 1.25 requires macOS 12 Monterey or newer; earlier macOS versions are no longer supported.
  • On Windows/ARM 32-bit: Go 1.25 is the last version to include the broken 32-bit Windows/ARM port (i.e. GOOS=windows GOARCH=arm). It will be removed in Go 1.26.
  • For amd64: in GOAMD64=v3 mode or higher, the compiler now uses fused multiply-add (FMA) instructions when possible, which can change floating-point results slightly (more accurate / faster). To disable FMA, use an explicit cast:
  float64(a*b) + c
Enter fullscreen mode Exit fullscreen mode

rather than a*b + c.

  • On loong64 (Linux): the race detector is now supported, and C stacktraces are gathered via runtime.SetCgoTraceback. Also, cgo programs now support internal linking.
  • On riscv64 (Linux): plugin build mode is now supported. Also, the GORISCV64 environment variable accepts a new mode rva23u64 for a custom application profile.

Migration Guidance & Things to Watch

  • The lack of any new language changes means most existing code should upgrade cleanly.
  • However, code that (incorrectly) relied on delayed nil-pointer checks (e.g. dereferencing struct fields before error checks) may now panic. Audit such code.
  • If your application uses unsafe.Pointer deeply, the increased aggressiveness of stack allocation could expose bugs. If needed, disable via -gcflags or investigate with bisect.
  • Try enabling GOEXPERIMENT=jsonv2 for JSON-heavy workloads; test thoroughly for any change in error semantics.
  • The new sync.WaitGroup.Go simplifies patterns; you might refactor code to use it.
  • For containerized workloads, the smarter GOMAXPROCS default is beneficial, but if you have manually set GOMAXPROCS, consider removing that override to let Go adapt.
  • For debugging/tracing, the new FlightRecorder offers a lighter-weight alternative to full trace logging.

Conclusion

Go 1.25 continues Go’s tradition of improving performance, observability, and container-first behavior, without destabilizing existing programs. The biggest shifts are under the hood: smarter runtime behavior, a more aggressive compiler, experimental GC, new concurrency- and trace-utilities, and evolution of core libraries like JSON.

More details on: https://go.dev/doc/go1.25

Top comments (0)