---
id: 1a31e06c0d969b4b6cbbc590259cf089
author: iwanhae
title: Go 1.27, avagy hogyan tanultam meg nem aggódni és szeretni a generikus metódusokat
description: Generikus metódusok, az encoding/json/v2, egy szabványos uuid csomag és egy valóban működő goroutine-szivárgásdetektor. Körutazás a Go 1.27 újdonságain, minden példát ténylegesen lefuttatva.
language: hu
date: 2026-08-15T02:08:43.129534158Z
path: /blog/posts/go-1-27-or-how-i-learned-to-stop-worrying-and-love-generic-methods-za800e454
category: release
---

![Overview of Go 1.27](/assets/images/whats-new-in-go1.27/overview.webp)

A Go 1.27 2026 augusztusában jelenik meg, és ezúttal a nyelv maga is megváltozott. Nem az a fajta változás, hogy "új függvényt adtunk a `slices` csomaghoz", hanem valódi változás. Megérkeztek a generikus metódusok. Lezárult egy tizenegy éves issue. Az `encoding/json` csomagot pedig egy új motor váltotta fel, miközben a gép a levegőben volt.

Az alábbi példák mindegyikét `go1.27rc3` (darwin/arm64) alatt futtattam. Minden kimeneti blokk valódi kimenet, átmásolva, a hibaüzenetekkel együtt. Ha valami furcsának tűnik, az azért van, mert a fordító valóban ezt írta.

```bash
go install golang.org/dl/go1.27rc3@latest
go1.27rc3 download
```

## 1. A nyelv megváltozott (igen, tényleg)

### 1.1. Generikus metódusok

A Go 1.18 óta vannak generikus típusaink, és a Go 1.18 óta van "A Beszélgetés" is. Nagyjából így zajlik:

> "Csak írok egy `Map` metódust a saját slice típusomra…"
>
> `method must have no type parameters`
>
> "…rendben. Akkor marad a csomagszintű függvény."

A metódusok korábban csak a _receiver_ által deklarált típusparamétereket használhattak. Saját metódus nem vezethetett be újakat. Így minden generikus átalakítás a csomagszintre szorult, ahol további tizenhét, valamilyen `MapSlice` változatra keresztelt, lebegő segédfüggvény mellé került.

A Go 1.27 ezt javítja:

```go
type List[E any] []E

// F a metódus által deklarált típusparaméter
func (l List[E]) Apply[F any](f func(E) F) List[F] {
	r := make(List[F], len(l))
	for i, x := range l {
		r[i] = f(x)
	}
	return r
}

func main() {
	l := List[int]{1, 2, 3}
	fmt.Println(l.Apply(func(i int) string { return fmt.Sprint(i * 10) }))
	// [10 20 30]
}
```

A receivernek sem kell generikusnak lennie. Egy egyszerű struct is kaphat generikus metódust:

```go
type Bag struct{ items []any }

func (b *Bag) Add[T any](v T) { b.items = append(b.items, v) }
```

Mielőtt azonban az egész kódbázist még ma délután átírnád, van **két korlátozás**, és ezek fontosabbak, mint amilyennek látszanak:

1. Interfész metódusok nem deklarálhatnak típusparamétereket.
2. Generikus metódusok nem valósíthatnak meg interfész metódusokat.

Ez utóbbi az, amelyik meg fog harapni:

```go
type Adder interface{ Add(int) int }

type G struct{}

func (G) Add[T any](v T) T { return v }

var _ Adder = G{}
```

```
cannot use G{} (value of struct type G) as Adder value in variable declaration:
	G does not implement Adder (wrong type for method Add)
		have Add[T any](T) T
		want Add(int) int
```

Tehát a generikus metódusok és a dinamikus diszpécselés nem férnek össze. Ez nem a Go csapat fukarságából fakad, hanem abból, hogy egy generikus metódusnak végtelen sok példányosítása lehet, az interfész metódustáblának viszont végesnek és fordítási időben ismertnek kell lennie. Egy végtelen dolgot nem lehet véges táblába tenni. A világegyetem nemet mondott.

Gyakorlati tanulság: a generikus metódusok konkrét típusokhoz, segédfüggvény jellegű API-khoz valók. Ami interfész mögé kell rejtőzzön, az továbbra is a régi megközelítést igényli.

A standard könyvtár már ki is használta ezt. A `math/rand/v2` `Rand` típusa kapott egy generikus metódust:

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

```go
r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.N(int32(100)))     // 76
fmt.Println(r.N(time.Second))    // 616.436223ms
```

Korábban csak a csomagszintű `rand.N` volt generikus, ami azt jelentette, hogy a globális forrást lehetett használni. Most a saját, magvetett `*Rand` példányod is ugyanazt a kényelmet kapja. Kicsi dolog. Szép dolog.

### 1.2. Struct literál mezőválasztók, avagy: a #9859-es issue végre lezárul

A beágyazás lehetővé teszi a `u.ID` használatát. A beágyazás _nem_ teszi lehetővé a `User{ID: 1}` használatát. Helyette a `User{Base: Base{ID: 1}}` írandó, ami Go módja annak, hogy megkérdezze, tényleg ezt akartad-e.

