Bannissez le JSON de votre plan de données IA : Protobuf et Arrow l’emportent sur les jolies chaînes

Par Diogo Hudson Dias
Senior backend engineer reviewing network traffic charts on dual monitors in a data center, standing near a rack-mounted 40G switch and fiber cabling.

JSON a rendu vos microservices possibles. Il est aussi en train d’étrangler silencieusement votre stack IA. Si vos embeddings, appels d’outils et traces d’agents circulent encore sous forme de jolies chaînes, vous payez, à chaque appel, une taxe de 2 à 4× en bande passante et en CPU. C’est de l’argent, de la latence et du budget SLO que vous ne reverrez pas.

La parution ce mois‑ci de “Protobuf for Python, without compromises” sur Hacker News est le coup de pouce qu’il vous manquait. L’effort protobuf‑py a supprimé nombre de pièges Python et de falaises de performance. Traduction : la dernière bonne excuse pour continuer à expédier du JSON à l’intérieur de votre plan de données IA vient de mourir.

À quoi ressemble la taxe JSON en 2026

Embeddings

Prenez un embedding float32 de 1,536 dimensions (6,144 octets bruts). Si vous le sérialisez en tableau JSON de nombres, chaque valeur coûte souvent 8 à 12 octets ASCII, plus la ponctuation. À 9 octets par valeur (hypothèse prudente), cela fait ~13.8 KB par vecteur. Si, à la place, vous base64‑encodez les floats bruts (un hack courant), base64 gonfle exactement de 33% : 6.0 KB devient ~8.2 KB.

Donc, le même vecteur fait :

  • 6.1 KB en binaire (float32)
  • 8.2 KB en base64 (binaire‑dans‑du‑texte)
  • ~13.8 KB en nombres JSON

Comptez une pénalité d’environ 2.3x pour le JSON vs le binaire. Multipliez sur 10 millions d’embeddings par jour (pas délirant si vous faites tourner un RAG à 100 QPS) : vous déplacez ~77 GB d’octets inutiles chaque jour. À $0.05–$0.09/GB de trafic sortant, cela brûle $3.8–$6.9K/mois en l’air — avant le coût CPU.

Appels d’outils et traces d’agents

Les frameworks d’agents adorent tout entasser en JSON : entrées d’outils, sorties, artefacts intermédiaires. C’est convivial pour les développeurs, pas pour les machines. Donnez un blob JSON de 150 KB au parseur de Python et vous payez des dizaines à des centaines de microsecondes de CPU par saut. Les décodeurs Protobuf/FlatBuffers sont typiquement 3–5× plus rapides que l’analyse JSON de Python en conditions réelles, et ils n’allouent pas de chaînes pour chaque nombre séparé par des virgules.

Logs et métriques

Même les logs deviennent pénibles. Des équipes diffusent des logs de tokens et des événements sémantiques en JSON via SSE ou WebSocket. Parfait pour un navigateur. Mauvais entre services. On observe couramment 5–10× plus d’octets sur le fil que le contenu informationnel réel, et tout doit être parsé avant d’être utile.

Cadre de décision : quoi va où

Il n’y a pas un seul marteau. Choisissez le bon format de fil pour chaque voie de votre plan de données IA. Le test : pouvez‑vous déplacer, décoder et indexer la charge utile en moins de 100 microsecondes par 100 KB sur votre nœud médian, avec de la marge ? Sinon, changez de format.

Voie 1 : Appels d’outils et actions d’agents (RPC)

  • Choix : gRPC avec Protobuf.
  • Pourquoi : Contrats explicites, versionnable, multiplexage HTTP/2, streaming, éprouvé dans des stacks polyglottes. Avec les liaisons Python modernes (protobuf‑py), vous évitez les ornières de réflexion/GC qui faisaient mal historiquement.
  • Notes : Utilisez oneof pour les variantes de sorties d’outils, réservez des numéros de champ pour l’évolution, et restez sur des types numériques primitifs autant que possible. Gardez les blobs hors bande (object store) et passez plutôt des références adressables par contenu.

