Idiomes disponibles:

La mort del JSON al Backend: Per què vaig migrar tota la meva pila a gRPC i Protobuf

Aquesta publicació va ser originalment escrita en anglès. La traducció pot no reflectir el 100% de les idees originals de l'autor.

Començaré aquest post amb una confessió que em pot guanyar alguns enemics: odio el JSON.

No és un odi irracional, d’aquells que apareixen de la nit al dia. És un odi construït, maó a maó, al llarg d’anys depurant càrregues útils mal formades, camps que haurien de ser números però que arribaven com a cadenes de text, i aquell clàssic null on esperaves un array buit. El JSON és l’equivalent digital d’una conversa telefònica amb la teva àvia: creus que has entès el que ha dit, però quan hi arribes, el pastís de pastanaga no tenia aquella glaçada de xocolata esperada (els brasilers ho entendran).

Dolor!

Durant anys, vaig acceptar aquesta tortura com a part del contracte social de ser un desenvolupador de backend. “És l’estàndard”, deien. “Tothom l’utilitza”, repetien. I jo, com un bon xaiet, seguia serialitzant i deserialitzant text com si estiguéssim al 2005 i l’amplada de banda fos infinita.

Fins que n’he tingut prou.

El Problema: El Text és Car

Fem un exercici mental. Imagina que necessites enviar el número 42 des del teu backend en Go a la teva app de Flutter.

En JSON, això es converteix en:

{"answer": 42}

Això són 14 bytes de text. Sembla molt poc, oi? Ara imagina que estàs enviant una llista de 10.000 transaccions financeres, cadascuna amb 15 camps. De sobte, aquell “text llegible per humans” es converteix en megabytes de malbaratament.

Analitzar JSON és un ritual de sacrifici de CPU. El teu servidor necessita:

  1. Convertir l’estructura de Go a text.
  2. Serialitzar aquest text a UTF-8.
  3. Enviar-lo per la xarxa.
  4. El client rep el text.
  5. El client analitza el text de nou en una estructura.
  6. El client resa perquè els tipus siguin correctes.

Cadascun d’aquests passos consumeix cicles de processament. I com vaig dir a la meva publicació Sobre Mi: els bits consumeixen energia. Malgastar-los és moralment ofensiu.

La Solució: Protobuf i la Bellesa del Binari

Protocol Buffers (Protobuf) és el format de serialització creat per Google per resoldre exactament aquest problema. En lloc de text, defines un contracte (un fitxer .proto), i el compilador genera codi natiu per a qualsevol llenguatge.

Aquell mateix {"answer": 42} en Protobuf? 2 bytes. No és màgia, és matemàtica. El binari és simplement més eficient que el text.

Però l’estalvi d’amplada de banda és només la guinda del pastís. El veritable regal és el tipat fort.

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;
}

Amb aquest fitxer, genero codi per a Go, Dart (Flutter) i TypeScript (SolidJS) amb una sola ordre. Si canvio el tipus de amount_cents de int64 a string, el compilador em crida abans de desplegar. Ja no hi ha aquell malson de “funcionava a Postman però a l’app ha petat”.

gRPC: El Matrimoni Perfecte amb HTTP/2

Protobuf és el format. gRPC és el protocol de comunicació que utilitza aquest format. I aquí és on les coses es posen interessants per a aquells que els importa el rendiment mòbil.

gRPC funciona sobre HTTP/2 per defecte (i ja té suport experimental per a HTTP/3). Això vol dir:

  • Multiplexació: Múltiples trucades sobre la mateixa connexió TCP. Sense obrir i tancar connexions per cada petició.

  • Compressió de capçaleres: HTTP/2 utilitza HPACK per comprimir capçaleres repetitives. En REST, envies Content-Type: application/json en cada petició. En gRPC, això es negocia una vegada.

  • Streaming bidireccional: Vols un xat en temps real? WebSockets? Oblida-ho. gRPC ho fa de forma nativa.

Per a una app mòbil de Flutter que consumeix dades d’un backend en Go, la diferència és brutal. Menys bytes transferits significa menys bateria consumida, temps de càrrega més curts i menys possibilitats que l’usuari es rendeixi i se’n vagi a veure TikTok.

La Pila: Go + Flutter + SolidJS

La meva arquitectura actual funciona així:

  1. Backend en Go: Utilitzo protoc-gen-go i protoc-gen-go-grpc per generar els stubs. El servidor és ridículament ràpid perquè Go ja és ràpid, i ara no necessita perdre temps analitzant JSON.
  2. Mòbil en Flutter: Utilitzo el paquet grpc de Dart. El codi generat per Protobuf té tipat fort. Si el backend canvia un camp, l’app no es compilarà fins que l’actualitzi. Això és seguretat.
  3. Web en SolidJS: Aquí ve l’única trampa. Els navegadors no parlen gRPC de forma nativa (encara). La solució és utilitzar gRPC-Web, un proxy que tradueix les trucades HTTP/1.1 a gRPC. No és perfecte, però encara és millor que el REST pur.

El fitxer .proto és l’única font de veritat. L’edito una vegada, executo el compilador, i totes les plataformes estan sincronitzades. Ja no més jugar a mantenir tres versions diferents de DTOs en tres llenguatges diferents.

L’Elefant a l’Habitació: Depuració

Seré honest: depurar gRPC és més difícil que depurar REST. No pots simplement obrir el navegador i mirar la càrrega útil a la pestanya Xarxa. El binari no és llegible per humans.

Per això, utilitzo grpcurl (el curl del món gRPC) i Kreya (una alternativa a Postman per a gRPC). També configuro registres estructurats al servidor per capturar les peticions deserialitzades.

És un cost de configuració inicial. Però un cop t’hi acostumes, no tornes enrere.

Quan NO Utilitzar gRPC

Com tot en tecnologia, no tot són roses. gRPC és massa per a:

  • APIs públiques: Si estàs creant una API perquè tercers la consumeixin, JSON/REST segueix sent l’estàndard. Ningú vol aprendre el teu esquema de Protobuf només per integrar-se. No siguis pesat.

  • Projectes petits: Si el teu backend és un simple CRUD amb 5 endpoints, la complexitat addicional no val la pena.

  • SEO i rastrejadors: Els bots de Google no entenen gRPC. Si necessites que el teu contingut sigui indexat, REST + JSON és el camí.

En el meu cas, estic centrat en sistemes interns on controlo tots els extrems. No he de preocupar-me per la compatibilitat externa.

Conclusió: El JSON No Està Mort, Però Hauria d’Estar de Baixa

Mira, el JSON no desapareixerà. És l’anglès del món de les APIs: no és el millor idioma (necessito escriure un post sobre com de lleig trobo l’anglès), però tothom el parla. Per al consum humà, depuració ràpida i prototipatge, encara té el seu lloc.

TODO: Escriure un post sobre com de lleig trobo l'anglès

Però si estàs construint sistemes d’alt rendiment, si t’importa la bateria del mòbil del teu usuari, si estàs cansat de descobrir errors de tipus en producció… potser és hora de considerar l’alternativa binària.

Jo he migrat. El meu servidor m’ho agraeix. La meva app m’ho agraeix. La meva sanitat mental? Bé, ja estava compromesa des del 7-1 que va prendre la selecció brasilera al Mundial del 2014.

En el futur, escriuré un post comparatiu. Res és més tècnic que callar i fer-ho, oi?

Abans que m’oblidi:

TODO: Escriure un post comparant REST x gRPC a la pràctica

Això és tot, me’n vaig, adéu!

Adéu!