INTERACTIVE TOUR
Go 1.27 em um mapa
Go 1.27 foi lançado em 19 de agosto de 2026. Este tour é seletivo: ele prioriza mudanças que alteram decisões práticas de código, ferramentas, runtime e biblioteca padrão.
Escolha um objetivo antes de avançar: simplificar uma API genérica, planejar uma migração de JSON, melhorar um diagnóstico concorrente ou revisar seu processo de upgrade. Você executará exemplos ao longo do caminho e voltará a esse objetivo no checklist.
Use o anúncio oficial como resumo e as notas da versão para limites completos.
LINGUAGEM
Métodos genéricos
Métodos genéricos
Um método agora pode declarar seus próprios parâmetros de tipo. Stream[T] fornece T por meio do receiver, e Map[U] declara U para a transformação.
Métodos de interfaces ainda não podem declarar parâmetros de tipo. Use o recurso quando o receiver oferece contexto real. O stream possui identidade e valores; ele não funciona somente como um namespace.
A regra e seus limites estão nas mudanças da linguagem.
EXPERIMENTE
Execute um método genérico
Preveja o resultado e execute. O compilador deduz T como int e U como string na chamada do método.
A saída é releases [go-42]. A relação entre os valores e a função impede uma transformação com tipos incompatíveis.
DESAFIO
Escreva um método genérico
Escreva um método genérico
Complete Stream[T].Map[U]. O método transforma os valores, preserva o nome e devolve um Stream[U]. Não use reflection nem type switch.
Dica 1
O receiver Stream[T] já fornece T; declare U depois do nome do método.
Dica 2
Transforme cada valor, preserve name e devolva um Stream[U] com a mesma ordem.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
type Stream[T any] struct {
name string
values []T
}
func (stream Stream[T]) Map[U any](transform func(T) U) Stream[U] {
values := make([]U, len(stream.values))
for i, value := range stream.values {
values[i] = transform(value)
}
return Stream[U]{name: stream.name, values: values}
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Want string `json:"want"`
Got string `json:"got"`
}
func emit(result caseResult) {
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
func main() {
releases := Stream[int]{name: "releases", values: []int{21}}
labels := releases.Map(func(value int) string { return fmt.Sprintf("go-%d", value*2) })
wantLabels := []string{"go-42"}
emit(caseResult{
Case: 1,
Pass: labels.name == "releases" && reflect.DeepEqual(labels.values, wantLabels),
Want: "releases:[go-42]",
Got: fmt.Sprintf("%s:%v", labels.name, labels.values),
})
words := Stream[string]{name: "words", values: []string{"gopher", "go"}}
lengths := words.Map(func(value string) int { return len(value) })
wantLengths := []int{6, 2}
emit(caseResult{
Case: 2,
Pass: lengths.name == "words" && reflect.DeepEqual(lengths.values, wantLengths),
Want: "words:[6 2]",
Got: fmt.Sprintf("%s:%v", lengths.name, lengths.values),
})
}
LINGUAGEM
Inferência em mais contextos
Inferência em mais contextos
Go 1.27 usa o tipo esperado em todos os contextos de atribuição compatíveis. No literal []IntFormatter{Format}, a assinatura do elemento informa que T deve ser int.
A mesma ideia vale para conversões para tipos de função e envios por channel. O contexto ainda precisa ser específico: uma atribuição para any não fornece a relação necessária.
Veja os exemplos oficiais nas notas de linguagem.
DESAFIO
Use a inferência no contexto
Use inferência no literal
Implemente BuildFormatters com um literal []IntFormatter. Coloque Format diretamente no literal, sem Format[int].
Dica 1
O tipo esperado dos elementos é IntFormatter.
Dica 2
Coloque Format diretamente no literal, sem escrever Format[int].
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
import "fmt"
func Format[T any](value T) string {
return fmt.Sprint(value)
}
type IntFormatter func(int) string
func BuildFormatters() []IntFormatter {
return []IntFormatter{Format}
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Want string `json:"want"`
Got string `json:"got"`
}
func emit(result caseResult) {
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
func main() {
formatters := BuildFormatters()
emit(caseResult{Case: 1, Pass: len(formatters) == 1, Want: "1 formatter", Got: fmt.Sprintf("%d formatter(s)", len(formatters))})
if len(formatters) == 0 {
emit(caseResult{Case: 2, Pass: false, Want: "127", Got: "sem formatter"})
return
}
got := formatters[0](127)
emit(caseResult{Case: 2, Pass: got == "127", Want: "127", Got: got})
}
LINGUAGEM
Seletores promovidos em literals
Seletores promovidos em literals
Uma chave de literal pode usar um seletor de campo válido. Assim, RequestID inicializa diretamente o campo promovido por Metadata.
A forma aninhada continua válida. Prefira a chave promovida quando existe um único caminho claro. Se dois campos promovidos tiverem o mesmo nome, o seletor fica ambíguo e o compilador rejeita o literal.
A nota oficial da linguagem define a regra.
EXPERIMENTE
Execute um literal promovido
Execute o literal e confirme que o valor chegou ao campo incorporado sem escrever Metadata: Metadata{...}.
A saída é req-42 true. O acesso posterior ao campo não muda.
BIBLIOTECA PADRÃO
encoding/json/v2
encoding/json/v2
O novo pacote oferece APIs de alto nível com opções variádicas e defaults mais estritos. Ele rejeita UTF-8 inválido e nomes duplicados. Por padrão, slices e maps nil viram coleções vazias, não null.
encoding/json continua suportado e preserva a semântica pública de v1. Você não precisa migrar somente para adotar Go 1.27.
Compare as notas de JSON v2 com a documentação do pacote.
EXPERIMENTE
Veja o default estrito
Execute duas entradas. A primeira é válida. A segunda repete port e deve produzir um erro.
O erro do segundo caso é parte do contrato estrito. Preserve esse erro para a borda da aplicação.
DECISÃO DE PRODUÇÃO
Migre para JSON v2 sem quebrar contratos
Migre para JSON v2 sem quebrar contratos
Identifique as mudanças observáveis antes de trocar o import: campos desconhecidos, slices nil, nomes duplicados e UTF-8 inválido. Proteja os contratos atuais com testes. Depois migre uma fronteira e compare entradas, saídas e erros.
Se consumidores distinguem null de [], não faça uma troca ampla sem testes. Em uma API nova, os defaults estritos podem ser a melhor base. O guia oficial de migração apresenta opções de compatibilidade e comparação paralela.
DESAFIO
Rejeite nomes JSON duplicados
Rejeite JSON ambíguo
Implemente DecodeConfig com encoding/json/v2. Preserve o erro para que nomes duplicados sejam rejeitados.
Dica 1
Use encoding/json/v2, não o alias antigo.
Dica 2
Devolva a configuração e o erro produzido por json.Unmarshal.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
import json "encoding/json/v2"
type Config struct {
Port int `json:"port"`
}
func DecodeConfig(data []byte) (Config, error) {
var config Config
err := json.Unmarshal(data, &config)
return config, err
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Want string `json:"want"`
Got string `json:"got"`
}
func emit(result caseResult) {
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
func main() {
config, err := DecodeConfig([]byte(`{"port":8080}`))
emit(caseResult{Case: 1, Pass: err == nil && config.Port == 8080, Want: "8080,nil", Got: fmt.Sprintf("%d,%v", config.Port, err)})
_, err = DecodeConfig([]byte(`{"port":8080,"port":9090}`))
emit(caseResult{Case: 2, Pass: err != nil, Want: "erro", Got: fmt.Sprint(err)})
}
BIBLIOTECA PADRÃO
UUID na biblioteca padrão
UUID na biblioteca padrão
O pacote uuid gera e analisa identificadores da RFC 9562. uuid.New escolhe um algoritmo adequado para a maioria dos usos e, nesta versão, equivale a UUID v4.
O tipo UUID é um array de 16 bytes e pode ser comparado com ==. O pacote também oferece NewV4, NewV7, Parse, MustParse, Nil e Max.
Consulte a documentação oficial de uuid.
EXPERIMENTE
Gere e valide um UUID
Gere um UUID, converta para texto e faça o caminho de volta. Os três resultados devem ser verdadeiros.
Mantenha o tipo uuid.UUID dentro do domínio. Converta para texto somente nas bordas que exigem essa forma.
DESAFIO
Crie uma resposta com UUID
Gere a resposta da API
Crie um UUID com uuid.New e inicialize o campo promovido RequestID diretamente no literal de APIResponse.
Dica 1
uuid.New cria um identificador adequado para a maioria dos usos.
Dica 2
Go 1.27 permite usar RequestID diretamente como chave do literal de APIResponse.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
import "uuid"
type Metadata struct {
RequestID uuid.UUID `json:"request_id"`
}
type APIResponse struct {
OK bool `json:"ok"`
Name string `json:"name"`
Metadata
}
func NewResponse(name string) APIResponse {
return APIResponse{OK: true, Name: name, RequestID: uuid.New()}
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
"uuid"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Want string `json:"want"`
Got string `json:"got"`
}
func emit(result caseResult) {
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
func main() {
response := NewResponse("gopher")
emit(caseResult{Case: 1, Pass: response.OK && response.Name == "gopher", Want: "true,gopher", Got: fmt.Sprintf("%t,%s", response.OK, response.Name)})
idText := response.RequestID.String()
parsed, err := uuid.Parse(idText)
emit(caseResult{Case: 2, Pass: err == nil && response.RequestID != uuid.Nil() && parsed == response.RequestID, Want: "UUID válido e não zero", Got: idText})
}
RUNTIME
Confirme o perfil goroutineleak
O perfil goroutineleak deixou a fase experimental. Execute o programa para confirmar que o runtime expõe o perfil por nome.
O perfil encontra uma classe de goroutines bloqueadas que o runtime consegue provar como inalcançáveis por ações úteis. Ele não prova a ausência de todos os vazamentos. Leia os limites nas notas do runtime.
GO TOOL
Ferramentas que reduzem trabalho manual
Ferramentas que reduzem trabalho manual
go doc package@version consulta uma versão específica. go doc -ex lista exemplos executáveis. go fix ganha modernizadores para tipos atômicos, literals com embedding, buscas reversas em slices e funções de unsafe.
Em módulos com go 1.27, go mod tidy consolida blocos require. go test executa o check stdversion do vet por padrão. Revise os diffs e trate cada comando como evidência, não como aprovação automática.
Veja as notas das ferramentas.
CÓDIGO ANOTADO
Uma resposta com as novidades
Este programa integra métodos genéricos, inferência, UUID e JSON v2 em uma resposta de API pequena.
package main
import (
json "encoding/json/v2"
"fmt"
"uuid"
)
type Stream[T any] struct {
name string
values []T
}
func (stream Stream[T]) Map[U any](fn func(T) U) Stream[U] {
values := make([]U, len(stream.values))
for i, value := range stream.values {
values[i] = fn(value)
}
return Stream[U]{name: stream.name, values: values}
}
func Format[T any](value T) string { return fmt.Sprint(value) }
type IntFormatter func(int) string
type APIResponse struct {
OK bool `json:"ok"`
Name string `json:"name"`
RequestID uuid.UUID `json:"request_id"`
}
func main() {
formatters := []IntFormatter{Format}
names := Stream[int]{name: "release", values: []int{127}}.Map(formatters[0])
response := APIResponse{OK: true, Name: "go-" + names.values[0], RequestID: uuid.New()}
body, err := json.Marshal(response)
fmt.Println("json válido:", err == nil)
fmt.Println(string(body))
} EXPERIMENTE
Execute o fluxo integrado
Execute a integração. Depois confirme que o JSON é válido e que o nome voltou como go-127.
Esse caminho une os recursos sem esconder o contrato: tipos na transformação, UUID no domínio e JSON na borda.
GALERIA SELETIVA
Performance e outras mudanças
Performance e outras mudanças
Alocações menores que 80 bytes podem custar até 30% menos em casos beneficiados. O ganho geral esperado em programas com muitas alocações fica perto de 1%, e o binário cresce cerca de 60 KB. Meça seu workload.
A versão também inclui ML-DSA em crypto/mldsa, Unicode 17, mudanças em HTTP, remoções permanentes de settings GODEBUG e macOS 13 como mínimo. SIMD experimental exige GOEXPERIMENT=simd; não trate simd/archsimd como API estável.
Consulte as notas completas.
PRÓXIMO PASSO
Seu checklist de upgrade
Checklist de upgrade
Primeiro mude a toolchain. Depois execute testes, vet, builds e exemplos. Revise contratos JSON, settings GODEBUG, plataformas suportadas e diffs de go fix e go mod tidy.
Para métodos genéricos, confirme que o receiver tem responsabilidade real. Para performance, compare benchmarks do seu workload. Para recursos experimentais, isole o uso e registre o flag.
Um upgrade confiável produz um diff explicável. Volte ao objetivo que você escolheu no início e transforme-o em uma verificação concreta.
TOUR CONCLUÍDO
O que você leva do tour
- Go 1.27 adiciona métodos genéricos, inferência ampliada e seletores promovidos em literals.
- JSON v2 oferece defaults mais estritos, mas a API v1 continua suportada.
- O pacote
uuidentra na biblioteca padrão. - O perfil
goroutineleakfica disponível sem experimento. - Ferramentas, alocação, HTTP, crypto, Unicode e plataformas recebem mudanças importantes.
- SIMD continua experimental, e este tour não substitui as notas completas.
Todo o código executável do curso é validado com Go 1.27. Também revisamos as aulas afetadas pela nova versão.
Fontes: anúncio oficial, notas da versão, migração JSON v2 e pacote uuid.