Actualizar a Go 1.27: Qué se rompe y qué adoptar primero
«json/v2 por debajo del capó, uuid de la biblioteca estándar y trampas de GODEBUG».
Go 1.27 se lanzó en agosto de 2026, y para la mayoría de los módulos, la actualización consiste en una sola línea en go.mod seguida de un pase de go fix; sin embargo, un puñado de cambios afectan silenciosamente el código. Aquí se detalla lo que realmente se rompe y qué adoptar primero.
Los cambios principales son estructurales: encoding/json ahora está respaldado internamente por la nueva implementación json/v2, el paquete uuid se incorporó a la biblioteca estándar y los métodos finalmente pueden declarar sus propios parámetros de tipo. Ninguno de estos cambios obliga a reescribir el código, y el equipo de Go mantuvo la promesa de compatibilidad habitual: casi todo se compila y ejecuta sin cambios.

Sin embargo, no todo es tranquilo. Varios ajustes de GODEBUG ahora hacen fallar la compilación si se mantienen en sus valores antiguos, compress/flate produce bytes diferentes, go test impone una nueva verificación de stdversion y los nombres de los símbolos generados por el compilador han cambiado. Si necesita un repaso de sintaxis sobre las partes que no se han modificado, la hoja de referencia de Go sigue siendo válida; esta guía cubre el resto, con comandos y correcciones para cada elemento.
El camino de actualización en un solo diagrama
El equipo de Go recomienda un orden de compilación primero y corrección después. go fix reescribe el código para nuevas API e idioms, pero solo una compilación limpia le indica si alguno de los cambios a nivel de ejecución le afecta.
Comience con la propia cadena de herramientas. Si gestiona las cadenas de herramientas mediante el comando go, fije la versión en go.mod o instale explícitamente:
go install golang.org/dl/go1.27@latest
go1.27 download
go1.27 version
Alternativamente, establezca GOTOOLCHAIN=go1.27.0 en el entorno, o agregue toolchain go1.27.0 a go.mod y deje que el comando go lo descargue. El compañero enfocado en módulos sobre estructura de espacios de trabajo de Go explica cómo interactúan las líneas go y toolchain entre múltiples módulos; la versión corta para un solo módulo es la siguiente.
Paso 1: aumentar la línea go y limpiar los ajustes GODEBUG eliminados
go mod edit -go=1.27
go mod tidy
go build ./...
Elevar la directiva go activa las nuevas funciones del lenguaje (métodos genéricos, claves de campos promovidos en literales de estructura, inferencia de tipo de función más amplia) y hace que el comando go sea estricto con los ajustes de GODEBUG cuyo soporte se ha eliminado. Go 1.27 eliminó permanentemente estos ajustes:
asynctimerchan: los canales de temporizador y tictaque son ahora siempre sin búfergotypesaliastlsunsafeekm,tlsrsakex,tls3des,tls10serverx509keypairleaf
La nueva regla: el comando go aún acepta estos ajustes en go.mod (entradas godebug) o en el código fuente (comentarios //go:debug) si están configurados en el valor predeterminado final que existía antes de la eliminación. Un valor antiguo (el que deshabilitaba un cambio de comportamiento) hace fallar la compilación:
//go:debug asynctimerchan=0 // error de compilación: valor antiguo de un ajuste eliminado
//go:debug asynctimerchan=1 // aceptado, pero elimine la línea en su lugar
Si su compilación falla aquí, el código dependía de canales de temporizador con búfer previos a Go 1.23. La corrección está en el código, no en la configuración: revise el uso de Timer.Stop y Timer.Reset frente a los patrones de contexto, cancelación y temporización que se basan en los canales síncronos. La misma búsqueda se aplica a los ajustes relacionados con TLS: cualquier cosa que fijara tls3des=1 o tls10server=1 estaba manteniendo vivas las suites de cifrado antiguas, y Go 1.27 se niega a compilarlas.
Paso 2: ejecutar go test — la trampa de stdversion
go build y go test ya no están de acuerdo. El go test de Go 1.27 ejecuta la verificación vet de stdversion de forma predeterminada, que informa sobre símbolos de la biblioteca estándar que son demasiado nuevos para la versión de go en vigor en el archivo que se referencia.
// go.mod todavía dice "go 1.26"
func Base(p string) string {
_, file, ok := strings.CutLast(p, "/") // agregado en Go 1.27
if !ok {
return p
}
return file
}
Con una cadena de herramientas 1.27, go build tiene éxito (el compilador tiene strings.CutLast), mientras que go test falla en el paso vet porque go.mod aún declara 1.26. Con una cadena de herramientas 1.26 real, el archivo no se compilaría en absoluto. La lección es procedural: nunca valide una actualización solo con go build; ejecute la suite completa de pruebas y luego aumente la línea go. La suite vet en go test también detectó un nuevo hallazgo de printf: fmt.Errorf("...: %w", p) donde p es un *E y E implementa error ahora emite una advertencia, porque envolver un puntero produce aserciones de tipo y verificaciones de errors.Is que fallan. Ese modo de fallo aparece en el código de manejo de errores exactamente como se describe en los patrones de la arquitectura de manejo de errores de Go: revise el tipo del operando, no solo el verbo.
encoding/json: el motor v2 por debajo
El mayor cambio interno: encoding/json/v2 (experimental desde Go 1.25 como GOEXPERIMENT=jsonv2) se graduó, y el clásico paquete encoding/json ahora está respaldado por la implementación v2. Para los llamadores v1, el comportamiento se preserva (las rebanadas nil aún se codifican como null, las claves de los mapas siguen ordenándose), con una diferencia visible: el texto exacto de algunos mensajes de error cambió. Si las pruebas o la supervisión coinciden con cadenas de error, espere diferencias.
Tres cosas aún pueden salir mal:
- Texto de error. Cualquier aserción de
strings.Contains(err.Error(), ...)basada en la redacción antigua v1 necesita actualizarse. - Las rutas sensibles al rendimiento pueden cambiar. El desempaquetado es más rápido, pero la reescritura cambia qué operaciones asignan memoria; los benchmarks marcados en CI lo mostrarán.
- Salida de emergencia de compatibilidad.
GOEXPERIMENT=nojsonv2restaura la implementación v1 original en tiempo de compilación. Es temporal: planee eliminarlo, no lanzar el producto con él.
Un efecto de segundo orden para las pruebas doradas: el codificador de compress/flate cambió en la misma liberación, por lo que las salidas de archive/zip, compress/gzip, compress/zlib e image/png pueden diferir byte a byte de Go 1.26, aunque se decodifiquen de manera idéntica. Regenerar los fijos comprimidos en lugar de fijar bytes antiguos.
Migrar a la API v2 deliberadamente
La API v2 es una importación separada (encoding/json/v2) con Marshal, Unmarshal y un sistema de opciones. La ruta de migración soportada consiste en hacer que las llamadas v2 se comporten como v1 primero, y luego ajustarse selectivamente:
import (
"encoding/json/v2"
"encoding/json/jsontext"
)
// Paso 1: comportamiento idéntico a encoding/json — un cambio seguro y mecánico
out, err := jsonv2.Marshal(v, jsonv2.DefaultOptionsV1())
// Paso 2: activar un valor predeterminado de v2 a la vez
out, err := jsonv2.Marshal(v,
jsonv2.DefaultOptionsV1(),
jsonv2.FormatNilSliceAsNull(false)) // las rebanadas nil ahora se codifican como []
Una vez que elimine DefaultOptionsV1, se aplican los valores predeterminados de v2, que son más estrictos e interoperables que los de v1:
| Comportamiento | v1 (encoding/json) |
Predeterminado de v2 |
|---|---|---|
| UTF-8 no válido en cadenas | Reemplazado con U+FFFD | Error |
| Nombres duplicados en un objeto | Permitido | Error |
| Rebanada nil / mapa nil | null |
[] / {} |
| Correspondencia de campos de estructura | Respuesta insensible a mayúsculas/minúsculas | Sensible a mayúsculas/minúsculas |
| Miembros de objeto desconocidos | Ignorado | Ignorado (RejectUnknownMembers para error) |
| Orden de claves de mapa | Siempre ordenado | No ordenado por defecto |
time.Duration |
Codificación de cadena integrada | Sin codificación predeterminada |
Las claves de mapa no ordenadas son las que sorprenden a las pruebas de archivos dorados: v2 omite el orden porque ordenar cuesta tiempo. Cuando necesite una salida estable (fijos, pruebas de instantánea, representaciones con hash), pase json.Deterministic(true), que restaura la salida ordenada para mapas y estructuras. Los modelos del artículo de comparación de ORMs llevan etiquetas json; esas etiquetas siguen funcionando en v2, pero la opción de etiqueta format se eliminó durante el experimento y inline se renombró a embed, así que busque con grep ambas en sus definiciones de modelo antes de migrar. Para necesidades de bajo nivel (flujo continuo, procesamiento por token, manejo de sintaxis personalizado), el paquete compañero encoding/json/jsontext expone una máquina de estados Encoder/Decoder sobre el texto JSON.
stdlib uuid: dejando atrás github.com/google/uuid
Go 1.27 agrega uuid a la biblioteca estándar, implementando RFC 9562. La superficie de la API cubre los casos comunes:
id := uuid.New() // actualmente equivalente a NewV4
v7 := uuid.NewV7() // ordenable por tiempo, marca de tiempo de 48 bits + aleatorio
u, err := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
github.com/google/uuid es el segundo módulo de Go más dependido, por lo que esta migración importa a gran escala. Las dos API son cercanas (los tipos son valores [16]byte comparables con == y usables como claves de mapa, String(), MarshalText/UnmarshalText hacen recorrido de ida y vuelta), y el proyecto principal está preparando simios go:fix inline para que go fix pueda reescribir las llamadas a uuid.NewV4() al paquete estándar y eliminar la dependencia.
La decisión de adopción interesante es NewV7. Los UUID de la versión 7 incrustan una marca de tiempo de milisegundos en los bits más significativos, por lo que los valores se ordenan en el orden de creación (excepto cuando el reloj del sistema retrocede). Para las claves primarias de base de datos, eso significa mejor localidad de árbol B que los UUIDv4 aleatorios, y orden similar a created_at de gratis en el identificador mismo (la misma propiedad que impulsa la discusión de claves ordenables por tiempo en modelos de datos multiinquilino).
Cambios en el lenguaje: métodos genéricos y amigos
Los métodos genéricos son la función del lenguaje más solicitada de esta liberación: una declaración de método ahora puede declarar sus propios parámetros de tipo.
// Go 1.27: math/rand/v2 ganó un método genérico junto con la función
func (r *Rand) N[Int intType](n Int) Int
Dos restricciones mantienen el sistema de tipos decidible: los métodos de interfaz no pueden declarar parámetros de tipo, y los métodos de interfaz no pueden ser implementados por métodos genéricos. Junto con ellos, las claves de literales de estructura ahora pueden ser cualquier selector de campo válido (no solo nombres de campo de nivel superior), y la inferencia de tipo de función se generaliza a todos los contextos donde una función genérica se asigna a una variable de tipo de función coincidente. Ninguno de estos rompe el código existente; eliminan soluciones temporales (funciones auxiliares genéricas a nivel de paquete e instanciaciones explícitas).
go fix creció con modernizadores para limpiezas adyacentes: atomictypes, embedlit, slicesbackward y unsafefuncs. Una precaución de los laboratorios de actualización práctica: ejecute go fix -fix=atomictypes deliberadamente y no en bloque en bibliotecas, ya que puede cambiar los tipos de variables a nivel de paquete que otros paquetes referencian. El modernizador waitgroup se renombró a waitgroupgo; tenga en cuenta que sync.WaitGroup.Go (de 1.25) ya cubre la mayoría de lo que detecta (vea patrones de concurrencia acotada) para ver cómo se integra en el apagado de pools de trabajadores.
Cosas que se rompen silenciosamente
Estos son los cambios que se compilan bien pero aún le cuestan un día de liberación:
- Nombres de símbolos de literales de función. El compilador ahora genera nombres más simples e independientes de la inlinación para cierres, y puede fusionar literales de función idénticos. Las pruebas que asercen sobre nombres de símbolos en trazas de pila o perfiles fallarán; el código que compara punteros de código de función para igualdad se expone en más casos.
- Alineación de
gofmt. Una corrección a la lógica de alineación de columnas (previamente variaba entre arquitecturas de CPU) refluja una pequeña cantidad de archivos ya formateados. Espere diferencias solo de espacios en blanco en un commit degofmt ./...después de la actualización. go mod tidyreescribego.mod. Para módulos engo 1.27,tidyahora fusiona bloquesrequireduplicados en un máximo de dos bloques (directo, indirecto), preservando comentarios. Diferencie sugo.mody resuelva cualquier residuo de conflicto de fusión antes del commit de liberación, no después.- Etiquetas de traza de pila. Las trazas de pila para módulos en
go 1.27incluyen etiquetas de gorroutine deruntime/pprofen la línea de cabecera; deshabilítelas conGODEBUG=tracebacklabels=0si las etiquetas contienen datos sensibles. go tool tracese une a localhost.-http=:6060ahora escucha solo en localhost; pase-http=0.0.0.0:6060explícitamente cuando necesite acceso remoto (y tenga en cuenta que un puerto de perfilización en un host compartido es un riesgo (vea el flujo de trabajo de perfilización de producción para la configuración reforzada)).
Experimentos dignos de atención
Dos paquetes experimentales se lanzaron en 1.27, ambos habilitados con GOEXPERIMENT=simd en tiempo de compilación:
simd— una API portátil e agnóstica al tamaño de vector (Int8s,Float32s, …) que se mapea a instrucciones de hardware cuando está disponible. Soporta AVX/AVX2/AVX-512 en amd64, NEON en arm64 y WebAssembly SIMD. La biblioteca estándar ya usa este mecanismo internamente: la implementación de mapas Swiss Table de Go se beneficia de él.simd/archsimd— la capa específica de arquitectura para el código que necesita conocer el ancho del vector.
En el lado criptográfico, crypto/mldsa implementa el esquema de firma ML-DSA postcuántico de FIPS 204 en tres conjuntos de parámetros (MLDSA44, MLDSA65, MLDSA87), integrado en crypto/x509 y ofrecido como esquemas de firma TLS 1.3. El intercambio de claves postcuántico híbrido ahora puede forzarse mediante Config.CurvePreferences incluso cuando se establecen los ajustes de retroceso GODEBUG de tlsmlkem/tlssecpmlkem. Ninguno es un elemento de migración hoy en día, pero ambos dan forma a la superficie de configuración TLS en la que crecerán sus servicios.
Nuevas API dignas de adopción inmediata
Algunas adiciones resuelven la fricción cotidiana y no cuestan nada de adoptar:
// strings.CutLast / bytes.CutLast — la danza LastIndex, reemplazada
before, after, found := strings.CutLast("a/b/c.tar.gz", ".")
// httptest.NewTestServer — red falsa en memoria, auto-limpieza
srv := httptest.NewTestServer(t, mux) // sin puerto real, sin defer srv.Close()
httptest.NewTestServer crea un httptest.Server respaldado por una red en memoria en lugar de un listener TCP y registra su propia t.Cleanup. Su verdadero beneficio es emparejarse con testing/synctest: los viajes de ida y vuelta HTTP se ejecutan en tiempo sintético, haciendo que las pruebas de temporización y reintento sean rápidas y deterministas (la combinación se demuestra en el artículo de pruebas de concurrencia con synctest).
Lista de verificación de actualización
El orden importa más que los elementos; cada paso asume que el anterior es verde.
| Paso | Módulo pequeño | Servicio / monorepo |
|---|---|---|
| 1. Cadena de herramientas | go install golang.org/dl/go1.27@latest |
Fijar toolchain go1.27.0; alinear imágenes de CI |
2. go.mod |
go mod edit -go=1.27 && go mod tidy |
Lo mismo, más resolver diferencias de fusión de bloques require |
| 3. GODEBUG eliminado | Buscar //go:debug y godebug: |
También auditar configuraciones y banderas desplegadas |
4. go build ./... |
Corregir errores de compilación | Corregir por módulo; bisección de fallos |
5. go test ./... |
Corregir hallazgos de stdversion/printf |
Ejecutar antes de elevar go para captar deriva de símbolos |
6. go fix ./... |
Revisar la diferencia | Un modernizador a la vez |
| 7. Pruebas JSON/doradas | Regenerar fijos | Revisar también fijos basados en flate (zip/gzip/png) |
8. GOEXPERIMENT=nojsonv2 |
Omitir | Solo temporal; archivar problemas para lo que lo forzó |
Notas finales
Los rompes de Go 1.27 se agrupan alrededor de bordes que la mayoría de los programas nunca tocan (opciones de salida de GODEBUG eliminadas, aserciones de nombres de símbolos, fijos de bytes comprimidos), mientras que sus ganancias caen donde vive cada programa: JSON más rápido, UUID estándar, métodos genéricos. Una actualización limpia es mayoritariamente disciplina: go build, luego go test, luego go fix, en ese orden, con la línea go elevada antes de la ejecución de pruebas y no después. La salida de emergencia nojsonv2 y la regla de aceptación en valor predeterminado final para asynctimerchan compran tiempo, pero ambas están programadas para desaparecer, así que trate esta actualización como la fecha límite para las correcciones diferidas que hereda de 1.23–1.26.