A [9859-es issue](https://go.dev/issue/9859) 2015-ben született. Most lezárták. Valahol egy gopher, aki azóta kétszer váltott szakmát, most kap egy GitHub-értesítést.

```go
type Object struct{ name, color string }
type Point3D struct {
	Object
	x, y, z float64
}
type Line struct {
	Object
	p, q Point3D
}

// Go 1.27: a felemelt mezőt közvetlenül használjuk
line := Line{name: "diagonal", q: Point3D{y: -4, z: 12.3}}
```

A `name` nem a `Line` mezője. A beágyazott `Object` mezője. Korábban ezt kellett írnod: `Line{Object: Object{name: "diagonal"}, ...}`.

Most jöjjenek az apró betűs részek, amelyeket úgy ellenőriztem, hogy tényleg megpróbáltam mindezt lefordítani.

**(a) A kulcs továbbra is egyszerű azonosító.** Tetszőleges szelektorutat nem írhatsz.

```go
_ = Line{Object.name: "x"}   // invalid field name Object.name in struct literal
_ = Line{p.x: 1}             // invalid field name p.x in struct literal
```

Tehát a funkció nem azt jelenti, hogy "a kulcsok mostantól úgy működnek, mint a mezőelérés". Pontosan azt jelenti, hogy a implicit módon felemelt mezőnevek megengedettek. Ez valamivel szűkebb, mint amit a címsor sugall.

**(b) Nem adhatsz meg egyszerre beágyazott mezőt és egy abból felemelt mezőt.**

```go
obj := Object{"edge", "black"}
_ = Line{Object: obj, name: "diagonal"}
// cannot specify promoted field name and enclosing embedded field Object
```

Ésszerű. Ugyanarra a memóriára két ellentmondó dolgot mondtál, és a fordító nem vállalta a találgatást.

**(c) A mutató típusú beágyazás nincs támogatva.**

```go
type PtrEmbed struct {
	*Object
	z int
}

_ = PtrEmbed{name: "x"}
// invalid implicit pointer indirection to reach name
```

Ahhoz, hogy egy mutatót követhess, kell egy mutató, a literál létrehozásakor viszont még nincs. Ez is igazságos.

Jó hír: a `go fix` átírja helyetted a régi literáljaidat. Erről később bővebben.

### 1.3. A függvénytípus-következtetés kevésbé önkényes lett

A generikus függvények típuskövetkeztetése korábban egyes helyeken működött, másokon nem, és nem volt felismerhető elv, amely eldöntötte volna, melyik eset melyik. Változóba történő értékadásnál működött? Igen. Ugyanazt a függvényt egy struct mezőbe tenni? Fordítóhiba, kérlek, írd úgy, mint 2022-ben: `double[int]`.

A Go 1.27 **minden olyan kontextusban** működteti a következtetést, ahol a céltípus egyértelműen ismert:

```go
func double[T ~int | ~float64](v T) T { return v * 2 }

type S struct{ f func(int) int }
type A [1]func(int) int

func main() {
	s := S{f: double}          // 1.26: double[int] kellett
	a := A{double}             // 1.26: double[int] kellett

	c := make(chan func(int) int, 1)
	c <- double                // 1.26: double[int] kellett

	var fn func(float64) float64 = double  // ez mindig is működött

	fmt.Println(s.f(21), a[0](5), (<-c)(7), fn(1.5))
	// 42 10 14 3
}
```

Egyetlen `double`, amelyet három helyen `func(int) int`, a negyedik helyen pedig `func(float64) float64` típusra példányosít, kizárólag a kontextusból. Ha függvényértékekkel teli opciós struktúrákat vagy handler táblákat írsz, a kódbázisodból a sok `[T]` zaj hamarosan eltűnik.

## 2. Futtatókörnyezet: ingyenes teljesítmény és szivárgásdetektor

### 2.1. Méretspecifikus allokáció

A fordító mostantól kis objektumok esetén méretspecifikus allokációs rutinok hívását generálja. A kiadási jegyzetek legfeljebb 30%-os gyorsulást ígérnek a 80 bájt alatti allokációknál, és nagyjából 1%-ot az allokációban gazdag programok egészére nézve.

Mérés nélkül nem hiszek a kiadási jegyzeteknek, ezért:

```go
type small struct{ a, b, c, d int }

var sink any

func BenchmarkAlloc(b *testing.B) {
	for b.Loop() {
		sink = &small{}
	}
}

func BenchmarkSlice(b *testing.B) {
	for b.Loop() {
		sink = make([]byte, 48)
	}
}
```

```
# alapértelmezett (méretspecifikus malloc bekapcsolva)
BenchmarkAlloc-12     3000000     5.358 ns/op
BenchmarkSlice-12     3000000    13.58 ns/op

# GOEXPERIMENT=nosizespecializedmalloc
BenchmarkAlloc-12     3000000     8.722 ns/op
BenchmarkSlice-12     3000000    20.81 ns/op

```

Apple Silicon-on, egy olyan mikrobenchmarkban, amely kizárólag allokál, 30–35%-kal gyorsabb. Vagyis ez a legjobb eset, laboratóriumi körülmények között, olyan benchmarkkal, amelyet azért terveztek, hogy a szám jól nézzen ki. Valódi programban a GC és a tényleges munka a legtöbbjét elnyeli. Az ~1%-os állítás az őszinte.

Az ára a bináris mérete. Hello World:

```
2413202 bytes  (alapértelmezett)
2361826 bytes  (nosizespecializedmalloc)
```

Nagyjából 50 KB, fix, a programod méretétől függetlenül. A `GOEXPERIMENT=nosizespecializedmalloc` beállítással kikapcsolható, de **ez a kiskapu a Go 1.28-ban eltávolításra van ütemezve**, ezért inkább hibajelentés-megkerülőként kezeld, ne életformaként.

### 2.2. A goroutine-szivárgási profil már valóban működik

A Go 1.26-ban kísérleti volt, az 1.27-ben általánosan elérhető. A `goroutineleakprofile` GOEXPERIMENT megszűnt; egyszerűen működik.

```go
func leak() {
	ch := make(chan int) // soha senki nem küld ide. soha.
	go func() {
		<-ch
	}()
}

func main() {
	for range 3 {
		leak()
	}
	runtime.GC()
	runtime.GC()
	pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1)
}
```

```
goroutineleak profile: total 3
3 @ 0x104de3f18 0x104d7f0b0 0x104d7ec34 0x104e374f4 0x104dea024
#	0x104e374f3	main.leak.func1+0x23	/tmp/go127/leak/main.go:12
```

Három goroutine, végleg elakadva, fájllal és sorszámmal, amely pontosan megmutatja, hol hoztad létre őket.

Az ezt megalapozó trükk valóban okos: **újrahasznosítja a szemétgyűjtő elérhetőség-elemzését**. Ha a G goroutine a P primitívre vár, a P pedig egyetlen futtatható goroutine-ból sem érhető el (és abból sem, amit ezek a goroutine-ok felébreszthetnek), akkor P-hez soha többé nem nyúl senki, így G soha nem ébred fel. Ez nem heurisztika, hanem bizonyítás.

Ugyanez a tervezés adja a korlátját is: ha a csatorna vagy a mutex egy globális változón, vagy egy még futó goroutine lokális változóján keresztül elérhető, a GC továbbra is látja, így a futtatókörnyezet semmit sem tud megállapítani. Figyelj arra, hogy még a fenti játékpélda is két `runtime.GC()` hívást igényel a jelentéshez. Nem fog mindent elkapni.

Viszont elkapja a klasszikus esetet, amikor "elfelejtettem megszakítani a contextet, és a worker goroutine örökké él". Ez a legtöbb szivárgás legtöbbször.

Ha importálod a `net/http/pprof` csomagot, a profil a `/debug/pprof/goroutineleak` címen is elérhető. Kösd be staging környezetbe, hetente ellenőrizd, és csendben borzadj el.

Ezt a munkát Vlad Saioc az Ubertől járult hozzá, akinek jár egy ital, amit ő választ.

### 2.3. A visszakövetések mostantól megmondják, melyik kérés hált el

Azoknál a moduloknál, amelyek Go 1.27-et vagy újabbat deklarálnak, a visszakövetés fejléc sorai mostantól tartalmazzák a `runtime/pprof` goroutine-címkéket.

```go
func handle(ctx context.Context) {
	var wg sync.WaitGroup
	wg.Go(func() {
		buf := make([]byte, 4<<10)
		os.Stdout.Write(buf[:runtime.Stack(buf, true)])
	})
	wg.Wait()
}

func main() {
	labels := pprof.Labels("request", "GET /orders/42", "tenant", "acme")
	pprof.Do(context.Background(), labels, handle)
}
```

```
goroutine 3 [running] {request: "GET /orders/42", tenant: acme}:
main.handle.func1()
	/tmp/go127/tblabel/main.go:15 +0x40
sync.(*WaitGroup).Go.func1()
	...
goroutine 1 [sync.WaitGroup.Wait] {request: "GET /orders/42", tenant: acme}:
...
```

A lényeges részlet: **a címkéket a gyermek goroutine-ok örökölik.** A 3-as goroutine soha nem állított be címkét. A szülőjétől kapta.

Így legközelebb, amikor az éles rendszer holtpontra jut, és valaki SIGQUIT-et küld a folyamatnak, nem 4000 egyformának látszó goroutine-t fogsz kapni, hanem 4000 olyan goroutine-t, amelyeken rajta van, melyik kérést és melyik tenantot szolgálják. Három sor `pprof.Do` a kérés-belépési pontodon ezt adja cserébe.

Mivel a címkék olyasmit is tartalmazhatnak, amit nem szeretnél stderr-re kiírni, a `GODEBUG=tracebacklabels=0` kikapcsolja a funkciót, és ez a kikapcsolási lehetőség kifejezetten örökre megmarad.

### 2.4. Az asynctimerchan végleg megszűnt

A Go 1.23 szinkronizálta (nem pufferelt) az időzítő csatornákat, és az `asynctimerchan=1` beállítást kínálta a régi viselkedés visszakapcsolására. Az 1.27-ben ez a beállítás **véglegesen eltávolításra került**. A `time` csomag csatornái szinkronok, teljesen és alkudozás nélkül.

Érdekes a mellé vezetett szabály. Egy eltávolított GODEBUG, amely a `go.mod` fájlodban maradt, nem töri el automatikusan a buildet. Csak akkor, ha a **régi** értékre van állítva:

```
# a go.mod tartalmazza: godebug asynctimerchan=1
go: error loading go.mod:
go.mod:5: removed GODEBUG "asynctimerchan" set to old value "1" (https://go.dev/doc/godebug#go-127)

# a go.mod tartalmazza: godebug asynctimerchan=0
(builds fine)
```

Ez azt jelenti, hogy csak azok kapnak szidást, akik ténylegesen az eltávolított viselkedésre támaszkodtak. Mindenki más, aki a későbbi alapértelmezettre állította és elfelejtette, megtarthatja a figyelmen kívül hagyás kényelmét. Ez az API-régészet elgondolkodtató darabja.

## 3. Standard könyvtár

### 3.1. encoding/json/v2: a motort menet közben cserélték le

Ez a nagy. [Több mint két év javaslatvitája](https://go.dev/issue/71497) végre célba ért.

Most **három csomag** van, és a felosztás megértése a csata nagy része:

| Csomag                   | Feladat                                                                                      |
| ------------------------ | -------------------------------------------------------------------------------------------- |
| `encoding/json`          | Az ismert v1 API. **Viselkedése 100%-ban változatlan.** Mostantól a v2-re épül              |
| `encoding/json/v2`       | Szemantikai feldolgozás. Go értékek ↔ JSON                                                  |
| `encoding/json/jsontext` | Szintaktikai feldolgozás. A JSON tokenfolyamként                                             |

A főcím az, hogy az `encoding/json` teljesen más implementáción alapuló újjáépítést kapott, és **azonosan viselkedik**. Így:

```go
// Go 1.27 encoding/json.Unmarshal, rövidítve
func Unmarshal(data []byte, v any) error {
	return jsonv2.Unmarshal(data, v, DefaultOptionsV1())
}
```

Minden örökölt v1-es sajátosság opcióként lett kódolva, a `DefaultOptionsV1()` csomagba került, és a v1 API mindig alkalmazza ezt a csomagot. Ez azt jelenti, hogy ez ugyanúgy viselkedik az 1.26-ban és az 1.27-ben:

```go
var m map[string]int
json.Unmarshal([]byte(`{"a":1,"a":2}`), &m)  // <nil>, map[a:2]
```

Igen, a v1 továbbra is csendben elfogadja az ismétlődő kulcsokat, és az utolsót veszi figyelembe. Mindig is így csinálta. Ma is így csinálja. A kompatibilitás a rossz részekkel való kompatibilitást is jelenti.

**A v2 ezzel szemben véleményt formál:**

```go
var m map[string]int
jsonv2.Unmarshal([]byte(`{"a":1,"a":2}`), &m)
// jsontext: duplicate object member name "a"

var s string
jsonv2.Unmarshal([]byte("\"\xff\""), &s)
// jsontext: invalid UTF-8 after offset 1
```

Az ismétlődő kulcsokat és az érvénytelen UTF-8-at elutasítja. Mindkettő valóban veszélyes, nem csak rendetlen: ha két parser nem ért egyet abban, melyik ismétlődő kulcs érvényes, biztonsági hibák keletkeznek. A CouchDB CVE-2017-12635 sebezhetősége pontosan ez volt: egy JSON törzs két `roles` kulccsal, ahol a validátor az egyiket, a tárolórétegy a másikat olvasta. Az elutasítás a helyes döntés.

Az opciók variadikusak:

```go
type Config struct {
	Name    string   `json:"name"`
	Tags    []string `json:"tags,omitzero"`
	Timeout int      `json:"timeout,omitzero"`
}

out, _ := jsonv2.Marshal(Config{Name: "api"})
// {"name":"api"}

// rendezett map kulcsok + behúzás
out, _ = jsonv2.Marshal(map[string]int{"b": 2, "a": 1},
	jsonv2.Deterministic(true), jsontext.WithIndent("  "))
// {
//   "a": 1,
//   "b": 2
// }

// ismeretlen mezők elutasítása — korábban Decoder és DisallowUnknownFields() kellett hozzá
var c Config
err := jsonv2.Unmarshal([]byte(`{"name":"api","nope":1}`), &c,
	jsonv2.RejectUnknownMembers(true))
// json: cannot unmarshal JSON string into Go main.Config:
//   unknown object member name "nope"
```

A `Deterministic`, `MatchCaseInsensitiveNames`, `StringifyNumbers`, `FormatNilSliceAsNull`, `OmitZeroStructFields`: a legtöbb dolog, amelyet korábban külső könyvtárral vagy kézzel írt `MarshalJSON` metódussal oldottál meg, most már jelzők.

A migrációs történet pedig a legjobb rész. **A későbbi opciók nyernek**, így a v2 szigorúságát viselkedésenként, lépésenként vezetheted be:

```go
// a v1 szemantika megtartása, de az ismétlődő kulcsok elutasítása a v2 módjára
jsonv2.Unmarshal(data, &v,
	json.DefaultOptionsV1(),
	jsontext.AllowDuplicateNames(false))
// duplicate object member name
```

Nem kell egy 200 ezer soros kódbázist v2-re portolnod ahhoz, hogy abbahagyd az ismétlődő kulcsok elfogadását. Egy opciót kell átállítanod. Nagy kódbázisoknál ez a reális út.

Teljesítmény: a marshalizálás nagyjából azonos szintű, **az unmarshalizálás lényegesen gyorsabb.** Ha valami elromlik, a `GOEXPERIMENT=nojsonv2` visszaállítja a régi implementációt. Ez a kiskapu szintén előbb-utóbb megszűnik, ezért inkább nyiss issue-t, ahelyett, hogy beletörődnél.

A `jsontext` az alacsony szintű réteg: az `Encoder`/`Decoder` `Token`-ekként és `Value`-kként járja be a JSON-t, egy állapotgép tartja tisztességesen. Akkor nyúlj hozzá, ha streamelő transzformert vagy JSON-szűrőt írsz, és nem akarsz Go értékeket létrehozni.

### 3.2. Szabványos uuid csomag

Végre. RFC 9562, a standard könyvtárban, `go get` nélkül.

```go
import "uuid"

func main() {
	fmt.Println(uuid.NewV4())
	// b97aa695-da08-472c-af81-ff088129019f

	// v7: az első 48 bit időbélyeg, így ezek mindig létrehozási sorrendben rendeződnek
	a, b := uuid.NewV7(), uuid.NewV7()
	fmt.Println(a.Compare(b) < 0) // true

	u, err := uuid.Parse("urn:uuid:0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b")
	fmt.Println(u, err)
	// 0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b <nil>

	fmt.Println(uuid.Nil(), uuid.Max())
	// 00000000-0000-0000-0000-000000000000 ffffffff-ffff-ffff-ffff-ffffffffffff
}
```

A teljes API: `New`, `NewV4`, `NewV7`, `Nil`, `Max`, `Parse`, `MustParse`, és egy típus: `type UUID [16]byte`. Ennyi. A teljes csomag dokumentációját el tudod olvasni, mire kihűl a kávéd.

Érdemes tudni a részleteket:

- **A `UUID` `[16]byte`**, így a `==` működik, és közvetlenül kulcsként használható térképekben. Ugyanaz a tervezés, mint a `google/uuid`-nál.
- **A `Nil` és a `Max` függvények, nem változók.** Mert egy csomagszintű `var Nil UUID` olyan, mint egy megtöltött pisztoly a saját lábadra irányítva, és valakinek előbb-utóbb rá fog írni.
- A véletlen biteket **kriptográfiailag biztonságos** generátor adja.
- Megvalósítja az `encoding.TextMarshaler`/`TextUnmarshaler`/`TextAppender` interfészeket, így közvetlenül beilleszthető JSON struktúrákba.
- **A `NewV7` az, amelyet adatbázis-elsődleges kulcsokhoz akarsz.** Az idő szerinti rendezés miatt a B-fa indexed nem fragmentálódik, amiben a v4 UUID-k hírhedten rosszak.

Mostantól törölhetsz egy függőséget. Ha v1/v3/v5 vagy bonyolultabb feldolgozási opciók kellenek, a külső könyvtáraknak továbbra is van feladatuk.

### 3.3. crypto/mldsa: kvantumbiztos aláírások, és hatalmasak

A Go 1.24 a `crypto/mlkem` csomaggal adott kvantumbiztos kulcscserét. A Go 1.27 hozza a másik felet: az ML-DSA aláírásokat, a FIPS 204 szabvány szerint.

```go
sk, _ := mldsa.GenerateKey(mldsa.MLDSA65())
pk := sk.PublicKey()

msg := []byte("release the gophers")
opts := &mldsa.Options{Context: "gosuda.org/blog"}

sig, _ := sk.Sign(rand.Reader, msg, opts)

fmt.Println("private key seed:", len(sk.Bytes()), "bytes")  // 32
fmt.Println("public key:", len(pk.Bytes()), "bytes")        // 1952
fmt.Println("signature:", len(sig), "bytes")                // 3309

fmt.Println(mldsa.Verify(pk, msg, sig, opts))
// <nil>
fmt.Println(mldsa.Verify(pk, msg, sig, &mldsa.Options{Context: "other"}))
// mldsa: invalid signature
```

Nézd meg ezeket a számokat. **Egyetlen aláírás 3309 bájt.** Az Ed25519 64. Ez 50-szeres növekedés, és a nyilvános kulcs is közel 2 KB. Ha ezekből pár darabot egy tanúsítványláncba teszel, a TLS-kézfogásodnak saját MTU-stratégia kell.

Ez a kvantumellenállás mai tényleges ára, és ezért nem vált át senki holnap mindenre. De most már a standard könyvtárban van, ami pontosan ott a helye, _mielőtt_ szükséged lenne rá.

Az `Options.Context` a domain-szétválasztás: ugyanazzal a kulccsal különböző célokra írhatsz alá, mindegyikhez más contextet használva, és az egyik contextben készült aláírás nem ellenőrizhető egy másikban. A fenti példa pontosan ezt mutatja: ugyanaz a kulcs, ugyanaz az üzenet, más context, elutasítva.

A `PrivateKey` megvalósítja a `crypto.Signer` interfészt, így beilleszthető a meglévő interfészekbe. A `crypto/x509` kezeli az ML-DSA kulcsokat és aláírásokat, a `crypto/tls` pedig támogatja az `MLDSA44`/`MLDSA65`/`MLDSA87` aláírási sémákat a TLS 1.3-ban.

Van még egy `SignDeterministic` is, amely kihagyja a véletlenszerűséget: hasznos tesztekhez és reprodukálható buildekhez.

### 3.4. simd: vektorutasítások assembly nélkül

A Go 1.26 kísérleti jelleggel bevezette az architektúra-specifikus `simd/archsimd` csomagot. A Go 1.27 hozzáadja a `simd` csomagot, amely **hordozható és vektorszélesség-független**. Engedélyezés: `GOEXPERIMENT=simd`.

```go
// dst = a*x + y
func axpy(dst, x, y []float32, a float32) {
	va := simd.BroadcastFloat32s(a)
	w := va.Len()

	i := 0
	for ; i+w <= len(x); i += w {
		vx := simd.LoadFloat32s(x[i:])
		vy := simd.LoadFloat32s(y[i:])
		vx.MulAdd(va, vy).Store(dst[i:])
	}
	// a farok kezelése részleges betöltéssel/tárolással — nincs skaláris takarító ciklus
	if i < len(x) {
		vx, _ := simd.LoadFloat32sPart(x[i:])
		vy, _ := simd.LoadFloat32sPart(y[i:])
		vx.MulAdd(va, vy).StorePart(dst[i:])
	}
}
```

```bash
$ GOEXPERIMENT=simd go1.27rc3 run ./simddemo
vector bits: 128 emulated: false
[4 7 10 13 16 19 22 25 28 31]
```

A lényeges tervezési döntés: **a vektorszélességet soha nincs kódba írva.** Futásidőben kérdezed meg a `va.Len()`-től. A `VectorBitSize()` megmondja a tényleges szélességet, az `Emulated()` pedig azt, hogy valódi hardvert kaptál-e, vagy udvarias szoftveres utánzatot. A Macem 128 bites NEON-t jelzett; egy AVX-512-es gép többet; egy olyan gép, amelyen semmi nincs, emulációt jelez, és a kód attól még lefut.

Az `LoadFloat32sPart`/`StorePart` külön említést érdemel. A kézzel írt SIMD legbosszantóbb része mindig a tömb végén maradó töredék, és ez a függvény ezt külön skaláris ciklus nélkül kezeli.

Még mindig kísérleti, az API még instabil, ne tedd éles környezetbe. De az, hogy ez Go kód, és nem assembly vagy cgo, valóban nagy dolog.

### 3.5. hash/maphash.Hasher

Új interfész, amely az érték típus és a hash-alapú tárolók közötti szerződést írja le:

```go
type Hasher[T any] interface {
	Hash(*Hash, T)
	Equal(x, y T) bool
}
```

Miért? A Go beépített map típusa csak `comparable` kulcsokat fogad el. Nem használhatsz slice-t kulcsként. Nem definiálhatsz "egyenlő kis- és nagybetű-érzéketlenül" összehasonlítást. A `Hasher` mindkét problémát egyszerre oldja meg:

```go
type CaseInsensitive struct{}

func (CaseInsensitive) Hash(h *maphash.Hash, s string) {
	h.WriteString(strings.ToLower(s))
}
func (CaseInsensitive) Equal(x, y string) bool {
	return strings.ToLower(x) == strings.ToLower(y)
}

var seed = maphash.MakeSeed()

func hashOf[T any](hr maphash.Hasher[T], v T) uint64 {
	var h maphash.Hash
	h.SetSeed(seed)   // ugyanaz a seed, különben ennek az egésznek nincs értelme
	hr.Hash(&h, v)
	return h.Sum64()
}

fmt.Println(hashOf(CaseInsensitive{}, "Go") == hashOf(CaseInsensitive{}, "GO"))
// true
```

Az egyszerű `==` szemantikához ott van a `ComparableHasher[T]`:

```go
hashOf(maphash.ComparableHasher[int]{}, 42)
```

A csapda: **a `Hasher` interfész, nem adatstruktúra.** Még nincs olyan standard hash-tábla vagy Bloom-szűrő, amely használná. Ez egy jövőbeli tárolócsomag alapozása. Ma akkor használnád, ha saját struktúrát építesz, vagy egy meglévő implementációt fogyasztasz, például a `go/types.Hasher`-t, amely lehetővé teszi, hogy a `types.Type` térkép kulcsként szerepeljen, az `Identical` szerint.

A seed kezelése a te felelősséged. Ha a fenti `hashOf` minden híváskor új seedet generálna, az azonos értékek különböző hash-t kapnának, és a példa `false`-t írna ki, ami pontosan az a hiba, amelyet az első próbálkozásomnál elkövettem. Egy seed tárolónként. (A seed véletlenszerű, hogy kivédje a hash-árasztásos DoS-támadásokat, ezért nem egyszerű konstans.)

### 3.6. httptest.NewTestServer + synctest.Sleep: a szundikáló ütés

Ha ebből a kiadásból csak egy dolgot használsz, legyen ez.

A `testing/synctest` a Go 1.25-ben érte el a stabil állapotot, és egy hamis órát adott a konkurens kód teszteléséhez. Csakhogy abban a pillanatban, amikor a tesztben valódi hálózathoz értél, az illúzió összeomlott. A Go 1.27 `httptest.NewTestServer` függvénye **memóriabeli hamis hálózatot** használ, így a buborék sértetlen marad. A `synctest.Sleep` (= `time.Sleep` + `synctest.Wait`) pedig teljessé teszi.

Itt egy teszt exponenciális visszalépéssel újrapróbálkozáshoz:

```go
func TestRetryWithBackoff(t *testing.T) {
	synctest.Test(t, func(t *testing.T) {
		var hits int
		srv := httptest.NewTestServer(t, http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				hits++
				if hits < 3 {
					w.WriteHeader(http.StatusServiceUnavailable)
					return
				}
				io.WriteString(w, "ok")
			}))

		client := srv.Client()
		start := time.Now()
		var body string
		for attempt := range 5 {
			resp, err := client.Get(srv.URL)
			if err != nil {
				t.Fatal(err)
			}
			b, _ := io.ReadAll(resp.Body)
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				body = string(b)
				break
			}
			synctest.Sleep(time.Duration(1<<attempt) * time.Second)
		}

		if body != "ok" {
			t.Fatalf("got %q", body)
		}
		// a visszalépésnek pontosan 1s + 2s = 3s kell legyen
		if elapsed := time.Since(start); elapsed != 3*time.Second {
			t.Fatalf("elapsed = %v, want 3s", elapsed)
		}
		t.Logf("hits=%d elapsed=%v", hits, time.Since(start))
	})
}
```

```
=== RUN   TestRetryWithBackoff
    x_test.go:48: hits=3 elapsed=3s
--- PASS: TestRetryWithBackoff (0.00s)
```

Olvasd el kétszer. Három másodperc visszalépést szimulált egy valódi HTTP szerver ellen, és **0,00 másodperc** alatt lefutott. Az állítás pedig `elapsed == 3*time.Second`, vagyis _pontosan_ három másodperc, nem "legalább három másodperc, ütemező-ingadozással együtt". A hamis órák nem ingadoznak.

A `synctest.Sleep` egy konkrét okból létezik: ha a tesztben ugyanannyit alszol, mint a tesztelt kódban, nem lehet tudni, melyik ébred fel előbb. A `synctest.Sleep` alszik, **majd addig vár**, amíg a buborékban lévő többi goroutine maradandóan blokkolttá nem válik, így a rendszert akkor figyeled meg, amikor már megnyugodott.

Időtúllépések, újrapróbálkozások, áramkörmegszakítók, sebességkorlátozók: minden időfüggő HTTP-kliens teszted gyorssá és determinisztikussá válhat. Ha a tesztsorozatodat jelenleg `time.Sleep(100 * time.Millisecond)` és remény tartja össze, ez a kiút.

### 3.7. net/http változások, amelyek ténylegesen érintik az éles környezetet

Csendesek, de megjelennek a metrikáidban.

**A HTTP/1 választörzsek zárásnál automatikusan kiürülnek.** A el nem olvasott tartalom zárásnál kiürül (egy konzervatív határig), így a kapcsolat újra felhasználható. Ez azt jelenti, hogy végre törölheted ezt a varázsigét, amelyet mindenki 2016 óta ugyanarról a Stack Overflow válaszról másol:

```go
// már nincs szükség rá
defer func() {
	io.Copy(io.Discard, resp.Body)
	resp.Body.Close()
}()
```

A legtöbb programnál ez nem tesz semmit, vagy kis nyereség. Ha _rosszabbá_ teszi, valószínűleg abba a kategóriába tartozol, amelyet a kiadási jegyzetek udvariasan leírnak: `Transport.MaxIdleConns` értéke 0, vagy kérésenként új `Client`, amely teljesen megkerüli az üresjárati kapcsolatok korlátját. A `Transport.DisableKeepAlives = true` elfedi a problémát, de a kiadási jegyzetek valódi tanácsa az, hogy "valószínűleg hasznos lenne mélyebben megnézni", ami Go-csapat nyelven azt jelenti: _van nagyobb problémád is_.

**HTTP/2 kliens prioritás (RFC 9218).** A szerver mostantól figyelembe veszi a kliens prioritási jelzéseit. Ha a régi körbeforgó ütemezést részesíted előnyben, állítsd a `Server.DisableClientPriority = true` értékre.

**`Server.MaxHeaderValueCount`.** Korlátozza, hány fejlécértéket fogad el a szerver, alapértelmezés szerint a `DefaultMaxHeaderValueCount`. Egy újabb ajtó záródott be azzal szemben, hogy "küldj tízezer fejlécet, és nézd meg, mi történik".

**ALPN felhasználó által biztosított kapcsolatokon.** Ha a `net.Conn` megvalósítja a `ConnectionState() tls.ConnectionState` metódust, a `Transport` és a `Server` elvégzi rajta a TLS ALPN egyeztetést, így a proxyn keresztül futó egyedi dialerek is tudnak HTTP/2-t egyeztetni.

### 3.8. Az apróságok, amelyek örömet okoznak

**`strings.CutLast` / `bytes.CutLast`.** A `Cut` az _első_ elválasztónál vág. "Utolsó" változat nem volt, ezért mindenki kézzel írta meg a `LastIndex`-et és a szeletelést, és nagyjából az emberek 30%-a elsőre egy off-by-one hibát vétett.

```go
name, ext, ok := strings.CutLast("archive.tar.gz", ".")
// archive.tar gz true
```

Kiválóan jó fájlkiterjesztésekhez és a `host:port` feldolgozásához, ahol az _utolsó_ kettőspontot kell megtalálni, mert az IPv6-címek tele vannak velük.

**`math/big.Int.Divide`.** Osztás explicit kerekítési móddal, felváltva a "`Quo` vagy `Div` kell nekem?" pénzfeldobással:

```go
x, y := big.NewInt(-7), big.NewInt(2)

new(big.Int).Divide(x, y, new(big.Int), big.Trunc)  // q=-3 r=-1
new(big.Int).Divide(x, y, new(big.Int), big.Floor)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Round)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Ceil)   // q=-3 r=-1
```

Ha olyan területen dolgozol, ahol a kerekítési szabályt rendelet írja elő, ez neked szól.

**`url.URL.Clone` és `url.Values.Clone`.** Mély másolatok. A régi `*u` sekély másolat megosztotta a `Userinfo` mutatót, ami pontosan olyan hibához vezetett, amelynek megtalálása egy napba, javítása öt másodpercbe kerül.

```go
orig, _ := url.Parse("https://gosuda.org/blog?tag=go&tag=1.27")
clone := orig.Clone()
clone.Host = "example.com"
fmt.Println(orig.Host, clone.Host)  // gosuda.org example.com
```

A **`net.UnixConn`** olvasó metódusai mostantól közvetlenül `io.EOF`-ot adnak vissza, ahelyett, hogy `net.OpError`-ba csomagolnák. Az `err == io.EOF` most működik, ahogy mindig is kellett volna.

**`database/sql.ConvertAssign` és `driver.RowsColumnScanner`.** Meghajtó-fejlesztőknek szóló funkciók. Az első feltárja azokat a típuskonverziókat, amelyeket a `Rows.Scan` végez; a második lehetővé teszi, hogy a meghajtók közvetlenül felhasználói célba olvassanak, átugorva egy köztes allokációt.

**`unicode` 15 → 17.** Két verzió egy ugrással. A karakterosztályozás és a normalizálás viselkedése finoman eltolódhat. Ha vannak erre épülő teszteid, rá fogsz jönni.

**A `compress/flate` gyorsabb lett**, és **kimeneti bájtjai eltérhetnek a Go 1.26-tól.** Ez továbbgyűrűzik az `archive/zip`, `compress/gzip`, `compress/zlib` és `image/png` csomagokra. Ha olyan golden-file teszteid vannak, amelyek a tömörített kimenet hash-ét ellenőrzik, azok el fognak törni, és ez nem regresszió lesz. Ellenőrizd ezt _mielőtt_ frissítesz, nem az incidens-utólagos elemzés során.

## 4. Eszköztár

### 4.1. A go fix több modernizálót kapott

A `go fix` csendben kódmodernizáló eszközzé válik. A Go 1.27 hozzáadja az `atomictypes`, `embedlit`, `slicesbackward` és `unsafefuncs` modernizálókat. A `-diff` kapcsolóval előnézetet nézhetsz:

```go
package fixdemo

import (
	"sync"
	"sync/atomic"
)

type Base struct{ ID int }
type User struct {
	Base
	Name string
}

func New() User {
	return User{Base: Base{ID: 1}, Name: "gopher"}
}

var counter int64

func Incr() { atomic.AddInt64(&counter, 1) }

func Reverse(s []string) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
	}
}

func Spawn(wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		defer wg.Done()
	}()
}
```

```bash
$ go1.27rc3 fix -diff ./fixdemo
```

```diff
 import (
+	"slices"
 	"sync"
 	"sync/atomic"
 )

 func New() User {
-	return User{Base: Base{ID: 1}, Name: "gopher"}
+	return User{ID: 1, Name: "gopher"}
 }

-var counter int64
+var counter atomic.Int64

-func Incr() { atomic.AddInt64(&counter, 1) }
+func Incr() { counter.Add(1) }

 func Reverse(s []string) {
-	for i := len(s) - 1; i >= 0; i-- {
-		_ = s[i]
+	for _, v := range slices.Backward(s) {
+		_ = v
 	}
 }

 func Spawn(wg *sync.WaitGroup) {
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-	}()
+	wg.Go(func() {
+	})
 }
```

Négy modernizáló futott le egyetlen kis fájlon:

- `embedlit`: a beágyazott literálokat átírja az 1.2. szakaszban bemutatott új szintaxisra
- `atomictypes`: az `atomic.AddInt64(&x, 1)` átíródik `atomic.Int64.Add(1)`-re, ami lehetetlenné teszi a nem atomikus hozzáférést, és csendben kijavítja a 32 bites igazítási hibákat, amelyekről nem is tudtál
- `slicesbackward`: a visszafelé futó ciklusokból `slices.Backward` lesz
- `waitgroupgo`: az `Add`/`go`/`Done` rituálé `wg.Go`-vá válik (az 1.26-os `waitgroup` nevet átnevezték a félreértések elkerülése érdekében)

A `go tool fix help` mind a 26 modernizálót felsorolja; a `go tool fix help <név>` egyet elmagyaráz. Ezenkívül a `fmtappendf` "stilisztikai aggályok miatt" eltávolításra került, ami gyönyörűen diplomatikus módja annak leírni, ami abban az issue-szálban történt.

Egy régi kódbázison a `go fix ./...` futtatása valóban kielégítő délután. Persze előbb olvasd el a diffet.

### 4.2. A go test alapértelmezés szerint futtatja a stdversion ellenőrzést

Ez az a változás, amely a legvalószínűbben megzavarja a napodat.

A `go test` mostantól alapértelmezés szerint lefuttatja a `stdversion` vet-ellenőrzést, amely megjelöli azokat a standard könyvtári szimbólumokat, amelyek újabbak, mint amit a `go.mod` `go` direktívája megenged:

```go
// a go.mod ezt mondja: go 1.24
package sv

import "strings"

func Ext(name string) string {
	_, ext, _ := strings.CutLast(name, ".")  // a CutLast egy 1.27-es szimbólum
	return ext
}
```

```
$ go1.27rc3 test ./...
# sv
./x.go:6:23: strings.CutLast requires go1.27 or later (module is go1.24)
FAIL	sv [build failed]
```

Ez kiküszöböli a klasszikus hibát, amikor minden a te gépeden (a legújabb toolchainnel) működik, de a CI-ben vagy egy felhasználó régebbi környezetében elszáll. Ha konzervatív `go` direktívával publikálsz könyvtárakat, ez ajándék.

### 4.3. A go test -json kapott OutputType mezőt

Az `"Action":"output"` sorok mostantól tartalmazhatnak egy opcionális `"OutputType"` mezőt: `"error"`, `"error-continue"` vagy `"frame"` értékkel.

```
'frame'  '=== RUN   TestFail\n'
None     '    x_test.go:6: hello\n'
'error'  '    x_test.go:7: boom\n'
'frame'  '--- FAIL: TestFail (0.00s)\n'
'frame'  'FAIL\n'
```

A `t.Log` kimenet (nincs mező), a `t.Error` kimenet (`error`) és a keretrendszer által generált sorok (`frame`) mostantól megkülönböztethetők. Bárki, aki regexet írt azért, hogy kitalálja, mely teszt-kimeneti sorok "valódiak", most törölheti.

### 4.4. A go doc fejlesztései

**`package@version` szintaxis.** Egy adott verzió dokumentációját úgy olvashatod, hogy nem adod hozzá a modulodhoz:

```bash
$ go1.27rc3 doc golang.org/x/sync/errgroup@v0.10.0
package errgroup // import "golang.org/x/sync/errgroup"
...
```

Hasznos, ha frissítés előtt ellenőrizni szeretnéd, mi változott.

**`-ex` és példa-forrás kiírása:**

```bash
$ go1.27rc3 doc -ex strings | grep Example
    func ExampleClone()
    func ExampleCompare()
    func ExampleContains()
    func ExampleCut()
    func ExampleCutPrefix()
    ...

$ go1.27rc3 doc strings.ExampleCut
package main

import (
	"fmt"
	"strings"
)

func main() {
	show := func(s, sep string) {
		before, after, found := strings.Cut(s, sep)
		fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
	}
	show("Gopher", "Go")
	...
}

Output:
Cut("Gopher", "Go") = "", "pher", true
```

A példa forrása _és_ a várt kimenet, a terminálban, anélkül, hogy megnyitnál egy böngészőlapot, amely jövő csütörtökön is nyitva lesz.

### 4.5. A go mod tidy rendet teremt a require blokkjaidban

Go 1.27-es vagy újabb modulok esetén a `go mod tidy` a szétszórt `require` blokkokat legfeljebb kettőre (közvetlen és közvetett) vonja össze, megőrizve a hozzájuk tartozó megjegyzéseket.

```go
// Előtte
module tidydemo

go 1.27

require golang.org/x/sync v0.10.0

// networking
require golang.org/x/net v0.33.0

require (
	golang.org/x/sys v0.28.0 // indirect
)

require golang.org/x/text v0.21.0 // indirect
```

```go
// Utána: go mod tidy
module tidydemo

go 1.27

require (
	// networking
	golang.org/x/net v0.33.0
	golang.org/x/sync v0.10.0
)
```

Figyeld meg, hogy a `// networking` megjegyzés megmaradt. Ez főként a **Git merge-konfliktusok feloldása** utáni takarításban segít, mert ott születnek a szétszórt `require` blokkok. Ha a csapatodban háromnál többen adnak hozzá függőségeket, a `go.mod` konfliktusaid észrevehetően csendesebbek lesznek.

### 4.6. Egyéb apróságok

- **A `bzr` támogatás megszűnt.** Ha ez téged érint, valóban szívesen hallanám a történetedet.
- **A `go tool trace -http` csak portot megadva a localhostra köt.** A `-http=:6060` már nem hallgat minden interfészen. Használd a `-http=0.0.0.0:6060` értéket, ha ezt szánod. Ez most összhangban van a `go tool pprof` viselkedésével, és megakadályozza a néha véletlen nyilvános profilozót.
- **A válaszfájlok (`@file`)** támogatottak a `compile`, `link`, `asm`, `cgo`, `cover` és `pack` eszközöknél, GCC-kompatibilis formátumban. Olyan build-rendszereknél, amelyek túllépik a parancssori hosszkorlátokat: üdv, Bazel.

## 5. Fordító, linker, portok

**Fordító.** A `//line` direktívákban a relatív fájlnevek mostantól a tartalmazó fájl könyvtárához képest oldódnak fel, a `go/scanner` viselkedését követve. Ez akkor fontos, ha kódgenerátorokat írsz.

A függvénylitálok (closure) szimbólumnevei is egyszerűbbek lettek: ugyanaz a név függetlenül az inlinálástól, és ugyanazon literál több példánya megoszthat kódot a binárisban. Funkcionális változás nincs, _kivéve_: a `reflect.Value.Pointer` által függvényazonosságot összehasonlító kód gyakrabban fogja "egyenlőnek" látni a függvényeket, mint korábban. Ez az összehasonlítás soha nem volt érvényes, de ha van ilyened, most kezd hangosabban hazudni.

**Linker.** Az új `-macos` és `-macsdk` kapcsolók beállítják az OS- és SDK-verziót a macOS `LC_BUILD_VERSION` betöltési parancsában.

**Portok.**

- **A Darwin mostantól legalább macOS 13 Ventura verziót igényel**, ahogy az 1.26-ban be lett jelentve. Ellenőrizd a CI futtatóidat.
- **A PowerPC (`GOOS=linux GOARCH=ppc64`) átállt az ELFv2 ABI-ra.** Linux kernel 3.13+ szükséges (a RHEL7 3.10-re visszaportolta). A cgo, a PIE és a külső linkelés mostantól támogatott. Ha cgo-t használsz, de statikus, tisztán Go bináris kell, állítsd a `CGO_ENABLED=0` értéket.

## 6. Frissítési ellenőrzőlista

A Go 1.27 komolyan veszi a kompatibilitást, de mielőtt a verziót emeled, ellenőrizd ezeket:

- [ ] **Tömörített kimenetre vonatkozó golden-file tesztek.** A `compress/flate` kódolót váltott; a gzip/zip/png bájtok eltérhetnek.
- [ ] **JSON hibaüzenet-szövegeket illesztő tesztek.** A viselkedés azonos; **a hibaszöveg nem az.**
- [ ] **Eltávolított GODEBUG-ok a `go.mod`-ban.** `asynctimerchan`, `gotypesalias`, `tlsrsakex`, `tls3des`, `tls10server`, `tlsunsafeekm`, `x509keypairleaf`: ezek csak akkor törik el a buildet, ha a _régi_ értékükre vannak állítva.
- [ ] **`stdversion` szabálysértések.** A `go test` ezeket mostantól elkapja. Ha konzervatív `go` direktívájú könyvtárat karbantartasz, futtasd korán.
- [ ] **macOS 12 vagy régebbi CI futtatók.** A támogatás megszűnt.
- [ ] **Függvénylitál-szimbólumnevekre állító tesztek**, és **`reflect.Value.Pointer` függvény-összehasonlítások.**
- [ ] **`Transport.MaxIdleConns = 0`** vagy kérésenkénti `Client`. A választörzsek automatikus kiürítésével együtt ez lassulást okozhat.

## Záró gondolatok

A Go 1.27 az a kiadás, amelynél egyszerre sok halogatott karbantartási feladat jött el.

A generikus metódusok hiányzó darabja voltak az 1.18-as generikus munkának. A struct literál mezőválasztók lezártak egy tizenegy éves issue-t. Az `encoding/json/v2` két év javaslatvitája volt. Az `uuid` pedig olyan rést tömött be, amelyet szinte minden Go projekt évtizede ugyanazzal a külső függőséggel foltozott.

Ha prioritási sorrendet szeretnél az érték kinyeréséhez:

1. **`httptest.NewTestServer` + `synctest.Sleep`**: az időfüggő HTTP-tesztek gyorssá és determinisztikussá válnak. A legjobb ráfordítás-hozam arány az egész kiadásban.
2. **A goroutine-szivárgási profil**: tegyél ki egy végpontot staging környezetbe, és készülj a megalázkodásra.
3. **A visszakövetés goroutine-címkéi**: három sor `pprof.Do` a kérés-belépési pontodon a következő hajnali 3 órai stack dumpodat olvashatóvá teszi.
4. **`go fix ./...`**: hagyd, hogy az eszköz elvégezze a modernizálást.
5. **`encoding/json/v2`**: nincs sietség. Egyesével, opciónként vezesd be.

Vedd elő a `go1.27rc3`-at, és futtasd rá most a tesztsorozatodat. Mindez az ellenőrzőlista jóval kellemesebb egy keddi délutánon felfedezni, mint egy incidens közepén.

**Hivatkozások**

- [Go 1.27 Release Notes](https://go.dev/doc/go1.27)
- [The Go Programming Language Specification](https://go.dev/ref/spec)
- [proposal: encoding/json/v2 (#71497)](https://go.dev/issue/71497)
- [proposal: portable SIMD (#78902)](https://go.dev/issue/78902)
- [proposal: hash/maphash.Hasher (#70471)](https://go.dev/issue/70471)