Voie 2 : Embeddings et vecteurs de caractéristiques par lot (bulk)

  • Choix : Apache Arrow pour la mémoire/IPC et Arrow Flight pour le transport réseau ; Parquet pour le stockage.
  • Pourquoi : Colonnaire, zero‑copy, compatible SIMD. Arrow compacte une colonne de floats de 1,536 dimensions en mémoire de façon serrée et l’expédie sans transformer les floats en texte. Flight offre un streaming conscient de la contre‑pression au‑dessus de gRPC sans réinventer des sémantiques de batch.
  • Notes : Gardez des tailles de lot entre 64 KB et 4 MB pour le débit sans blocage en tête de file. Préférez float16/bfloat16 quand c’est acceptable ; documentez les compromis de précision.

Voie 3 : Streaming temps réel vers les navigateurs (edge)

  • Choix : Gardez JSON ou JSON Lines pour les Server‑Sent Events vers les navigateurs. Compressez avec gzip ou zstd et limitez la taille des événements.
  • Pourquoi : Les navigateurs aiment le texte. Ne luttez pas contre la plateforme en bordure. Imposez en revanche le binaire pour tous les flux serveur‑à‑serveur.
  • Notes : Si vous devez diffuser du binaire au navigateur, utilisez WebTransport ou WebSocket avec ArrayBuffer. Attendez‑vous à des problèmes de compatibilité plus larges qu’avec SSE.

Voie 4 : Caches et files internes

  • Choix : Utilisez des valeurs binaires dans Redis/Valkey. Pour Kafka/Pulsar, utilisez Protobuf ou Avro avec un registre de schémas.
  • Pourquoi : Des valeurs JSON dans des caches chauds font exploser la mémoire et le CPU. Des topics sensibles au schéma évitent la dérive des consommateurs et permettent de faire évoluer les champs sans retraiter manuellement.

Les chiffres durs (et un rapide contrôle de ROI)

Voici un calcul « à la louche » pour un système RAG de taille moyenne :

  • Charge : 120 QPS en moyenne, chaque requête écrit un embedding 1,536‑dim float dans un feature store et publie une trace d’appel d’outil d’environ 50 KB.
  • Base JSON : Embedding ~13.8 KB + trace 50 KB = ~63.8 KB/requête. Cela fait ~7.6 MB/s ou 657 GB/jour entre services.
  • Refactor binaire : Embedding 6.1 KB + trace 15 KB (protobuf, légèrement compressé) = ~21.1 KB/requête. ~2.5 MB/s ou 218 GB/jour.
  • Économies : ~439 GB/jour. À $0.05–$0.09/GB de trafic sortant : $657–$1,183/mois rien qu’en egress. Les économies CPU dépassent généralement cela : la réduction du parsing JSON et du GC sur votre flotte peut libérer 5–20% de CPU sur les services chauds, ce qui, aux prix actuels des instances, est de l’argent bien réel.

Notez à quelle vitesse cela se compose dans des topologies multi‑régions ou à éventail.

Conception de schéma : ne recréez pas vos spaghettis JSON en Protobuf

On ne gagne pas en remplaçant des accolades par des varints. On gagne en imposant une forme.

  • Séparez les blobs des métadonnées : Placez les gros tableaux binaires (embeddings, images) dans des champs ou flux dédiés. Gardez des métadonnées concises et typées.
  • Utilisez oneof pour les variantes : Les résultats d’outils doivent être un type algébrique, pas un objet JSON avec des clés ad hoc. Par exemple, result peut être l’un de : search_result, file_write, http_call.
  • Faites évoluer vers l’avant, pas en arrière : Ajoutez des champs avec de nouveaux numéros. Ne réutilisez jamais un numéro de champ. Utilisez des feature flags ou une négociation de capacités, pas des suppositions.
  • Choisissez la précision numérique en connaissance de cause : float16/bfloat16 divisent par deux la bande passante par rapport à float32. Pour la similarité cosinus et l’ANN, c’est généralement suffisant. Documentez où ça ne l’est pas.
  • Définissez des limites : Tailles max de tableaux, longueurs max de chaînes. Faites respecter au décodage. Votre plan de données IA fait partie de votre périmètre de sécurité.

