Aggiornamento a Go 1.27: cosa si rompe e cosa adottare per primo
json/v2 sotto il cofano, uuid della libreria standard e trappole GODEBUG.
Go 1.27 è uscita nell’agosto 2026 e, per la maggior parte dei moduli, l’aggiornamento consiste in una singola riga nel file go.mod seguita da un passaggio di go fix — ma un piccolo numero di modifiche incide in modo silenzioso. Ecco cosa si rompe effettivamente e cosa adottare per primo.
Le modifiche principali sono strutturali: encoding/json ora è supportato dall’implementazione json/v2 sotto il cofano, il pacchetto uuid è entrato a far parte della libreria standard e i metodi possono finalmente dichiarare i propri parametri di tipo. Nessuna di queste impone una riscrittura e il team Go ha mantenuto la solita promessa di compatibilità: quasi tutto viene compilato ed eseguito senza modifiche.

Tuttavia, non tutto è tranquillo. Diverse impostazioni GODEBUG ora causano il fallimento della compilazione con i loro valori vecchi, compress/flate produce byte diversi, go test applica un nuovo controllo stdversion e i nomi dei simboli generati dal compilatore sono cambiati. Se hai bisogno di un ripasso della sintassi sulle parti che non sono cambiate, la sintesi rapida di Go resta valida; questa guida copre il resto, con comandi e correzioni per ogni voce.
Il percorso di aggiornamento in un diagramma
Il team Go raccomanda un ordine: prima la compilazione, poi le correzioni. go fix riscrive il codice per le nuove API e gli idiomi, ma solo una compilazione pulita ti dice se qualcuna delle modifiche a livello di runtime ti ha colpito.
Inizia con la toolchain stessa. Se gestisci le toolchain tramite il comando go, fissa la versione in go.mod oppure installa esplicitamente:
go install golang.org/dl/go1.27@latest
go1.27 download
go1.27 version
In alternativa, imposta GOTOOLCHAIN=go1.27.0 nell’ambiente, oppure aggiungi toolchain go1.27.0 a go.mod e lascia che il comando go lo recuperi. Il compagno orientato ai moduli sull’struttura del workspace Go spiega come le righe go e toolchain interagiscano attraverso più moduli; la versione breve per un singolo modulo è di seguito.
Passo 1: aumentare la riga go e chiarire le impostazioni GODEBUG rimosse
go mod edit -go=1.27
go mod tidy
go build ./...
Aumentare la direttiva go attiva le nuove funzionalità del linguaggio (metodi generici, chiavi di campi promossi in letterali di struttura, inferenza più ampia dei tipi di funzione) e rende il comando go rigoroso riguardo alle impostazioni GODEBUG il cui supporto è stato rimosso. Go 1.27 ha rimosso definitivamente questi:
asynctimerchan— i canali timer e ticker sono ora sempre non limitati (unbuffered)gotypesaliastlsunsafeekm,tlsrsakex,tls3des,tls10serverx509keypairleaf
La nuova regola: il comando go accetta ancora queste impostazioni in go.mod (voci godebug) o nel sorgente (commenti //go:debug) se sono impostate al valore predefinito finale esistente prima della rimozione. Un valore vecchio — quello che disabilitava una modifica comportamentale — fa fallire la compilazione:
//go:debug asynctimerchan=0 // errore di compilazione: valore vecchio di un'impostazione rimossa
//go:debug asynctimerchan=1 // accettato, ma elimina la riga
Se la tua compilazione fallisce qui, il codice dipendeva dai canali timer limitati (buffered) pre-Go-1.23. La correzione è nel codice, non nella configurazione: controlla l’uso di Timer.Stop e Timer.Reset rispetto ai pattern di contesto, annullamento e timeout che si affidano ai canali sincroni. La stessa ricerca si applica alle impostazioni relative a TLS — qualsiasi cosa che fissasse tls3des=1 o tls10server=1 manteneva vive vecchie suite di cifratura, e Go 1.27 rifiuta di compilarle.
Passo 2: eseguire go test — la trappola di stdversion
go build e go test non sono più d’accordo. go test di Go 1.27 esegue il controllo vet stdversion in modo predefinito, che segnala i simboli della libreria standard troppo nuovi per la versione go in vigore nel file di riferimento.
// go.mod dice ancora "go 1.26"
func Base(p string) string {
_, file, ok := strings.CutLast(p, "/") // aggiunto in Go 1.27
if !ok {
return p
}
return file
}
Con una toolchain 1.27, go build ha successo — il compilatore ha strings.CutLast — mentre go test fallisce il passaggio vet perché go.mod dichiara ancora 1.26. Con una toolchain reale 1.26 il file non verrebbe compilato affatto. La lezione è procedurale: non validare mai un aggiornamento solo con go build; esegui la suite di test completa, poi aumenta la riga go. La suite vet in go test ha anche raccolto una nuova segnalazione printf: fmt.Errorf("...: %w", p) dove p è un *E ed E implementa error ora avvisa, perché avvolgere un puntatore produce asserzioni di tipo e controlli errors.Is che falliscono. Questo modalità di fallimento si mostra nel codice di gestione degli errori esattamente come descritto nei pattern dell’architettura di gestione degli errori Go — controlla il tipo dell’operando, non solo il verbo.
encoding/json: il motore v2 sotto il cofano
La modifica più grande sotto il cofano: encoding/json/v2 (sperimentale da Go 1.25 come GOEXPERIMENT=jsonv2) è diventato stabile, e il classico pacchetto encoding/json è ora supportato dall’implementazione v2. Per i chiamanti v1, il comportamento è preservato — le slice nil vengono ancora codificate come null, le chiavi delle mappe vengono ancora ordinate — con una differenza visibile: il testo esatto di alcuni messaggi di errore è cambiato. Se i test o il monitoraggio corrispondono a stringhe di errore, aspettati differenze.
Tre cose possono ancora andare storte:
- Testo degli errori. Tutte le asserzioni
strings.Contains(err.Error(), ...)basate sul vecchio formato v1 hanno bisogno di aggiornamenti. - I percorsi sensibili alle prestazioni possono spostarsi. Lo smarshalling è più veloce, ma la riscrittura cambia quali operazioni allocano; i benchmark segnati in CI lo mostreranno.
- Via d’uscita per la compatibilità.
GOEXPERIMENT=nojsonv2ripristina l’implementazione originale v1 al momento della compilazione. È temporaneo — pianifica di rimuoverlo, non di spedirlo con il software.
Un effetto di secondo ordine per i test golden: il codificatore di compress/flate è cambiato nello stesso rilascio, quindi gli output di archive/zip, compress/gzip, compress/zlib e image/png possono differire byte per byte da Go 1.26 anche se si decodificano in modo identico. Rigenera i fixture compressi invece di fissare i byte vecchi.
Migrazione deliberata all’API v2
L’API v2 è un import separato (encoding/json/v2) con Marshal, Unmarshal e un sistema di opzioni. Il percorso di migrazione supportato è far sì che le chiamate v2 si comportino come v1 per prima cosa, poi stringere selettivamente:
import (
"encoding/json/v2"
"encoding/json/jsontext"
)
// Passo 1: comportamento identico a encoding/json — un cambiamento sicuro e meccanico
out, err := jsonv2.Marshal(v, jsonv2.DefaultOptionsV1())
// Passo 2: opt-in a un default v2 alla volta
out, err := jsonv2.Marshal(v,
jsonv2.DefaultOptionsV1(),
jsonv2.FormatNilSliceAsNull(false)) // le slice nil vengono ora codificate come []
Una volta che rimuovi DefaultOptionsV1, si applicano i default v2, e sono più rigorosi e più interoperabili di v1:
| Comportamento | v1 (encoding/json) |
default v2 |
|---|---|---|
| UTF-8 non valido nelle stringhe | Sostituito con U+FFFD | Errore |
| Nomi duplicati in un oggetto | Permesso | Errore |
| Slice nil / mappe nil | null |
[] / {} |
| Abbincamento dei campi struttura | Fallback non sensibile alla maiuscole/minuscole | Sensibile alla maiuscole/minuscole |
| Membri di oggetto sconosciuti | Ignorati | Ignorati (RejectUnknownMembers per errore) |
| Ordine delle chiavi di mappa | Sempre ordinate | Non ordinate per default |
time.Duration |
Codifica stringa integrata | Nessuna codifica predefinita |
Le chiavi di mappa non ordinate sono quelle che sorprendono i test dei file golden: v2 salta l’ordinamento perché l’ordinamento costa tempo. Quando hai bisogno di un output stabile — fixture, test snapshot, rappresentazioni hash — passa json.Deterministic(true), che ripristina l’output ordinato per mappe e strutture. Le model nell’articolo del confronto ORM portano tutte tag json; quei tag continuano a funzionare in v2, ma l’opzione del tag format è stata rimossa durante il test e inline è stato rinominato in embed, quindi cerca con grep le definizioni delle model per entrambi prima di migrare. Per esigenze di basso livello — streaming, elaborazione token per token, gestione di sintassi personalizzata — il pacchetto compagno encoding/json/jsontext espone una macchina a stati Encoder/Decoder sul testo JSON.
uuid stdlib: abbandonare github.com/google/uuid
Go 1.27 aggiunge uuid alla libreria standard, implementando la RFC 9562. La superficie dell’API copre i casi comuni:
id := uuid.New() // attualmente equivalente a NewV4
v7 := uuid.NewV7() // ordinabile per tempo, timestamp 48 bit + casuale
u, err := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
github.com/google/uuid è il secondo modulo Go più dipendente, quindi questa migrazione è importante alla scala. Le due API sono vicine — i tipi sono valori [16]byte confrontabili con == e utilizzabili come chiavi di mappa, String(), MarshalText/UnmarshalText fanno round-trip — e il progetto upstream sta preparando shim go:fix inline in modo che go fix possa riscrivere le chiamate uuid.NewV4() al pacchetto standard ed eliminare la dipendenza.
La decisione di adozione interessante è NewV7. I UUID versione 7 incorporano un timestamp in millisecondi nei bit più significativi, quindi i valori si ordinano nell’ordine di creazione (tranne quando l’orologio di sistema va all’indietro). Per le chiavi primarie del database significa una migliore località degli alberi B rispetto ai UUIDv4 casuali, e un ordinamento simile a created_at gratis nell’identificatore stesso — la stessa proprietà che guida la discussione delle chiavi ordinabili per il tempo nei modelli di dati multi-tenant.
Modifiche al linguaggio: metodi generici e amici
I metodi generici sono la funzionalità linguistica più richiesta del rilascio: una dichiarazione di metodo può ora dichiarare i propri parametri di tipo.
// Go 1.27: math/rand/v2 ha acquisito un metodo generico accanto alla funzione
func (r *Rand) N[Int intType](n Int) Int
Due restrizioni mantengono il sistema di tipi decidibile: i metodi di interfaccia non possono dichiarare parametri di tipo, e i metodi di interfaccia non possono essere implementati da metodi generici. Accanto a questi, le chiavi dei letterali di struttura possono ora essere qualsiasi selettore di campo valido (non solo nomi di campo di livello superiore), e l’inferenza del tipo di funzione si generalizza a tutti i contesti in cui una funzione generica viene assegnata a una variabile di tipo di funzione corrispondente. Nessuno di questi rompe il codice esistente; rimuovono workaround — funzioni helper generiche a livello di pacchetto e istanze esplicite.
go fix è cresciuto con modernizer per pulizie adiacenti: atomictypes, embedlit, slicesbackward e unsafefuncs. Una cautela dai laboratori di aggiornamento pratico: esegui go fix -fix=atomictypes deliberatamente piuttosto che in blocco sulle librerie, poiché può cambiare i tipi delle variabili a livello di pacchetto che altri pacchetti riferiscono. Il modernizer waitgroup è stato rinominato in waitgroupgo; nota che sync.WaitGroup.Go (da 1.25) copre già gran parte di ciò che rileva — vedi i pattern di concorrenza delimitata per come si inserisce nello spegnimento dei worker pool.
Cose che si rompono silenziosamente
Queste sono le modifiche che compilano bene eppure ti costano un giorno di rilascio:
- Nomi dei simboli dei literal di funzione. Il compilatore ora genera nomi più semplici e indipendenti dall’inlining per le closure, e può unire literal di funzione identici. I test che affermano sui nomi dei simboli negli traceback o nei profili falliranno; il codice che confronta i puntatori di codice delle funzioni per uguaglianza è esposto in più casi.
- Allineamento gofmt. Una correzione alla logica di allineamento delle colonne (precedentemente variava tra architetture CPU) rifluisce un piccolo numero di file già formattati. Aspettati differenze solo di spaziatura in un commit di
gofmt ./...dopo l’aggiornamento. - go mod tidy riscrive go.mod. Per i moduli su
go 1.27,tidyora unisce i blocchirequireduplicati in al massimo due blocchi (diretto, indiretto), preservando i commenti. Prendi in carico le differenze del tuogo.mode risolvi eventuali residui di conflitto di merge prima del commit di rilascio, non dopo. - Etichette traceback. Gli traceback per i moduli su
go 1.27includono le etichette dei goroutine diruntime/pprofnella riga di intestazione; disattiva conGODEBUG=tracebacklabels=0se le etichette portano dati sensibili. - go tool trace si lega a localhost.
-http=:6060ora ascolta solo su localhost; passa-http=0.0.0.0:6060esplicitamente quando hai bisogno di accesso remoto (e tieni presente che una porta di profiling su un host condiviso è un rischio — vedi il flusso di profiling di produzione per la configurazione rafforzata).
Sperimentazioni da tenere d’occhio
Due pacchetti sperimentali sono usciti in 1.27, entrambi abilitati con GOEXPERIMENT=simd al momento della compilazione:
simd— un’API portabile e agnostica rispetto alla dimensione del vettore (Int8s,Float32s, …) che mappa a istruzioni hardware dove disponibili. Supporta AVX/AVX2/AVX-512 su amd64, NEON su arm64 e WebAssembly SIMD. La libreria standard usa già questa meccanica internamente: l’implementazione Swiss Table map di Go ne beneficia.simd/archsimd— il layer specifico per architettura per il codice che ha bisogno di conoscere la larghezza del vettore.
Sul lato crittografico, crypto/mldsa implementa lo schema di firma post-quantistica ML-DSA dalla FIPS 204 in tre set di parametri (MLDSA44, MLDSA65, MLDSA87), integrato in crypto/x509 e offerto come schemi di firma TLS 1.3. Lo scambio di chiavi ibrido post-quantistico può ora essere forzato tramite Config.CurvePreferences anche quando i fallback GODEBUG tlsmlkem/tlssecpmlkem sono impostati. Nessuno dei due è un elemento di migrazione oggi, ma entrambi plasmano la superficie di configurazione TLS nei cui servizi cresceranno.
Nuove API da adottare immediatamente
Alcune aggiizioni risolvono l’attrito quotidiano e non costano nulla per adottarle:
// strings.CutLast / bytes.CutLast — il ballo LastIndex, sostituito
before, after, found := strings.CutLast("a/b/c.tar.gz", ".")
// httptest.NewTestServer — rete finta in memoria, pulizia automatica
srv := httptest.NewTestServer(t, mux) // nessuna porta reale, niente defer srv.Close()
httptest.NewTestServer crea un httptest.Server supportato da una rete in memoria invece che da un listener TCP e registra il proprio t.Cleanup. Il suo vero vantaggio è l’abbinamento con testing/synctest: i round-trip HTTP vengono eseguiti in tempo sintetico, rendendo i test di timeout e retry veloci e deterministici — la combinazione è dimostrata nell’articolo sui test di concorrenza con synctest.
Elenco di controllo per l’aggiornamento
L’ordine conta più degli elementi; ogni passo presume che quello precedente sia verde.
| Passo | Modulo piccolo | Servizio / monorepo |
|---|---|---|
| 1. Toolchain | go install golang.org/dl/go1.27@latest |
Fissa toolchain go1.27.0; allinea le immagini CI |
2. go.mod |
go mod edit -go=1.27 && go mod tidy |
Stesso, più risoluzione delle differenze di merge dei blocchi require |
| 3. GODEBUG rimossi | Cerca con grep //go:debug e godebug: |
Audit anche le configurazioni e i flag deployati |
4. go build ./... |
Correggi errori di compilazione | Correggi per modulo; bisetta i fallimenti |
5. go test ./... |
Correggi segnalazioni stdversion/printf |
Esegui prima dell’aumento di go per catturare lo slittamento dei simboli |
6. go fix ./... |
Rivedi le differenze | Un modernizer alla volta |
| 7. Test JSON/golden | Rigenera i fixture | Controlla anche i fixture basati su flate (zip/gzip/png) |
8. GOEXPERIMENT=nojsonv2 |
Salta | Solo temporaneo; segna gli issue per ciò che lo ha imposto |
Note conclusive
Le rotture di Go 1.27 si raggruppano attorno a margini che la maggior parte dei programmi non tocca mai — opt-out di GODEBUG rimossi, asserzioni sui nomi dei simboli, fixture di byte compressi — mentre i suoi successi cadono dove ogni programma vive: JSON più veloce, UUID standard, metodi generici. Un aggiornamento pulito è in gran parte disciplina: go build, poi go test, poi go fix, in quell’ordine, con la riga go alzata prima dell’esecuzione dei test piuttosto che dopo. La via d’uscita nojsonv2 e la regola di accettazione al default finale di asynctimerchan comprano tempo, ma entrambi sono programmati per scomparire, quindi trattare questo aggiornamento come la scadenza per le correzioni differite che eredita da 1.23–1.26.