Go 1.25 Release: Features and Innovations

(medium subtitle)

image.png|300

Note: The core content was generated by an LLM, with human fact-checking and structural refinement.

Go 1.25 Release: Features and Innovations

The Go team officially announced the release of Go 1.25 in August 2025, arriving as scheduled, six months after Go 1.24. This latest version primarily focuses on significant enhancements to the toolchain, runtime, and standard libraries. As with previous releases, Go 1.25 upholds the Go 1 compatibility promise, ensuring that nearly all existing Go programs are expected to continue to compile and run without issues. The release underscores Go’s commitment to stability and pragmatic evolution, bringing “unseen benefits” to developers through underlying improvements.

Language Changes

Go 1.25 introduces no language changes that directly affect existing Go programs. This dedication to stability is a core reason for Go’s popularity in production environments. However, an internal “cleanup” occurred within the language specification: the notion of “core types” has been removed in favor of dedicated prose. While this change does not impact day-to-day coding, it simplifies the language specification and is considered a step towards paving the way for future evolutions of generics.

Tools

The Go 1.25 release significantly enhances the Go toolchain, boosting efficiency and reliability.

  • Go Command Improvements:
    • The go build -asan option now defaults to leak detection at program exit, reporting errors if C-allocated memory is unreferenced and not freed. This behavior can be disabled by setting ASAN_OPTIONS=detect_leaks=0 in the environment.
    • The Go distribution will include fewer prebuilt tool binaries, with tools not invoked by build or test operations now being built and run by go tool as needed.
    • A new go.mod ignore directive allows specifying directories that the go command should disregard when matching package patterns like all or ./…. These files are still included in module zip files. This feature is particularly beneficial for large monorepo projects.
    • The new go doc -http option launches a local documentation server, opening the requested object’s documentation in a browser window. This improves offline documentation access.
    • The new go version -m -json option prints JSON encodings of runtime/debug.BuildInfo structures embedded in Go binary files.
    • The go command now supports using a subdirectory of a repository as a module root, enabling more flexible module path resolution via the <meta name="go-import" content="root-path vcs repo-url subdir"> syntax. This addresses a long-standing challenge for monorepo managers and custom vanity imports.
    • A new work package pattern matches all packages within the work (formerly main) modules, whether in single-module or workspace mode.
    • The go command no longer automatically adds a toolchain line specifying the command’s current version when updating go.mod or go.work files.
  • Vet Analyzers: The go vet command includes two new analyzers:
    • waitgroup: Reports misplaced calls to sync.WaitGroup.Add, such as calling it from inside a goroutine.
    • hostport: Detects uses of fmt.Sprintf("%s:%d", host, port) for net.Dial that may not work with IPv6, suggesting net.JoinHostPort as an alternative. These static checks help prevent common concurrency and networking errors.

Runtime