Vérité côté navigateur : JSON en bordure, binaire à l’intérieur

« Mais notre front TypeScript… » Bien sûr. Gardez JSON à la frontière. Utilisez gRPC‑Web ou Connect pour la compatibilité navigateur si vous souhaitez des APIs fortement typées ; ils peuvent parler JSON sur le fil côté navigateur tandis que votre serveur parle Protobuf en interne.

La règle : pas de JSON au‑delà de la couche edge. Les front ends parlent JSON ou JSON Lines vers l’edge ; tous les liens inter‑services sont binaires. Si vous devez absolument passer des embeddings à un navigateur (rare), envoyez‑les comme ArrayBuffer dans un WebSocket ou un WebTransport, pas comme chaînes base64 dans du JSON.

Maturité des outils en 2026 : les excuses ont disparu

  • Python : Les nouvelles couches protobuf‑py sont plus rapides et moins « magiques ». Elles s’entendent bien avec les vérificateurs de types et évitent le churn de GC que causaient les anciens wrappers. Les bindings Python d’Apache Arrow sont stables et prêts pour la prod ; Arrow Flight fonctionne avec asyncio.
  • TypeScript/Go/Java : Compilateurs et bibliothèques gRPC de premier plan. Des workflows basés sur Buf imposent les règles de rupture d’API en CI.
  • FlatBuffers et Cap’n Proto : Si votre cas d’usage exige des lectures in‑place et une latence ultra‑basse, envisagez‑les. La contrepartie : courbe d’apprentissage plus raide et ergonomie multi‑langages plus rugueuse que Protobuf. Utilisez‑les pour les hot paths, pas partout.
  • Registres de schémas : Les registres à la Confluent ne sont pas réservés à Avro. Conservez un dépôt de fichiers .proto avec des règles de lint et une doc générée ; traitez‑le comme source de vérité.

Plan de migration : 30, 60, 90 jours

Jour 0–30 : mesurer et cadrer

  • Instrumentez les charges utiles : Journalisez les tailles « sur le fil » et les temps de parsing par route, par service. Échantillonnez 1–5% du trafic si nécessaire. Étiquetez avec le type de contenu.
  • Choisissez deux périmètres pilotes : Un RPC (un endpoint d’appel d’outil chargé). Un job bulk (ingestion d’embeddings).
  • Décidez des formats : gRPC+Protobuf pour le RPC ; Arrow Flight pour le job bulk. Mettez à l’écrit une politique de nommage des champs et de versioning.

Jour 31–60 : construire en dual‑stack

  • Générez les types : Posez les fichiers .proto dans un dépôt partagé. Générez les liaisons de langage dans la CI (ts‑proto pour TS, protobuf‑py ou protoc‑gen‑mypy pour Python).
  • Implémentez des adaptateurs : Pour les endpoints choisis, produisez et consommez JSON et Protobuf en parallèle. Basculez via un en‑tête ou un chemin versionné.
  • Tests de compatibilité sur le fil : Créez des fixtures binaires « golden » et des tests de round‑trip entre langages. Ajoutez une passe de fuzz pour les décodeurs avec plafonds de taille afin de piéger les bombes de parseur.
  • Points d’observabilité : Émettez des métriques : decode_failures, decode_latency_p99, payload_ratio (json_bytes/binary_bytes), et bande passante par route.

Jour 61–90 : basculer, puis étendre

  • Politique edge : Imposer « JSON only at the edge. » Bloquez JSON sur les liens internes avec Envoy ou une politique de service mesh (liste d’autorisation des content‑types).
  • Éliminez le base64 : Pour tout payload binaire restant dans des transports texte, remplacez les blobs base64 par des références d’object store ou des flux binaires.
  • Calendrier de déploiement : Migrez un hot path par semaine. Attendez‑vous à 20–40% de réduction de bande passante par saut et à une marge CPU visible.
  • Contrôle des coûts : Après 30 jours, comparez egress et CPU avant/après. Réinvestissez le CPU libéré dans les SLO de latence ou dans la réduction du nombre d’instances.

