Contents

Five Upcoming Features In Go 1.26

Go 1.26 continues the project’s focus on performance, memory efficiency, and modernising the standard library. While not as disruptive as generics (1.18) or modules (1.11), this release introduces several quality-of-life improvements that developers will start using immediately.

The upcoming release of Go 1.26 (February 2026) includes meaningful improvements across the language, runtime, and tooling.

Let’s take a look at five of the most impactful changes, based on the official draft release notes.

1. The new built-in now accepts expressions

Go 1.26 extends the new built-in so that it can take an expression, not just a type. This means new(expr) allocates storage for the expression’s value and returns a pointer to it.

Practical applications

This small but elegant enhancement tends to shine in real-world scenarios such as configuration parsing, database models, and API request structs. Any time optional fields are represented as pointers, new(expr) eliminates the need for an intermediate variable. This leads to cleaner struct initialisers and helps avoid shadowing and repetition when constructing objects with many pointer fields.

Subtle benefits

Another practical benefit is that the expression is evaluated exactly once, making it safer and more explicit than embedding expressions inside helper functions. It also aligns with Go’s preference for lightweight, declarative initialisation rather than verbose constructor functions for simple cases.

package main

import "fmt"

type User struct {
	Age *int
}

func main() {
	// Go 1.25
	age := 18
	user1 := User{
		Age: &age,
	}

	// Go 1.26
	user2 := User{
		Age: new(21),
	}

	fmt.Printf("user 1 is %d years old\n", *user1.Age)
	fmt.Printf("user 2 is %d years old\n", *user2.Age)
}

2. Green Tea becomes the default garbage collector

The experimental Green Tea garbage collector introduced in Go 1.25 is now enabled by default, improving performance and memory locality. If your application allocates frequently or manipulates many short-lived objects, expect improved responsiveness and throughput.

As per the draft release notes:

  • Expect to see a 10–40% reduction in GC overhead, depending on workload
  • CPU-rich hardware benefits even more, thanks to vectorised scanning

How Green Tea works (high-level)

Green Tea extends Go’s non-moving collector with locality-aware strategies that keep related allocations closer together in memory. This reduces cache misses and improves throughput in allocation-heavy workloads. It also introduces vectorised scanning paths on modern CPUs, which is where the biggest reductions in GC time occur.

Further details can be found in the official go.dev blog post.

You can temporarily opt out by using the following flag. However, this flag is expected to be removed in Go 1.27.

GOEXPERIMENT=nogreenteagc

3. MultiHandler for fan-out logging

A welcome addition in Go 1.26 is the new slog.NewMultiHandler, allowing a single logger to dispatch log records to multiple handlers at once. Previously, achieving similar functionality required either a custom slog.Handler, or a third-party package, e.g. slog-multi.

With the introduction of slog.MultiHandler to the standard library, it is now even easier to multiplex logs to JSON, text, files, buffers, or even external systems.

Why this matters for modern deployments

Logging pipelines have grown increasingly complex. Applications often need to emit structured logs for machines, readable logs for humans, and audit logs for compliance, all at once. slog.MultiHandler turns this into a first-class, boilerplate-free operation in the standard library, helping to unify the logging ecosystem.

Advanced usage patterns

This feature makes it straightforward to compose layered logging setups: sampling high-volume logs, filtering by severity per output, forwarding security-sensitive events to separate handlers, or storing logs in memory during tests. Because each handler processes records independently, you can customise formatting, output destinations, and severity levels without interfering with other channels.

package main

import (
	"log/slog"
	"os"
)

func main() {
	// This example emits:
	// JSON-formatted log lines to stdout.
	// Human-readable text log lines to stderr.
	jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
	textHandler := slog.NewTextHandler(os.Stderr, nil)

	logger := slog.New(
		slog.NewMultiHandler(jsonHandler, textHandler),
	)

	logger.Info("starting service", "version", "1.26")
}

4. Context-aware, network-specific dialling

One of the more subtle but powerful improvements in Go 1.26 is the addition of network-specific, context-aware dialling methods on net.Dialer:

  • DialTCP
  • DialUDP
  • DialIP
  • DialUnix

Why this API resolves a long-standing trade-off

Previously, developers had to choose between generic context-aware dialling (DialContext) and efficient protocol-specific dialling (DialTCP, etc.), which lacked context support. Go 1.26 finally unifies these two worlds, enabling both efficient, type-safe dialling and context cancellation using pre-resolved addresses such as netip.AddrPort.

Impact on performance and clarity

Avoiding DNS resolution and string parsing results in more predictable performance, especially in latency-sensitive systems that open many short-lived connections. Using explicit DialTCP and DialUDP calls also documents the programmer’s intent clearly, reducing ambiguity and the chance of runtime errors.

Overall, these new dialling methods give developers the best of both worlds: network-specific performance and context-based control.

package main

import (
	"context"
	"net"
	"net/netip"
	"time"
)

func main() {
	var dialer net.Dialer
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	raddr := netip.MustParseAddrPort("93.184.216.34:443")
	conn, err := dialer.DialTCP(ctx, "tcp", netip.AddrPort{}, raddr)
	if err != nil {
		panic(err)
	}

	defer conn.Close()
}

5. Major upgrade to go fix

Go 1.26 modernises the go fix command, bringing it in line with the evolution that go vet underwent back in Go 1.10.

Historically, go fix shipped with a collection of legacy fixers targeting old language transitions (e.g. Go 1 -> Go 1.1), but these had long since become obsolete.

A real upgrade, not a historical clean-up

By adopting the Go analysis framework, go fix becomes a proper refactoring assistant rather than a vestige of early Go migrations. This gives it a long-term future: as the language evolves, so can its automated fixers, without being constrained by outdated transition logic.

Practical migration value

For large or ageing codebases, the new fixers provide substantial value by modernising patterns that no longer reflect best practices, updating deprecated crypto usage, older slog constructs, or identifying opportunities to adopt newer standard library APIs. Running go fix can save hours of manual clean-up during an upgrade cycle.

Integration with CI and editors

Because the tool now shares a foundation with go vet, its behaviour becomes far more consistent across IDEs, CI pipelines, and static analysis tools. For teams already relying on vet, adding fix becomes a natural step in automated maintenance.

Simply run:

go fix ./...

This now applies smart, analysis-driven rewrites, not just mechanical global substitutions. Think of Go 1.26s go fix as a built-in refactoring tool.