Go 1.25 features critical runtime improvements, including a container-aware GOMAXPROCS and an experimental new garbage collector, among others.

  • Container-aware GOMAXPROCS:

    • The default behavior of GOMAXPROCS has changed. Previously, it defaulted to runtime.NumCPU, the number of logical CPUs available.

    • On Linux, the runtime now considers the CPU bandwidth limit of the cgroup containing the process. If this limit (often corresponding to Kubernetes’ “CPU limit” option) is lower than the number of logical CPUs, GOMAXPROCS defaults to the lower limit. This makes Go applications more intelligent and efficient in containerized environments like Kubernetes, preventing resource waste or performance bottlenecks.

    • For example, running a Go 1.25 program in a Docker container with CPU limits now respects those limits:

      1
      2
      3
      4
      docker run --cpus=4 golang:1.25-alpine go run /app/nproc.go
      # Output:
      # NumCPU: 8
      # GOMAXPROCS: 4

      In contrast to Go 1.24 where GOMAXPROCS would still be 8.

    • The runtime periodically updates GOMAXPROCS if the number of logical CPUs or the cgroup CPU bandwidth limit changes.

    • These behaviors are automatically disabled if GOMAXPROCS is manually set via the environment variable or a call to runtime.GOMAXPROCS. They can also be explicitly disabled with GODEBUG settings: containermaxprocs=0 and updatemaxprocs=0.

    • A new function, runtime.SetDefaultGOMAXPROCS, allows reverting to the runtime’s default value, enabling the new container-aware behavior even after a manual override.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      package main

      import (
      "fmt"
      "runtime"
      )

      func main() {
      // GOMAXPROCS=2 go run nproc.go (using environment variable)
      fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))

      // Using the manual setting
      runtime.GOMAXPROCS(4)
      fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))

      // Back to the default value
      runtime.SetDefaultGOMAXPROCS()
      fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
      }

      Running with GOMAXPROCS=2 as an env var:

      1
      2
      3
      GOMAXPROCS: 2
      GOMAXPROCS: 4
      GOMAXPROCS: 8 // Assuming 8 logical CPUs on the host

      This behavior only takes effect if the program uses Go version 1.25 or higher in its go.mod file.

  • New Experimental Garbage Collector (Green Tea GC):

    • Go 1.25 introduces an experimental garbage collector, codenamed “Green Tea GC,” which can be enabled by setting GOEXPERIMENT=greenteagc at build time.
    • Designed to improve the performance of marking and scanning small objects through better locality and CPU scalability.
    • The Go team anticipates a 10–40% reduction in garbage collection overhead in real-world programs that heavily use the GC. This is a significant performance benefit, especially for memory-intensive applications. For example, Josh Baker reported a 35% GC overhead reduction for his project Tile38 after enabling Green Tea GC.
    • The design is expected to continue evolving, and user feedback is encouraged.
  • Trace Flight Recorder:

    • A new runtime/trace.FlightRecorder API provides a lightweight way to capture runtime execution traces.
    • It continuously records trace data into an in-memory ring buffer, tracking a sliding window of recent execution.
    • When a significant event occurs, programs can call FlightRecorder.WriteTo to snapshot the last few seconds of the trace to a file. This approach produces much smaller traces, making it practical for debugging rare or intermittent issues in production environments.
    • The length of time and amount of data captured by a FlightRecorder can be configured.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    package main

    import (
    "fmt"
    "math/rand"
    "os"
    "runtime/trace"
    "time"
    )

    func main() {
    // Configure the flight recorder to keep at least 5 seconds of trace data,
    // with a maximum buffer size of 3MB. Both of these are hints, not strict limits.
    cfg := trace.FlightRecorderConfig{
    MinAge: 5 * time.Second,
    MaxBytes: 3 << 20, // 3MB
    }

    // Create and start the flight recorder.
    rec := trace.NewFlightRecorder(cfg)
    rec.Start()
    defer rec.Stop()

    // Simulate some workload.
    done := make(chan struct{})
    go func() {
    defer close(done)
    const n = 1 << 20
    var s []int
    for range n {
    s = append(s, rand.IntN(n))
    }
    fmt.Printf("done filling slice of %d elements\n", len(s))
    }()
    <-done

    // Save the trace snapshot to a file.
    file, _ := os.Create("/tmp/trace.out")
    defer file.Close()
    n, _ := rec.WriteTo(file)
    fmt.Printf("wrote %dB to trace file\n", n)

    fmt.Println("Use 'go tool trace /tmp/trace.out' to view the trace.")
    }
  • Unhandled Panic Output Change: The message for unhandled panics that were recovered and repanicked no longer repeats the panic value text. Instead, it now prints [recovered, repanicked].

  • VMA Names on Linux: On Linux systems with kernel support, the Go runtime now annotates anonymous memory mappings with their purpose (e.g., [anon: Go: heap]). This can be disabled with GODEBUG=decoratemappings=0.

  • Concurrent Cleanups: Functions scheduled by runtime.AddCleanup are now executed concurrently and in parallel, making cleanups more viable for heavy use cases.

  • Finalizer/Cleanup Diagnostics: A new GODEBUG=checkfinalizers=1 setting assists in identifying common issues with finalizers and cleanups by running diagnostics and reporting queue lengths.

  • Mutex Profile Improvement: The mutex profile for contention on runtime-internal locks now correctly points to the end of the critical section that caused the delay, matching the behavior for sync.Mutex values.

Compiler