Garde‑fous opérationnels

  • Observabilité : Les erreurs de parsing doivent être des incidents de premier ordre. Alertez sur toute hausse. Conservez des histogrammes par format des temps de décodage et des tailles.
  • Contre‑pression : Utilisez les limites de flux et le contrôle de flux HTTP/2 dans gRPC. Pour Arrow Flight, limitez les lots en vol par pair.
  • Sécurité : Traitez vos désérialiseurs comme vos clients SQL : fuzz‑ez, plafonnez‑les, et tenez‑les à l’écart du pool de threads de requête si possible.
  • Rétention des données : Conservez les payloads binaires éphémères. Ne persistez que les faits métiers. Pour l’analytique, convertissez en Parquet avec un schéma explicite et un TTL.

Compromis à regarder en face

  • Débogabilité : Vous perdez la facilité du « un simple curl ». Contrez avec des CLIs pour pretty‑printer Protobuf et Arrow, et intégrez‑les à vos images développeur.
  • Friction côté navigateur : gRPC‑Web et Connect simplifient beaucoup, mais vous garderez quand même du JSON à la frontière externe. C’est très bien — gardez‑le là.
  • Courbe d’apprentissage de l’équipe : Vos ingénieurs devront apprendre l’évolution de schéma et la discipline de numérotation des champs. Ça vaut le coup ; faites respecter avec des linters et des diffs en CI.
  • APIs tierces : Certains vendeurs de bases vectorielles sont encore par défaut sur des tableaux JSON. Quand c’est possible, utilisez leurs APIs binaires/batch. Sinon, convertissez à la lisière de votre système, pas dans les internes chauds.

Cas limites : quand JSON suffit

  • Petits endpoints rarement appelés : JSON convient très bien pour une lecture de paramètres ou un outil d’admin ponctuel.
  • APIs publiques avec un large écosystème de clients : Gardez JSON pour la portée, et cadrez explicitement les payloads. Proposez une variante binaire pour les partenaires.
  • Logs avec humain dans la boucle : Si un payload n’est lu que par un humain dans un navigateur, privilégiez la lisibilité et compressez agressivement.

Une règle simple à institutionnaliser

Binaire à l’intérieur ; JSON à l’extérieur. Collez‑le sur un poster. Ajoutez une règle CI qui bloque les nouveaux champs JSON sur les endpoints internes. Ajoutez un widget de dashboard qui montre votre ratio trafic JSON/binaire baisser semaine après semaine. Et fêtez la première fois où vous supprimez 20% d’un parc de services sans dégrader un SLO.

Les anciens d’Internet nous ont légué un monde de texte interopérable. La prochaine décennie de l’IA sera faite de débit, de latence et d’économies unitaires. Cela veut dire des contrats qui évoluent et des octets qui travaillent pour vous. Pas besoin de tout réécrire — arrêtez simplement de payer une taxe inutile.

Points clés

  • JSON gonfle embeddings et traces d’un facteur 2 à 4 vs le binaire, et base64 ajoute une surcharge stricte de 33%. C’est de l’argent et de la latence bien réels.
  • Utilisez gRPC+Protobuf pour les appels d’outils, Arrow/Flight pour les embeddings et features en batch, et des valeurs binaires dans caches/files.
  • Gardez JSON à la périphérie navigateur ; imposez le binaire à l’intérieur du mesh. Migrez voie par voie avec des endpoints dual‑stack.
  • Instrumentez taille de payload et latence de décodage ; visez moins de 100 microsecondes par 100 KB sur le nœud médian.
  • Attendez‑vous à 20–40% d’économie de bande par saut et 5–20% de marge CPU sur les services chauds après migration.
  • L’écosystème est prêt en 2026 : protobuf‑py moderne, Arrow mature, et un outillage TS/Go/Java solide lèvent les excuses d’hier.

Ready to scale your engineering team?

Tell us about your project and we'll get back to you within 24 hours.

Start a conversation