Atualização para o Go 1.27: O que quebra e o que adotar primeiro
json/v2 sob o capô, uuid da stdlib e armadilhas do GODEBUG.
O Go 1.27 foi lançado em agosto de 2026, e para a maioria dos módulos a atualização consiste em uma única linha no go.mod seguida de uma passagem do go fix — mas algumas mudanças causam problemas silenciosamente. Aqui está o que realmente quebra e o que adotar primeiro.
As principais mudanças são estruturais: o encoding/json agora é suportado pela nova implementação json/v2 por baixo dos panos, o pacote uuid entrou na biblioteca padrão e os métodos finalmente podem declarar seus próprios parâmetros de tipo. Nenhuma dessas mudanças força uma reescrita, e a equipe do Go manteve a promessa usual de compatibilidade — quase tudo compila e roda sem alterações.

Nem tudo está quieto, no entanto. Vários ajustes de GODEBUG agora fazem a compilação falhar em seus valores antigos, o compress/flate produz bytes diferentes, o go test aplica uma nova verificação de stdversion e os nomes de símbolos gerados pelo compilador mudaram. Se você precisa de uma revisão de sintaxe das partes que não mudaram, a folha de dicas Go continua válida; este guia cobre o resto, com comandos e correções para cada item.
O caminho de atualização em um único diagrama
A equipe do Go recomenda uma ordem: compilar primeiro, corrigir depois. O go fix reescreve o código para novas APIs e idiomas, mas apenas uma compilação limpa diz se alguma das mudanças em nível de execução atingiu você.
Comece pela própria toolchain. Se você gerencia toolchains através do comando go, fixe a versão no go.mod ou instale explicitamente:
go install golang.org/dl/go1.27@latest
go1.27 download
go1.27 version
Alternativamente, defina GOTOOLCHAIN=go1.27.0 no ambiente, ou adicione toolchain go1.27.0 ao go.mod e deixe o comando go buscá-la. O companheiro focado em módulos sobre estrutura de workspace Go explica como as linhas go e toolchain interagem através de múltiplos módulos; a versão curta para um único módulo está abaixo.
Passo 1: elevar a linha go e limpar configurações GODEBUG removidas
go mod edit -go=1.27
go mod tidy
go build ./...
Elevar a diretiva go ativa os novos recursos de linguagem (métodos genéricos, chaves de campos promovidos em literais de struct, inferência mais ampla de tipos de função) e torna o comando go rigoroso quanto às configurações GODEBUG cujo suporte foi removido. O Go 1.27 removeu permanentemente estes:
asynctimerchan— canais de timer e ticker agora são sempre sem buffergotypesaliastlsunsafeekm,tlsrsakex,tls3des,tls10serverx509keypairleaf
A nova regra: o comando go ainda aceita estas configurações no go.mod (entradas godebug) ou no código-fonte (comentários //go:debug) se estiverem definidos com o valor padrão final que existia antes da remoção. Um valor antigo — aquele que desativava uma mudança de comportamento — faz a compilação falhar:
//go:debug asynctimerchan=0 // erro de compilação: valor antigo de uma configuração removida
//go:debug asynctimerchan=1 // aceito, mas apague a linha em vez disso
Se sua compilação falhar aqui, o código dependia de canais de timer com buffer anteriores ao Go 1.23. A correção está no código, não na configuração: verifique o uso de Timer.Stop e Timer.Reset contra os padrões de contexto, cancelamento e tempo limite que dependem dos canais síncronos. A mesma busca se aplica às configurações relacionadas a TLS — qualquer coisa fixando tls3des=1 ou tls10server=1 estava mantendo vivas suítes de cifragem antigas, e o Go 1.27 se recusa a compilá-las.
Passo 2: executar go test — a armadilha stdversion
go build e go test não concordam mais. O go test do Go 1.27 executa a verificação vet stdversion por padrão, que relata símbolos da biblioteca padrão que são novos demais para a versão go vigente no arquivo referenciado.
// go.mod ainda diz "go 1.26"
func Base(p string) string {
_, file, ok := strings.CutLast(p, "/") // adicionado no Go 1.27
if !ok {
return p
}
return file
}
Com uma toolchain 1.27, o go build tem sucesso — o compilador tem strings.CutLast — enquanto o go test falha na etapa vet porque o go.mod ainda declara 1.26. Com uma toolchain 1.26 real, o arquivo nem sequer compilaria. A lição é procedural: nunca valide uma atualização apenas com go build; execute a suíte de testes completa, depois eleve a linha go. A suíte vet no go test também pegou um novo achado de printf: fmt.Errorf("...: %w", p) onde p é um *E e E implementa error agora gera aviso, porque envolver um ponteiro produz asserções de tipo e verificações errors.Is que falham. Esse modo de falha aparece no código de tratamento de erros exatamente como descrito nos padrões da arquitetura de tratamento de erros Go — verifique o tipo do operando, não apenas o verbo.
encoding/json: o motor v2 por baixo dos panos
A maior mudança interna: o encoding/json/v2 (experimental desde o Go 1.25 como GOEXPERIMENT=jsonv2) se formou, e o clássico pacote encoding/json agora é suportado pela implementação v2. Para chamadores v1, o comportamento é preservado — fatias nulas ainda codificam como null, chaves de mapa ainda são ordenadas — com uma diferença visível: o texto exato de algumas mensagens de erro mudou. Se testes ou monitoramento correspondem a strings de erro, espere diffs.
Três coisas ainda podem dar errado:
- Texto de erro. Quaisquer asserções
strings.Contains(err.Error(), ...)baseadas na redação antiga v1 precisam de atualização. - Caminhos sensíveis a desempenho podem mudar. A desserialização está mais rápida, mas a reescrita altera quais operações alocam; benchmarks marcados no CI mostrarão isso.
- Porta de saída de compatibilidade.
GOEXPERIMENT=nojsonv2restaura a implementação v1 original em tempo de compilação. É temporário — planeje removê-lo, não enviá-lo com ele.
Um efeito de segunda ordem para testes de ouro: o codificador do compress/flate mudou na mesma versão, então as saídas de archive/zip, compress/gzip, compress/zlib e image/png podem diferir byte a byte do Go 1.26, mesmo que decodifiquem identicamente. Regenere fixtures comprimidos em vez de fixar bytes antigos.
Migrando para a API v2 deliberadamente
A API v2 é um import separado (encoding/json/v2) com Marshal, Unmarshal e um sistema de opções. O caminho de migração suportado é fazer as chamadas v2 se comportarem como v1 primeiro, depois ajustar seletivamente:
import (
"encoding/json/v2"
"encoding/json/jsontext"
)
// Passo 1: comportamento idêntico a encoding/json — uma mudança segura e mecânica
out, err := jsonv2.Marshal(v, jsonv2.DefaultOptionsV1())
// Passo 2: optar por um padrão v2 de cada vez
out, err := jsonv2.Marshal(v,
jsonv2.DefaultOptionsV1(),
jsonv2.FormatNilSliceAsNull(false)) // fatias nulas agora codificam como []
Quando você remover DefaultOptionsV1, os padrões v2 se aplicam, e eles são mais rígidos e interoperáveis que v1:
| Comportamento | v1 (encoding/json) |
padrão v2 |
|---|---|---|
| UTF-8 inválido em strings | Substituído por U+FFFD | Erro |
| Nomes duplicados em um objeto | Permitido | Erro |
| Fatia nula / mapa nulo | null |
[] / {} |
| Correspondência de campos de struct | Fallback sem distinção de maiúsculas/minúsculas | Sensível a maiúsculas/minúsculas |
| Membros desconhecidos de objeto | Ignorados | Ignorados (RejectUnknownMembers para erro) |
| Ordenação de chaves de mapa | Sempre ordenado | Desordenado por padrão |
time.Duration |
Codificação de string embutida | Sem codificação padrão |
As chaves de mapa desordenadas são o que surpreende os testes de arquivo-ouro: o v2 pula a ordenação porque ordenar custa tempo. Quando você precisa de saída estável — fixtures, testes de snapshot, representações com hash — passe json.Deterministic(true), que restaura a saída ordenada para mapas e structs. Os modelos do artigo comparação de ORMs carregam todas as tags json; essas tags continuam funcionando no v2, mas a opção de tag format foi removida durante o experimento e inline foi renomeada para embed, então procure suas definições de modelos por ambos antes de migrar. Para necessidades de baixo nível — streaming, processamento token a token, manipulação de sintaxe personalizada — o pacote companheiro encoding/json/jsontext expõe uma máquina de estados Encoder/Decoder sobre texto JSON.
stdlib uuid: descartando github.com/google/uuid
O Go 1.27 adiciona uuid à biblioteca padrão, implementando a RFC 9562. A superfície da API cobre os casos comuns:
id := uuid.New() // atualmente equivalente a NewV4
v7 := uuid.NewV7() // ordenável por tempo, timestamp de 48 bits + aleatório
u, err := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
github.com/google/uuid é o segundo módulo Go mais dependido, então essa migração importa em escala. As duas APIs são próximas — os tipos são valores [16]byte comparáveis com == e usáveis como chaves de mapa, String(), MarshalText/UnmarshalText fazem ida e volta — e o projeto de origem está preparando shims go:fix inline para que o go fix possa reescrever chamadas de uuid.NewV4() para o pacote padrão e remover a dependência.
A decisão de adoção interessante é NewV7. UUIDs da versão 7 embutem um timestamp em milissegundos nos bits mais significativos, então os valores são ordenados na ordem de criação (exceto quando o relógio do sistema recua). Para chaves primárias de banco de dados, isso significa melhor localidade B-tree do que UUIDv4 aleatórios, e uma ordenação semelhante a created_at de graça no próprio identificador — a mesma propriedade que impulsiona a discussão sobre chaves ordenáveis por tempo em modelos de dados multi-tenant.
Mudanças na linguagem: métodos genéricos e afins
Métodos genéricos são o recurso de linguagem mais pedido da versão: uma declaração de método agora pode declarar seus próprios parâmetros de tipo.
// Go 1.27: math/rand/v2 ganhou um método genérico junto com a função
func (r *Rand) N[Int intType](n Int) Int
Duas restrições mantêm o sistema de tipos decidível: métodos de interface não podem declarar parâmetros de tipo, e métodos de interface não podem ser implementados por métodos genéricos. Juntos com eles, chaves de literais de struct agora podem ser qualquer seletor de campo válido (não apenas nomes de campos de nível superior), e a inferência de tipo de função se generaliza para todos os contextos onde uma função genérica é atribuída a uma variável de tipo de função correspondente. Nenhuma dessas coisas quebra código existente; elas removem contornos — funções auxiliares genéricas em nível de pacote e instanciações explícitas.
O go fix ganhou modernizadores para limpezas adjacentes: atomictypes, embedlit, slicesbackward e unsafefuncs. Uma cautela dos laboratórios de upgrade práticos: execute go fix -fix=atomictypes deliberadamente, em vez de em massa em bibliotecas, pois pode mudar os tipos de variáveis em nível de pacote que outros pacotes referenciam. O modernizador waitgroup foi renomeado para waitgroupgo; note que sync.WaitGroup.Go (de 1.25) já cobre a maioria do que ele detecta — veja os padrões de concorrência limitada para como ele se encaixa no desligamento de pools de workers.
Coisas que quebram silenciosamente
Estas são as mudanças que compilam bem e ainda custam um dia de release:
- Nomes de símbolos de literais de função. O compilador agora gera nomes mais simples, independentes de inlining, para closures, e pode mesclar literais de função idênticos. Testes que asseveram nomes de símbolos em tracebacks ou perfis falharão; código que compara ponteiros de código de função para igualdade é exposto em mais casos.
- Alinhamento do
gofmt. Uma correção na lógica de alinhamento de colunas (que variava anteriormente entre arquiteturas de CPU) reformata um pequeno número de arquivos já formatados. Espere diffs apenas de espaços em branco em um commitgofmt ./...após a atualização. go mod tidyreescrevego.mod. Para módulos emgo 1.27, otidyagora mescla blocosrequireduplicados em no máximo dois blocos (direto, indireto), preservando comentários. Diff seugo.mode resolva qualquer resíduo de conflito de mesclagem antes do commit de release, não depois.- Rótulos de traceback. Tracebacks para módulos em
go 1.27incluem rótulos de goroutines doruntime/pprofna linha de cabeçalho; desative comGODEBUG=tracebacklabels=0se os rótulos carregarem dados sensíveis. go tool tracese vincula a localhost.-http=:6060agora escuta apenas em localhost; passe-http=0.0.0.0:6060explicitamente quando precisar de acesso remoto (e tenha em mente que uma porta de profiling em um host compartilhado é um risco — veja o fluxo de trabalho de profiling de produção para a configuração reforçada).
Experimentos que valem a atenção
Dois pacotes experimentais foram enviados no 1.27, ambos ativados com GOEXPERIMENT=simd em tempo de compilação:
simd— uma API portátil, agnóstica ao tamanho do vetor (Int8s,Float32s, …) que mapeia para instruções de hardware onde disponível. Suporta AVX/AVX2/AVX-512 em amd64, NEON em arm64 e WebAssembly SIMD. A biblioteca padrão já usa este mecanismo internamente: a implementação de mapa Swiss Table do Go se beneficia disso.simd/archsimd— a camada específica da arquitetura para código que precisa conhecer a largura do vetor.
No lado da criptografia, o crypto/mldsa implementa o esquema de assinatura pós-quântica ML-DSA da FIPS 204 em três conjuntos de parâmetros (MLDSA44, MLDSA65, MLDSA87), integrado ao crypto/x509 e oferecido como esquemas de assinatura TLS 1.3. A troca de chaves pós-quântica híbrida agora pode ser forçada via Config.CurvePreferences, mesmo quando os fallbacks GODEBUG de tlsmlkem/tlssecpmlkem estão definidos. Nenhum deles é um item de migração hoje, mas ambos moldam a superfície de configuração TLS para a qual seus serviços crescerão.
Novas APIs que valem a adoção imediata
Algumas adições resolvem atritos do dia a dia e custam nada para adotar:
// strings.CutLast / bytes.CutLast — a dança LastIndex, substituída
before, after, found := strings.CutLast("a/b/c.tar.gz", ".")
// httptest.NewTestServer — rede falsa em memória, limpeza automática
srv := httptest.NewTestServer(t, mux) // sem porta real, sem defer srv.Close()
httptest.NewTestServer cria um httptest.Server suportado por uma rede em memória em vez de um listener TCP e registra seu próprio t.Cleanup. Seu verdadeiro retorno é o pareamento com testing/synctest: idas e voltas HTTP rodam em tempo sintético, tornando testes de tempo limite e reteste rápidos e determinísticos — a combinação é demonstrada no artigo testes de concorrência com synctest.
Lista de verificação de atualização
A ordem importa mais do que os itens; cada passo assume que o anterior está verde.
| Passo | Módulo pequeno | Serviço / monorepo |
|---|---|---|
| 1. Toolchain | go install golang.org/dl/go1.27@latest |
Fixar toolchain go1.27.0; alinhar imagens CI |
2. go.mod |
go mod edit -go=1.27 && go mod tidy |
Idem, mais resolver diffs de mesclagem de blocos require |
| 3. GODEBUG removidos | Grep //go:debug e godebug: |
Auditar também configurações e flags implantadas |
4. go build ./... |
Corrigir erros de compilação | Corrigir por módulo; bisectar falhas |
5. go test ./... |
Corrigir achados stdversion/printf |
Executar antes de elevar o go para pegar deriva de símbolos |
6. go fix ./... |
Revisar o diff | Um modernizador de cada vez |
| 7. Testes JSON/ouro | Regenerar fixtures | Verificar também fixtures baseados em flate (zip/gzip/png) |
8. GOEXPERIMENT=nojsonv2 |
Pular | Apenas temporário; abrir issues para o que o forçou |
Notas finais
As quebras do Go 1.27 se aglomeram ao redor de bordas que a maioria dos programas nunca toca — opt-outs de GODEBUG removidos, asserções de nomes de símbolos, fixtures de bytes comprimidos — enquanto seus ganhos chegam onde cada programa vive: JSON mais rápido, UUIDs padrão, métodos genéricos. Uma atualização limpa é principalmente disciplina: go build, depois go test, depois go fix, nessa ordem, com a linha go elevada antes da execução dos testes, não depois. A porta de saída nojsonv2 e a regra de aceitação no padrão final de asynctimerchan compram tempo, mas ambas estão programadas para desaparecer, então trate esta atualização como o prazo para as correções adiadas herdadas de 1.23–1.26.