The compiler in Go 1.25 includes important fixes and performance enhancements.

  • Nil Pointer Bug Fix: This release fixes a compiler bug, introduced in Go 1.21, that could incorrectly delay nil pointer checks. Programs that previously executed successfully (but incorrectly) will now correctly panic with a nil-pointer exception.
    • For instance, code using the result of os.Open before checking the error will now behave as expected:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      package main

      import "os"

      func main() {
      f, err := os.Open("nonExistentFile")
      name := f.Name() // This line will now correctly panic if f is nil
      if err != nil {
      return
      }
      println(name)
      }

      The solution is to perform the non-nil error check immediately after the error-generating statement.

  • DWARF5 Support: The compiler and linker now generate debug information using DWARF version 5. This reduces the space required for debugging information in Go binaries and decreases linking time, especially for large projects. DWARF 5 generation can be disabled using GOEXPERIMENT=nodwarf5.
  • Faster Slices: The compiler can now allocate the backing store for slices on the stack in more situations, leading to improved performance. This change might amplify issues with incorrect unsafe.Pointer usage; the bisect tool with -compile=variablemake can help identify problems. All new stack allocations can also be turned off with -gcflags=all=-d=variablemakehash=n.

Linker

The linker in Go 1.25 gains a new command-line option:

  • The linker now accepts a -funcalign=N command-line option, which specifies the alignment of function entries. The default value remains platform-dependent and unchanged in this release.

Standard Library

