A Morte do JSON no Backend: Por que migrei toda minha stack para gRPC e Protobuf

Este post foi originalmente escrito em inglês. A tradução pode não refletir 100% das ideias originais do autor.

Vou começar este post com uma confissão que pode me render alguns inimigos: Eu odeio JSON.

Não é um ódio irracional, daqueles que aparecem do nada. É um ódio construído, tijolo por tijolo, ao longo de anos debugando payloads malformados, campos que deveriam ser números mas chegaram como strings, e aquele clássico null onde você esperava um array vazio. JSON é o equivalente digital de uma conversa telefônica com sua avó: você acha que entendeu o que ela disse, mas quando você chega lá, o bolo de cenoura não tinha aquela cobertura de chocolate esperada (brasileiros vão entender).

Dor!

Por anos, aceitei essa tortura como parte do contrato social de ser um desenvolvedor backend. “É o padrão”, diziam. “Todo mundo usa”, repetiam. E eu, como um bom carneirinho, continuei serializando e desserializando texto como se estivéssemos em 2005 e a banda larga fosse infinita.

Até que eu cansei.

O Problema: Texto é Caro

Vamos fazer um exercício mental. Imagine que você precisa enviar o número 42 do seu backend em Go para seu app em Flutter.

Em JSON, isso vira:

{"answer": 42}

São 14 bytes de texto. Parece muito pouco, certo? Agora imagine que você está enviando uma lista de 10.000 transações financeiras, cada uma com 15 campos. De repente, aquele “texto legível por humanos” vira megabytes de desperdício.

O parsing de JSON é um ritual de sacrifício de CPU. Seu servidor precisa:

  1. Converter a estrutura Go em texto.
  2. Serializar esse texto em UTF-8.
  3. Enviá-lo pela rede.
  4. O cliente recebe o texto.
  5. O cliente faz o parsing do texto de volta para uma estrutura.
  6. O cliente reza para que os tipos estejam corretos.

Cada um desses passos consome ciclos de processamento. E como eu disse no meu post Sobre Mim: bits consomem energia. Desperdiçá-los é moralmente ofensivo.

A Solução: Protobuf e a Beleza do Binário

Protocol Buffers (Protobuf) é o formato de serialização criado pelo Google para resolver exatamente esse problema. Em vez de texto, você define um contrato (um arquivo .proto), e o compilador gera código nativo para qualquer linguagem.

Aquele mesmo {"answer": 42} em Protobuf? 2 bytes. Não é mágica, é matemática. Binário é simplesmente mais eficiente que texto.

Mas a economia de banda é só a cereja do bolo. O verdadeiro presente é a tipagem forte.

syntax = "proto3";

message Transaction {
  string id = 1;
  int64 amount_cents = 2;
  string currency = 3;
  int64 timestamp = 4;
  TransactionType type = 5;
}

enum TransactionType {
  UNKNOWN = 0;
  CREDIT = 1;
  DEBIT = 2;
}

Com esse arquivo, eu gero código para Go, Dart (Flutter) e TypeScript (SolidJS) com um único comando. Se eu mudar o tipo de amount_cents de int64 para string, o compilador grita comigo antes de eu fazer o deploy. Acabou aquele pesadelo de “funcionou no Postman mas quebrou no app”.

gRPC: O Casamento Perfeito com HTTP/2

Protobuf é o formato. gRPC é o protocolo de comunicação que usa esse formato. E é aqui que as coisas ficam interessantes para quem se importa com performance mobile.

O gRPC roda sobre HTTP/2 por padrão (e já tem suporte experimental para HTTP/3). Isso significa:

  • Multiplexação: Múltiplas chamadas na mesma conexão TCP. Sem abrir e fechar conexões para cada requisição.

  • Compressão de cabeçalhos: HTTP/2 usa HPACK para comprimir cabeçalhos repetitivos. Em REST, você envia Content-Type: application/json em toda requisição. Em gRPC, isso é negociado uma vez.

  • Streaming bidirecional: Quer um chat em tempo real? WebSockets? Esquece. gRPC faz isso nativamente.

Para um app mobile em Flutter consumindo dados de um backend em Go, a diferença é brutal. Menos bytes transferidos significa menos bateria consumida, tempos de carregamento mais curtos e menos chances do usuário desistir e ir assistir TikTok.

A Stack: Go + Flutter + SolidJS

Minha arquitetura atual funciona assim:

  1. Backend em Go: Uso protoc-gen-go e protoc-gen-go-grpc para gerar os stubs. O servidor é ridiculamente rápido porque Go já é rápido, e agora não precisa mais perder tempo parseando JSON.
  2. Mobile em Flutter: Uso o pacote grpc do Dart. O código gerado pelo Protobuf é type-safe. Se o backend mudar um campo, o app não compila até eu atualizar. Isso é segurança.
  3. Web em SolidJS: Aqui vem a única pegadinha. Browsers não falam gRPC nativamente (ainda). A solução é usar gRPC-Web, um proxy que traduz chamadas HTTP/1.1 para gRPC. Não é perfeito, mas ainda é melhor que REST puro.

O arquivo .proto é a única fonte da verdade. Eu edito ele uma vez, rodo o compilador, e todas as plataformas estão sincronizadas. Chega daquele jogo de manter três versões diferentes de DTOs em três linguagens diferentes.

O Elefante na Sala: Debugging

Vou ser honesto: debugar gRPC é mais difícil que debugar REST. Você não pode simplesmente abrir o navegador e olhar o payload na aba Network. Binário não é legível por humanos.

Para isso, uso grpcurl (o curl do mundo gRPC) e Kreya (uma alternativa ao Postman para gRPC). Também configuro logging estruturado no servidor para capturar as requisições desserializadas.

É um custo de setup inicial. Mas uma vez que você se acostuma, não volta atrás.

Quando NÃO Usar gRPC

Como tudo em tecnologia, não são só flores. gRPC é exagero para:

  • APIs públicas: Se você está criando uma API para terceiros consumirem, JSON/REST ainda é o padrão. Ninguém quer aprender seu schema Protobuf só para integrar. Não seja chato.

  • Projetos pequenos: Se seu backend é um CRUD simples com 5 endpoints, a complexidade adicional não vale a pena.

  • SEO e crawlers: Os bots do Google não entendem gRPC. Se você precisa que seu conteúdo seja indexado, REST + JSON é o caminho.

No meu caso, estou focado em sistemas internos onde eu controlo todas as pontas. Não preciso me preocupar com compatibilidade externa.

Conclusão: JSON Não Morreu, Mas Deveria Estar de Férias

Olha, JSON não vai desaparecer. Ele é o inglês do mundo das APIs: não é a melhor língua (preciso escrever um post sobre como eu acho o inglês feio), mas todo mundo fala. Para consumo humano, debugging rápido e prototipagem, ele ainda tem seu lugar.

TODO: Escrever um post sobre como eu acho o inglês feio

Mas se você está construindo sistemas de alta performance, se você se importa com a bateria do celular do seu usuário, se você está cansado de descobrir bugs de tipagem em produção… talvez seja hora de considerar a alternativa binária.

Eu migrei. Meu servidor me agradece. Meu app me agradece. Minha sanidade mental? Bom, essa já estava comprometida desde o 7-1 que a seleção brasileira levou na Copa de 2014.

No futuro, vou escrever um post comparativo. Nada mais técnico do que calar a boca e fazer, né?

Antes que eu esqueça:

TODO: Escrever um post comparando REST x gRPC na prática

É isso, tô saindo, tchau!

Tchau!