Diseño de Esquemas de Protocol Buffers
Los esquemas de Protobuf son contratos vivos.
Busca en todas las páginas de la documentación
Los esquemas de Protobuf son contratos vivos.
Los números de campo, los tipos de cable y los límites del paquete deciden si puedes desplegar un nuevo servicio sin tumbar la mitad de la flota.
Los mensajes de Protobuf son estructuras de datos versionadas definidas en archivos .proto.
Cada campo tiene una etiqueta numérica permanente.
Los clientes y servidores compilados con meses de diferencia aún deben poder decodificar las cargas útiles del otro.
El diseño del esquema es diseño de API: los nombres importan para la legibilidad, pero los números importan para la compatibilidad.
Esta página cubre la disposición de mensajes, oneof, mapas, enums, importaciones y las reglas que Google documenta para una evolución segura.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
syntax = "proto3";
package inventory.v1;
option go_package = "example.com/inventory/api/inventory/v1;inventoryv1";
import "google/protobuf/timestamp.proto";
message Item {
string id = 1;
string sku = 2;
int32 quantity = 3;
google.protobuf.Timestamp updated_at = 4;
oneof status {
bool in_stock = 5;
string backorder_note = 6;
}
map<string, string> attributes = 7;
}Cuándo usar esto:
oneof en lugar de flags booleanos paralelos.map.Timestamp, Duration, Empty) en lugar de reinventarlos.syntax = "proto3";
package orders.v1;
option go_package = "example.com/orders/api/orders/v1;ordersv1";
import "google/protobuf/field_mask.proto";
enum OrderState {
ORDER_STATE_UNSPECIFIED = 0;
ORDER_STATE_PENDING = 1;
ORDER_STATE_SHIPPED = 2;
}
message Order {
string id = 1;
OrderState state = 2;
repeated LineItem items = 3;
}
message LineItem {
string product_id = 1;
int32 qty = 2;
}
message UpdateOrderRequest {
Order order = 1;
google.protobuf.FieldMask update_mask = 2;
}package main
import (
"fmt"
ordersv1 "example.com/orders/api/orders/v1"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
func main() {
req := &ordersv1.UpdateOrderRequest{
Order: &ordersv1.Order{
Id: "ord-1",
State: ordersv1.OrderState_ORDER_STATE_SHIPPED,
},
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}},
}
fmt.Println(req.GetUpdateMask().GetPaths())
}Lo que esto demuestra:
UNSPECIFIED - nunca uses cero como un estado de negocio significativo sin documentarlo.repeated se mapea a slices de Go; los campos ausentes se decodifican como nil/vacío, no como errores.FieldMask expresa actualizaciones parciales para RPCs de estilo PATCH.go_package mantiene el código Go generado en una ruta de importación estable.Protobuf codifica mensajes como triples de etiqueta-longitud-valor en el cable.
El número de campo selecciona la ranura; el tipo de cable le dice a los decodificadores cómo analizar los bytes.
Los decodificadores ignoran los campos desconocidos, que es la base de la compatibilidad hacia atrás.
Renombrar un campo en .proto no cambia el formato del cable; solo lo hacen el número y el tipo.
| Regla | Cambio seguro | Cambio que rompe la compatibilidad |
|---|---|---|
| Agregar campo opcional | Sí, con número nuevo | Reutilizar un número antiguo |
| Cambiar tipo de campo | No | int32 a string en la misma etiqueta |
| Renombrar campo | Sí (mismo número) | - |
| Eliminar campo | Reservar número + nombre | Reutilizar etiqueta eliminada |
Palabra clave optional | Presencia explícita en proto3 | - |
Usa reserved 3, 5; y reserved "legacy_field"; después de las eliminaciones.
oneof, mapas y mensajes anidadosoneof impone la exclusión mutua; solo se establece un miembro.
En Go, los wrappers generados exponen accesores estilo GetInStock() y un tipo de interfaz para el caso activo.
Los campos map<K,V> se convierten en mapas de Go; las claves no pueden ser mensajes ni flotantes.
Mantén los valores de mapa como cadenas o números simples cuando sea posible para la consistencia entre lenguajes.
Anida mensajes para mayor claridad, pero aplana cuando la misma estructura se repite en RPCs; los tipos compartidos pertenecen a common.proto.
// Prefiere constructores para invariantes requeridos
order := &ordersv1.Order{
Id: id,
State: ordersv1.OrderState_ORDER_STATE_PENDING,
}
// Clona antes de mutar protobufs compartidos pasados entre goroutines
clone := proto.Clone(order).(*ordersv1.Order)proto.Marshal / proto.Unmarshal funcionan sin gRPC para cargas útiles de eventos.buf lint o prototool en CI para forzar el estilo y la detección de cambios que rompen la compatibilidad.int32 a enum - Los tipos de cable pueden coincidir pero las semánticas divergen. Solución: agrega un nuevo número de campo para el enum.PENDING colisionan. Solución: mantén UNSPECIFIED = 0 y comienza los valores de negocio en 1.oneof enorme con muchos brazos - El código Go generado se expande. Solución: divide las variantes en mensajes anidados o RPCs separadas..proto. Solución: genera OpenAPI o usa verificaciones de buf contra main para cambios que rompen la compatibilidad.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Mensajes protobuf simples | Servicios gRPC, eventos | Necesitas blobs sin tipo y sin esquema |
google.protobuf.Struct | Atributos verdaderamente dinámicos | Rutas críticas de rendimiento de alta frecuencia |
| FlatBuffers / Cap'n Proto | Cargas de trabajo de solo lectura de cero copia | El equipo ya está estandarizado en gRPC+proto |
| JSON Schema + OpenAPI | APIs públicas solo HTTP | Quieres eficiencia binaria y generación de código |
| Avro con registro de esquema | Pipelines centrados en Kafka | gRPC simple punto a punto |
La codificación en el cable usa solo números.
Renombrar es seguro; reenumerar no lo es.
Proto3 es el predeterminado para nuevo trabajo gRPC.
Proto2 persiste en APIs antiguas de Google; evita mezclar en un solo servicio.
Rastrea la presencia explícita por separado de los valores predeterminados cero.
Útil para semántica PATCH y para distinguir "no establecido" de "cadena vacía".
Extrae mensajes comunes a common/v1/types.proto e impórtalos.
Mantén los nombres de paquetes versionados (v1, v2).
Usa google.protobuf.Timestamp en protos.
Convierte en los límites con timestamppb.New(t) en Go.
Mantén las cargas útiles de RPC por debajo de unos pocos MB; usa streaming o almacenamiento de objetos para volúmenes grandes de binarios.
Mensajes enormes perjudican la latencia y los búferes del proxy.
buf breaking, prototool break check, o CI fijada contra una referencia git base.
Los gateways mapean enums a cadenas en JSON.
Los nombres de enum de Proto deben ser estables ya que aparecen externamente.
Usa comentarios // encima de los campos; algunos generadores emiten comentarios estilo godoc.
Para APIs públicas, refleja la documentación en el README de tu repositorio de esquemas.
Proto3 omite los valores predeterminados en la codificación binaria.
No dependas de recibir valores cero para campos primitivos no establecidos a menos que uses optional.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (Predeterminado GC de Green Tea, go fix modernizers - verificar parche en la compilación), chi (última versión - verificar en la compilación), gin (última versión - verificar en la compilación), echo (última versión - verificar en la compilación), google.golang.org/grpc (última versión - verificar en la compilación), sigs.k8s.io/controller-runtime (última versión - verificar en la compilación), kubebuilder (última versión - verificar en la compilación), tinygo (última versión - verificar objetivos de placa en la compilación), wazero (última versión - verificar en la compilación) y golangci-lint (última versión - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026