Go 1.25 brings a comprehensive list of standard library improvements, including new packages, experimental features, and minor enhancements across various modules.

  • New testing/synctest Package:

    • The testing/synctest package, introduced experimentally in Go 1.24, has graduated to general availability in Go 1.25.

    • It provides robust support for testing concurrent code. The Test function runs a test function in an isolated “bubble” where time is virtualized, allowing time package functions to operate on a fake clock. The clock advances instantaneously if all goroutines in the bubble are blocked. This enables testing timeout scenarios without actual delays.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      package main

      import (
      "errors"
      "fmt"
      "testing"
      "time"

      "golang.org/x/sync/synctest" // Renamed from "sync/synctest" in source for illustration
      )

      // Read reads a value from the input channel and returns it.
      // Timeouts after 60 seconds.
      func Read(in chan int) (int, error) {
      select {
      case v := <-in:
      return v, nil
      case <-time.After(60 * time.Second):
      return 0, errors.New("timeout")
      }
      }

      func TestReadTimeout(t *testing.T) {
      synctest.Test(t, func(t *testing.T) {
      ch := make(chan int)
      _, err := Read(ch)
      if err == nil {
      t.Fatal("expected timeout error, got nil")
      }
      fmt.Println("TestReadTimeout PASS") // Illustrative print
      })
      }
      // This test would pass instantly without waiting 60 seconds
    • The Wait function waits for all goroutines in the current bubble to block before resuming execution.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      package main

      import (
      "fmt"
      "testing"
      "time"

      "golang.org/x/sync/synctest"
      )

      func TestWait(t *testing.T) {
      synctest.Test(t, func(t *testing.T) {
      var innerStarted bool
      done := make(chan struct{})
      go func() {
      innerStarted = true
      time.Sleep(time.Second)
      close(done)
      }()
      // Wait for the inner goroutine to block on time.Sleep.
      synctest.Wait()
      // innerStarted is guaranteed to be true here.
      fmt.Printf("inner started: %v\n", innerStarted)
      <-done
      })
      }
      // Output: inner started: true
    • The Run function from the Go 1.24 experiment is now deprecated in favor of Test.

  • New Experimental encoding/json/v2 Package:

    • Go 1.25 includes a new, experimental JSON implementation, accessible by setting GOEXPERIMENT=jsonv2 at build time.

    • This is a major revision of the encoding/json package, accompanied by the encoding/json/jsontext package for lower-level JSON syntax processing.

    • The new implementation offers substantially faster decoding performance compared to the existing one, with encoding performance remaining at parity in many scenarios. This addresses long-standing performance concerns with the encoding/json package.

    • It allows developers to implement custom marshalers and unmarshalers for arbitrary types using MarshalToFunc and UnmarshalFromFunc, providing greater control over JSON serialization.

    • For example, marshaling boolean values (true/false) and “boolean-like” strings (on/off) to custom symbols:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      package main

      import (
      "fmt"
      "encoding/json"
      "github.com/go-json-experiment/jsonv2" // Updated import for v2
      "github.com/go-json-experiment/jsonv2/jsontext"
      )

      func main() {
      // Marshals boolean values to ✓ or ✗.
      boolMarshaler := jsonv2.MarshalToFunc(
      func(enc *jsontext.Encoder, val bool) error {
      if val {
      return enc.WriteToken(jsontext.String("✓"))
      }
      return enc.WriteToken(jsontext.String("✗"))
      },
      )

      // Marshals boolean-like strings to ✓ or ✗.
      strMarshaler := jsonv2.MarshalToFunc(
      func(enc *jsontext.Encoder, val string) error {
      if val == "on" || val == "true" {
      return enc.WriteToken(jsontext.String("✓"))
      }
      if val == "off" || val == "false" {
      return enc.WriteToken(jsontext.String("✗"))
      }
      // SkipFunc tells Go to skip the current marshaler and move to the next.
      return jsonv2.SkipFunc
      },
      )

      // Combine custom marshalers with JoinMarshalers.
      marshalers := jsonv2.JoinMarshalers(boolMarshaler, strMarshaler)

      // Marshal some values.
      vals := []any{true, "off", "hello"}
      data, err := jsonv2.Marshal(vals, jsonv2.WithMarshalers(marshalers))
      fmt.Println(string(data), err)
      }
      // Output: ["✓","✗","hello"] <nil>
    • The design of encoding/json/v2 is expected to continue evolving, and user feedback is highly encouraged for compatibility issues.

  • sync.WaitGroup.Go:

    • A new WaitGroup.Go method makes the common pattern of creating and counting goroutines more convenient.
    • It automatically increments the wait group counter, runs a function in a goroutine, and decrements the counter when the function is done, eliminating the need for boilerplate wg.Add(1) and defer wg.Done() calls.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package main

    import (
    "fmt"
    "sync"
    )

    func main() {
    var wg sync.WaitGroup
    wg.Go(func() {
    fmt.Println("go is awesome")
    })
    wg.Go(func() {
    fmt.Println("cats are cute")
    })
    wg.Wait()
    fmt.Println("done")
    }
    /*
    Output (order may vary for "go is awesome" and "cats are cute"):
    cats are cute
    go is awesome
    done
    */
  • os.Root Enhancements:

    • The os.Root type, used to limit filesystem operations to a specific directory, now supports several new methods mirroring existing os package functions. This makes working with files much safer.

    • New methods include Chmod, Chown, Chtimes, Lchown, Link, MkdirAll, ReadFile, Readlink, RemoveAll, Rename, Symlink, and WriteFile.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      package main

      import (
      "fmt"
      "os"
      "syscall"
      "time"
      )

      func main() {
      // This example assumes a "data" directory exists and has a "01.txt" file.
      // In a real scenario, you'd handle directory/file creation and errors.
      // For demonstration, these files might need to be created manually or programmatically.

      // Example: Chmod
      root, _ := os.OpenRoot("data")
      defer root.Close() // Ensure the root is closed

      // Create a dummy file for Chmod example if it doesn't exist
      root.WriteFile("01.txt", []byte("hello"), 0644)

      root.Chmod("01.txt", 0600)
      finfo, _ := root.Stat("01.txt")
      fmt.Println("Chmod:", finfo.Mode().Perm()) // Output: -rw-------

      // Example: Chown (requires appropriate permissions and user IDs)
      // root.Chown("01.txt", 1000, 1000)
      // finfo, _ = root.Stat("01.txt")
      // stat := finfo.Sys().(*syscall.Stat_t)
      // fmt.Printf("Chown: uid=%d, gid=%d\n", stat.Uid, stat.Gid)

      // Example: MkdirAll
      const dname = "path/to/secret"
      root.MkdirAll(dname, 0750)
      finfo, _ = root.Stat(dname)
      fmt.Printf("MkdirAll: %s %s\n", dname, finfo.Mode()) // Output: path/to/secret drwxr-x---

      // Example: Symlink & Readlink
      const lname = "symlink.txt"
      root.Symlink("01.txt", lname)
      lpath, _ := root.Readlink(lname)
      fmt.Printf("Symlink/Readlink: %s -> %s\n", lname, lpath) // Output: symlink.txt -> 01.txt

      // Example: WriteFile & ReadFile
      const fname = "go.txt"
      root.WriteFile(fname, []byte("go is awesome"), 0644)
      content, _ := root.ReadFile(fname)
      fmt.Printf("WriteFile/ReadFile: %s: %s\n", fname, content) // Output: go.txt: go is awesome

      // Example: Rename
      const oldname = "01.txt"
      const newname = "go_old.txt"
      root.Rename(oldname, newname)
      _, err := root.Stat(oldname)
      fmt.Printf("Rename (old name error): %v\n", err) // Output: stat data/01.txt: no such file or directory
      finfo, _ = root.Stat(newname)
      fmt.Printf("Rename (new name): %s\n", finfo.Name()) // Output: go_old.txt
      }
    • Filesystems returned by os.DirFS and os.Root.FS now implement the new io/fs.ReadLinkFS interface, which provides ReadLink and Lstat methods for symbolic links.

  • reflect.TypeAssert:

    • A new reflect.TypeAssert function allows converting a reflect.Value directly to a Go value of a given type.

    • This is similar to using a type assertion on the result of Value.Interface but avoids unnecessary memory allocations by not boxing the value in an interface.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      package main

      import (
      "fmt"
      "reflect"
      )

      type Person struct {
      Name string
      Age int
      }

      func main() {
      alice := &Person{"Alice", 25}
      // Given a reflection Value...
      aliceVal := reflect.ValueOf(alice).Elem()
      // ...convert it back to the Person type using TypeAssert.
      person, _ := reflect.TypeAssert[Person](aliceVal)
      fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
      }
      // Output: Name: Alice, Age: 25
  • testing Package Improvements:

    • The new T.Attr, B.Attr, and F.Attr methods allow emitting arbitrary key-value attributes to the test log. This is useful for associating metadata like issue links or descriptions with test results, especially when using the -json flag for automated processing.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      package main

      import "testing"

      func TestAttrs(t *testing.T) {
      t.Attr("issue", "demo-1234")
      t.Attr("description", "Testing for the impossible")
      if 21*2 != 42 {
      t.Fatal("What in the world happened to math?")
      }
      }
      /*
      Output (partially):
      === RUN TestAttrs
      === ATTR TestAttrs issue demo-1234
      === ATTR TestAttrs description Testing for the impossible
      --- PASS: TestAttrs (0.00s)
      */
    • The new Output method of T, B, and F provides an io.Writer that writes to the same test output stream as TB.Log but without file and line numbers. This is handy for directing application logs into the test output.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      package main

      import (
      "log/slog"
      "testing"
      )

      func TestLog(t *testing.T) {
      t.Log("test message 1")
      t.Log("test message 2")
      appLog := slog.New(slog.NewTextHandler(t.Output(), nil))
      appLog.Info("app message")
      }
      /*
      Output (partially):
      === RUN TestLog
      main_test.go:12: test message 1
      main_test.go:13: test message 2
      time=... level=INFO msg="app message"
      --- PASS: TestLog (0.00s)
      */
    • The AllocsPerRun function now panics if parallel tests are running. This change helps catch bugs where the results of AllocsPerRun would be flaky due to concurrent test execution.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      package main

      import "testing"

      func TestAllocs(t *testing.T) {
      t.Parallel() // Enables parallel execution
      allocs := testing.AllocsPerRun(100, func() {
      var s []int
      // Do some allocations.
      for i := range 1024 {
      s = append(s, i)
      }
      })
      t.Log("Allocations per run:", allocs) // This will now panic in Go 1.25
      }
      /*
      Output (Go 1.25):
      === RUN TestAllocs
      === PAUSE TestAllocs
      === CONT TestAllocs
      --- FAIL: TestAllocs (0.00s)
      panic: testing: AllocsPerRun called during parallel test [recovered, repanicked]
      */
  • log/slog.GroupAttrs: The new slog.GroupAttrs function creates a group Attr from a slice of Attr values, resolving an issue where slog.Group did not accept []slog.Attr directly.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package main

    import (
    "log/slog"
    "os"
    )

    func main() {
    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    // Before GroupAttrs, this would cause a compilation error:
    // attrs := []slog.Attr{
    // slog.Int("value", 1000),
    // slog.String("currency", "USD"),
    // }
    // logger.Info("deposit", slog.Bool("ok", true), slog.Group("amount", attrs...),)

    // With slog.GroupAttrs
    attrs := []slog.Attr{
    slog.Int("value", 1000),
    slog.String("currency", "USD"),
    }
    logger.Info("deposit", slog.Bool("ok", true), slog.GroupAttrs("amount", attrs...),)
    }
    // Output: time=... level=INFO msg=deposit ok=true amount.value=1000 amount.currency=USD
  • hash.Cloner Interface: A new hash.Cloner interface allows hash functions to return a copy of their current state. All standard library hash.Hash implementations now implement Cloner, including MD5, SHA-1, SHA-3, FNV-1, CRC-64, and others.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import (
    "fmt"
    "reflect"

    "golang.org/x/crypto/sha3" // Example with sha3
    )

    func main() {
    h1 := sha3.New256()
    h1.Write([]byte("hello"))

    clone, _ := h1.Clone() // Clone the current state of h1
    h2 := clone.(*sha3.SHA3)

    // h2 has the same state as h1. Further writes to each will result in the same hash.
    h1.Write([]byte("world"))
    h2.Write([]byte("world"))

    fmt.Printf("h1: %x\n", h1.Sum(nil))
    fmt.Printf("h2: %x\n", h2.Sum(nil))
    fmt.Printf("h1 == h2: %t\n", reflect.DeepEqual(h1.Sum(nil), h2.Sum(nil)))
    }
    // Output:
    // h1: ... (same hash)
    // h2: ... (same hash)
    // h1 == h2: true
  • Other Notable Changes:

    • archive/tar: Writer.AddFS now supports symbolic links for filesystems implementing io/fs.ReadLinkFS.

    • encoding/asn1: Unmarshal and UnmarshalWithParams parse T61String and BMPString more consistently, potentially rejecting previously accepted malformed encodings.

    • crypto packages: Introduce MessageSigner interface and SignMessage function. Significant performance boosts for signing in crypto/ecdsa and crypto/ed25519 (4x faster in FIPS 140-3 mode) and crypto/rsa key generation (3x faster). SHA-1 hashing is 2x faster on amd64 with SHA-NI instructions. SHA3 hashing is 2x faster on Apple M processors.

    • crypto/tls: Stricter adherence to specifications, disallowing SHA-1 signature algorithms in TLS 1.2 by default (re-enableable via GODEBUG=tlssha1=1). TLS servers now prefer the highest supported protocol version.

    • net: LookupMX and Resolver.LookupMX now return DNS names that resemble valid IP addresses, addressing a practical discrepancy where name servers sometimes return IP addresses despite RFC requirements. On Windows, ListenMulticastUDP supports IPv6 addresses, and conversion between os.File and network connections is now possible.

    • net/http: A new CrossOriginProtection type implements protections against Cross-Site Request Forgery (CSRF) using modern browser Fetch metadata, without requiring tokens or cookies.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      package main

      import (
      "fmt"
      "io"
      "log"
      "net/http"
      )

      func main() {
      mux := http.NewServeMux()
      mux.HandleFunc("GET /get", func(w http.ResponseWriter, req *http.Request) {
      io.WriteString(w, "ok\n")
      })
      mux.HandleFunc("POST /post", func(w http.ResponseWriter, req *http.Request) {
      io.WriteString(w, "ok\n")
      })

      antiCSRF := http.NewCrossOriginProtection()
      antiCSRF.AddTrustedOrigin("https://example.com")
      antiCSRF.AddTrustedOrigin("https://*.example.com")

      srv := http.Server{
      Addr: ":8080",
      Handler: antiCSRF.Handler(mux),
      }
      log.Fatal(srv.ListenAndServe())

      // To test, run this Go program and then use curl in another terminal:
      // curl --data "ok" -H "sec-fetch-site:same-origin" localhost:8080/post
      // Output: ok
      // curl --data "ok" -H "sec-fetch-site:cross-site" localhost:8080/post
      // Output: cross-origin request detected from Sec-Fetch-Site header
      // curl --data "ok" -H "origin:https://evil.com" -H "host:antonz.org" localhost:8080/post
      // Output: cross-origin request detected, and/or browser is out of date: Sec-Fetch-Site is missing, and Origin does not match Host
      // curl --data "ok" -H "origin:https://example.com" -H "host:antonz.org" localhost:8080/post
      // Output: ok
      // curl -H "origin:https://evil.com" localhost:8080/get
      // Output: ok
      }
    • unique: The unique package now reclaims interned values more eagerly, efficiently, and in parallel, reducing memory consumption for applications that intern many unique values.

Ports

Go 1.25 includes updates and changes to supported platforms:

  • Darwin: Go 1.25 requires macOS 12 Monterey or later. Support for previous versions has been discontinued, as announced in the Go 1.24 release notes.
  • Windows: Go 1.25 is the last release that includes the 32-bit windows/arm port (GOOS=windows GOARCH=arm). It is scheduled for removal in Go 1.26.
  • Loong64: The linux/loong64 port now supports the race detector, gathers traceback information from C code using runtime.SetCgoTraceback, and can link cgo programs with the internal link mode.
  • RISC-V: The linux/riscv64 port now supports the plugin build mode. Additionally, the GORISCV64 environment variable accepts a new value, rva23u64, which selects the RVA23U64 user-mode application profile.

Community Reception

The Go 1.25 release has been met with a generally positive reception from the community, highlighting key features and broader aspects of the language.

  • The new encoding/json/v2 package is a standout feature, drawing significant attention and excitement. Developers anticipate performance improvements and the ability to implement custom marshalers for external types, addressing a long-standing pain point in Go’s JSON handling. The experimental nature and expected evolution of the API are noted, with calls for users to test and provide feedback.
  • Go’s stability and maintainability continue to be highly valued. Many developers praise the language for its consistent backward compatibility, allowing non-trivial source code to build and run even after a decade without issues. This “killer feature” is particularly appreciated by sysadmin/ops focused professionals for its static binaries and predictable resource profiles.
  • Discussion around Go’s documentation and ecosystem reveals varied experiences. While some find third-party module documentation lacking or outdated, others appreciate the consistent documentation on pkg.go.dev and find Go code itself often more readable than external documentation due to its emphasis on simplicity and discouragement of deep abstractions.
  • The container-aware GOMAXPROCS change is seen as one of the most impactful improvements, particularly for applications deployed in Kubernetes and Docker environments, leading to more intelligent and efficient CPU resource usage.
  • The sync.WaitGroup.Go method is widely welcomed for simplifying concurrent programming boilerplate, improving code readability and reducing common errors.
  • The experimental Green Tea GC is also eagerly anticipated, with expectations of significant overhead reduction in garbage collection for real-world programs.
  • Minor changes, such as stricter TLS handling and the net.LookupMX update (which prioritizes “reality compliance” over strict RFC adherence to handle real-world DNS server behaviors), are generally well-received.
  • The overall sentiment is that Go 1.25 is a “great release”, emphasizing pragmatism over “flashy syntax sugar,” focusing on fundamental improvements that provide “invisible benefits” to developers, allowing them to concentrate on business logic while Go handles complex system optimization.

Links to quoted articles and resources:

More

Recent Articles:

Random Article:


More Series Articles about You Should Know In Golang:

https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a

And I’m Wesley, delighted to share knowledge from the world of programming. 

Don’t forget to follow me for more informative content, or feel free to share this with others who may also find it beneficial. It would be a great help to me.

Give me some free applauds, highlights, or replies, and I’ll pay attention to those reactions, which will determine whether I continue to post this type of article.

See you in the next article. 👋

中文文章: https://programmerscareer.com/zh-cn/go-1-25/
Author: Medium,LinkedIn,Twitter
Note: Originally written at https://programmerscareer.com/go-1-25/ at 2025-08-17 10:33.
Copyright: BY-NC-ND 3.0

Go AI SDKs: Powering the Next Generation of AI Applications and Agents in Go Vibe Specs: Spec-First AI Development

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×